├── .gitignore ├── CMake ├── FindGeographicLib.cmake ├── FindLIBJSON.cmake ├── FindSHAPELIB.cmake ├── FindTinyXML.cmake ├── kwant-depends-fletch.cmake ├── kwant-depends-kwiver.cmake └── kwant-depends.cmake ├── CMakeLists.txt ├── README.rst ├── doc └── manuals │ ├── Makefile │ ├── README │ ├── _static │ └── style.css │ ├── _templates │ └── layout.html │ ├── conf.py │ ├── index.rst │ ├── introduction.rst │ ├── make.bat │ ├── requirements.txt │ └── sphinx_server.py ├── dockerfile ├── scoring_framework ├── CMakeLists.txt ├── activity_phase1_parameters.h ├── event_phase1_parameters.h ├── matching_args_type.cxx ├── matching_args_type.h ├── phase1_parameters.cxx ├── phase1_parameters.h ├── quickfilter_box.cxx ├── quickfilter_box.h ├── score_core.h ├── score_events.cxx ├── score_frames_aipr.cxx ├── score_frames_aipr.h ├── score_phase1.cxx ├── score_phase1.h ├── score_phase2_aipr.cxx ├── score_phase2_aipr.h ├── score_phase2_hadwav.cxx ├── score_phase2_hadwav.h ├── score_phase3_aipr.cxx ├── score_phase3_aipr.h ├── score_phase3_hadwav.cxx ├── score_phase3_hadwav.h ├── score_tracks.cxx ├── score_tracks_aipr.h ├── score_tracks_hadwav.h ├── score_tracks_loader.cxx ├── score_tracks_loader.h ├── test_phase1.cxx ├── time_window_filter.cxx ├── time_window_filter.h ├── timestamp_utilities.cxx ├── timestamp_utilities.h ├── track_synthesizer.cxx ├── track_synthesizer.h ├── virat_scenario_utilities.cxx └── virat_scenario_utilities.h └── utilities ├── blank_line_filter.h └── shell_comments_filter.h /.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | *.pyc 3 | *~ 4 | *# 5 | .DS_Store 6 | 7 | # this next pattern is for vim swap files 8 | .*.sw* 9 | .sw* 10 | -------------------------------------------------------------------------------- /CMake/FindGeographicLib.cmake: -------------------------------------------------------------------------------- 1 | #ckwg +4 2 | # Copyright 2010-2014 by Kitware, Inc. All Rights Reserved. Please refer to 3 | # KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | # Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | 6 | # The following variables will guide the build: 7 | # 8 | # GeographicLib_ROOT - Set to the install prefix of the PROJ library 9 | # 10 | # The following variables will be set: 11 | # 12 | # GeographicLib_FOUND - Set to true if GeographicLib can be found 13 | # GeographicLib_INCLUDE_DIR - The path to the GeographicLib header files 14 | # GeographicLib_LIBRARY - The full path to the GeographicLib library 15 | 16 | if( Geographiclib_DIR ) 17 | find_package( GeographicLib NO_MODULE ) 18 | elseif( NOT GeographicLib_FOUND ) 19 | include(CommonFindMacros) 20 | 21 | setup_find_root_context(GeographicLib) 22 | find_path( GeographicLib_INCLUDE_DIR GeographicLib/GeoCoords.hpp 23 | ${GeographicLib_FIND_OPTS}) 24 | find_library( GeographicLib_LIBRARY 25 | NAMES Geographic GeographicLib Geographic_d GeographicLib_d 26 | ${GeographicLib_FIND_OPTS}) 27 | restore_find_root_context(GeographicLib) 28 | 29 | include( FindPackageHandleStandardArgs ) 30 | FIND_PACKAGE_HANDLE_STANDARD_ARGS( GeographicLib GeographicLib_INCLUDE_DIR GeographicLib_LIBRARY ) 31 | if( GEOGRAPHICLIB_FOUND ) 32 | set( GeographicLib_FOUND TRUE ) 33 | endif() 34 | endif() 35 | -------------------------------------------------------------------------------- /CMake/FindLIBJSON.cmake: -------------------------------------------------------------------------------- 1 | #ckwg +4 2 | # Copyright 2012-2014 by Kitware, Inc. All Rights Reserved. Please refer to 3 | # KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | # Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | 6 | # Locate the system installed json 7 | # 8 | # The following variables will guide the build: 9 | # 10 | # LIBJSON_ROOT - Set to the install prefix of the json library 11 | # LIBJSON_LIBNAME - Name of the installed library (defaults to json) 12 | # 13 | # The following variables will be set: 14 | # 15 | # LIBJSON_FOUND - Set to true if json can be found 16 | # LIBJSON_INCLUDE_DIR - The path to the json header files 17 | # LIBJSON_LIBRARY - The full path to the json library 18 | 19 | if( LIBJSON_DIR ) 20 | find_package( LIBJSON NO_MODULE ) 21 | elseif( NOT LIBJSON_FOUND ) 22 | include(CommonFindMacros) 23 | 24 | if(NOT LIBJSON_LIBNAME) 25 | set(LIBJSON_LIBNAME json) 26 | endif() 27 | 28 | setup_find_root_context(LIBJSON) 29 | find_path(LIBJSON_INCLUDE_DIR ${LIBJSON_LIBNAME}.h 30 | PATH_SUFFIXES ${LIBJSON_LIBNAME} ${LIBJSON_FIND_OPTS}) 31 | find_library(LIBJSON_LIBRARY ${LIBJSON_LIBNAME} ${LIBJSON_FIND_OPTS}) 32 | restore_find_root_context(LIBJSON) 33 | 34 | include( FindPackageHandleStandardArgs ) 35 | FIND_PACKAGE_HANDLE_STANDARD_ARGS( LIBJSON LIBJSON_INCLUDE_DIR LIBJSON_LIBRARY ) 36 | endif() 37 | -------------------------------------------------------------------------------- /CMake/FindSHAPELIB.cmake: -------------------------------------------------------------------------------- 1 | #ckwg +4 2 | # Copyright 2010 2014 by Kitware, Inc. All Rights Reserved. Please refer to 3 | # KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | # Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | 6 | # Locate the system installed SHAPELIB 7 | # The following variables will be set: 8 | # 9 | # SHAPELIB_FOUND - Set to true if SHAPELIB can be found 10 | # SHAPELIB_INCLUDE_DIR - The path to the SHAPELIB header files 11 | # SHAPELIB_LIBRARY - The full path to the SHAPELIB library 12 | 13 | if( SHAPELIB_DIR ) 14 | find_package( SHAPELIB NO_MODULE ) 15 | elseif( NOT SHAPELIB_FOUND ) 16 | include(CommonFindMacros) 17 | 18 | setup_find_root_context(SHAPELIB) 19 | find_path( SHAPELIB_INCLUDE_DIR shapefil.h PATH_SUFFIXES libshp ${SHAPELIB_FIND_OPTS}) 20 | find_library( SHAPELIB_LIBRARY shp ${SHAPELIB_FIND_OPTS}) 21 | restore_find_root_context(SHAPELIB) 22 | 23 | include( FindPackageHandleStandardArgs ) 24 | FIND_PACKAGE_HANDLE_STANDARD_ARGS( SHAPELIB SHAPELIB_INCLUDE_DIR SHAPELIB_LIBRARY ) 25 | endif() 26 | -------------------------------------------------------------------------------- /CMake/FindTinyXML.cmake: -------------------------------------------------------------------------------- 1 | #ckwg +4 2 | # Copyright 2010,2014 by Kitware, Inc. All Rights Reserved. Please refer to 3 | # KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | # Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | 6 | # Locate the system installed TinyXML 7 | # The following variables will be set: 8 | # 9 | # TinyXML_FOUND - Set to true of the system TinyXML can be found 10 | # TinyXML_INCLUDE_DIR - The path to the tinyxml header files 11 | # TinyXML_LIBRARY - The full path to the TinyXML library 12 | 13 | if( TinyXML_DIR ) 14 | find_package( TinyXML ${TinyXML_FIND_VERSION} NO_MODULE ) 15 | elseif( NOT TinyXML_FOUND ) 16 | include(CommonFindMacros) 17 | 18 | setup_find_root_context(TinyXML) 19 | find_path( TinyXML_INCLUDE_DIR tinyxml.h ${TinyXML_FIND_OPTS}) 20 | find_library( TinyXML_LIBRARY tinyxml ${TinyXML_FIND_OPTS}) 21 | restore_find_root_context(TinyXML) 22 | 23 | include( FindPackageHandleStandardArgs ) 24 | FIND_PACKAGE_HANDLE_STANDARD_ARGS( TinyXML TinyXML_INCLUDE_DIR TinyXML_LIBRARY ) 25 | 26 | if( TINYXML_FOUND ) 27 | # Check to see if TinyXML was built with STL support or not 28 | include( CheckCXXSourceCompiles ) 29 | set( CMAKE_REQUIRED_DEFINITIONS "-DTIXML_USE_STL" ) 30 | set( CMAKE_REQUIRED_INCLUDES ${TinyXML_INCLUDE_DIR} ) 31 | set( CMAKE_REQUIRED_LIBRARIES ${TinyXML_LIBRARY}) 32 | 33 | #The following approach, while unfortunate, is required under certain circumstances. 34 | #CMake always does a try/compile in Debug mode. In Visual Studio >= 2010 we can't 35 | #link against the tinyxml.lib when it's Release, nor can we always decide at CMake config time 36 | #which mode we will run in. Running both modes explicitly and testing whether either succeeds 37 | #will tell us what we need to know 38 | set(CMAKE_TRY_COMPILE_CONFIGURATION "Debug") 39 | CHECK_CXX_SOURCE_COMPILES(" 40 | #include 41 | int main() { TiXmlNode *node; std::cin >> *node; } " 42 | TinyXML_USE_STL_D 43 | ) 44 | set(CMAKE_TRY_COMPILE_CONFIGURATION "Release") 45 | CHECK_CXX_SOURCE_COMPILES(" 46 | #include 47 | int main() { TiXmlNode *node; std::cin >> *node; } " 48 | TinyXML_USE_STL_R 49 | ) 50 | 51 | if( TinyXML_USE_STL_D OR TinyXML_USE_STL_R) 52 | add_definitions( -DTIXML_USE_STL ) 53 | endif() 54 | 55 | # Determine the TinyXML version found 56 | file( READ ${TinyXML_INCLUDE_DIR}/tinyxml.h TinyXML_INCLUDE_FILE ) 57 | string( REGEX REPLACE 58 | ".*TIXML_MAJOR_VERSION = ([0-9]+).*" "\\1" 59 | TinyXML_VERSION_MAJOR "${TinyXML_INCLUDE_FILE}" ) 60 | string( REGEX REPLACE 61 | ".*TIXML_MINOR_VERSION = ([0-9]+).*" "\\1" 62 | TinyXML_VERSION_MINOR "${TinyXML_INCLUDE_FILE}" ) 63 | string( REGEX REPLACE 64 | ".*TIXML_PATCH_VERSION = ([0-9]+).*" "\\1" 65 | TinyXML_VERSION_PATCH "${TinyXML_INCLUDE_FILE}" ) 66 | set( TinyXML_VERSION "${TinyXML_VERSION_MAJOR}.${TinyXML_VERSION_MINOR}.${TinyXML_VERSION_PATCH}" ) 67 | 68 | # Determine version compatibility 69 | if( TinyXML_FIND_VERSION ) 70 | if( TinyXML_FIND_VERSION VERSION_EQUAL TinyXML_VERSION ) 71 | message( STATUS "TinyXML version: ${TinyXML_VERSION}" ) 72 | set( TinyXML_FOUND TRUE ) 73 | else() 74 | if( (TinyXML_FIND_VERSION_MAJOR EQUAL TinyXML_VERSION_MAJOR) AND 75 | (TinyXML_FIND_VERSION_MINOR EQUAL TinyXML_VERSION_MINOR) AND 76 | (TinyXML_FIND_VERSION VERSION_LESS TinyXML_VERSION) ) 77 | message( STATUS "TinyXML version: ${TinyXML_VERSION}" ) 78 | set( TinyXML_FOUND TRUE ) 79 | endif() 80 | endif() 81 | else() 82 | message( STATUS "TinyXML version: ${TinyXML_VERSION}" ) 83 | set( TinyXML_FOUND TRUE ) 84 | endif() 85 | 86 | unset( TINYXML_FOUND ) 87 | endif() 88 | endif() 89 | -------------------------------------------------------------------------------- /CMake/kwant-depends-fletch.cmake: -------------------------------------------------------------------------------- 1 | #The following required packages are provided by fletch 2 | 3 | find_package( fletch REQUIRED ) 4 | list(APPEND CMAKE_PREFIX_PATH "${fletch_ROOT}") 5 | 6 | find_package( TinyXML REQUIRED ) 7 | include_directories( SYSTEM ${TinyXML_INCLUDE_DIR} ) 8 | 9 | find_package( LIBJSON REQUIRED ) 10 | include_directories( SYSTEM ${LIBJSON_INCLUDE_DIR} ) 11 | 12 | find_package(Boost 1.55 REQUIRED 13 | COMPONENTS 14 | date_time 15 | system 16 | ) 17 | add_definitions(-DBOOST_ALL_NO_LIB) 18 | include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) 19 | link_directories(${Boost_LIBRARY_DIRS}) 20 | 21 | find_package( VXL REQUIRED ) 22 | include(${VXL_CMAKE_DIR}/UseVXL.cmake) 23 | include_directories( SYSTEM ${VXL_CORE_INCLUDE_DIR} ) 24 | include_directories( SYSTEM ${VXL_VCL_INCLUDE_DIR} ) 25 | include_directories( SYSTEM ${VXL_RPL_INCLUDE_DIR} ) 26 | link_directories( ${VXL_LIBRARY_DIR} ) 27 | 28 | -------------------------------------------------------------------------------- /CMake/kwant-depends-kwiver.cmake: -------------------------------------------------------------------------------- 1 | find_package( kwiver REQUIRED ) 2 | 3 | if( NOT ";${KWIVER_LIBRARIES};" MATCHES ";track_oracle;" ) 4 | message( FATAL_ERROR "Kwant requires that kwiver be built with track_oracle enabled." ) 5 | endif() 6 | 7 | include_directories( SYSTEM ${KWIVER_INCLUDE_DIRS} ) 8 | link_directories("${KWIVER_LIBRARY_DIRS}") 9 | include(kwiver-cmake-future) 10 | include(kwiver-utils) 11 | include(kwiver-flags) 12 | include(kwiver-configcheck) 13 | -------------------------------------------------------------------------------- /CMake/kwant-depends.cmake: -------------------------------------------------------------------------------- 1 | # Central location for KWANT external dependency declaration and resolution 2 | 3 | # Order matters, find kwiver, then the fletch kwiver used 4 | include( kwant-depends-kwiver ) 5 | include( kwant-depends-fletch ) 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project( KWANT ) 4 | 5 | include(CMakeDependentOption) 6 | ### 7 | # KWANT version 8 | set(KWANT_VERSION_MAJOR 0) 9 | set(KWANT_VERSION_MINOR 1) 10 | set(KWANT_VERSION_PATCH 0) 11 | set(KWANT_VERSION "${KWANT_VERSION_MAJOR}.${KWANT_VERSION_MINOR}.${KWANT_VERSION_PATCH}") 12 | 13 | ### 14 | # Ask for kwiver build directory 15 | # 16 | set( kwiver_DIR "" CACHE PATH "Path to KWIVER packages" ) 17 | 18 | ### 19 | # project specific directories 20 | # 21 | set(kwant_CMAKE_DIR "${CMAKE_SOURCE_DIR}/CMake") 22 | 23 | # root directories 24 | set(kwant_SOURCE_DIR "${CMAKE_SOURCE_DIR}") 25 | set(kwant_BINARY_DIR "${CMAKE_BINARY_DIR}") 26 | 27 | #Set CMP0022 to NEW so we error if someone writes older style cmake code 28 | #See http://www.cmake.org/cmake/help/v2.8.12/cmake.html#policy:CMP0022 29 | #for details on CMP0022 30 | if(POLICY CMP0022) 31 | cmake_policy(SET CMP0022 NEW) 32 | endif() 33 | 34 | if(POLICY CMP0054) 35 | cmake_policy(SET CMP0054 NEW) 36 | endif() 37 | 38 | if( NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY ) 39 | set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${kwant_BINARY_DIR}/lib ) 40 | endif() 41 | if( NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY ) 42 | set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${kwant_BINARY_DIR}/lib ) 43 | endif() 44 | if( NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY ) 45 | set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${kwant_BINARY_DIR}/bin ) 46 | endif() 47 | 48 | ### 49 | # Add the CMake directory for CMake modules 50 | # 51 | list(INSERT CMAKE_MODULE_PATH 0 "${kwant_SOURCE_DIR}/CMake" ) 52 | 53 | ### 54 | # Options and setup 55 | # 56 | OPTION(KWANT_BUILD_SHARED "Build KWANT components shared or not" TRUE ) 57 | set(BUILD_SHARED_LIBS ${KWANT_BUILD_SHARED}) 58 | 59 | include_directories( ${kwant_SOURCE_DIR} ) 60 | include_directories( ${kwant_BINARY_DIR} ) 61 | 62 | include( kwant-depends ) 63 | 64 | add_subdirectory( scoring_framework ) 65 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Kitware Analytics Toolkit 2 | ========================= 3 | 4 | KWANT is an open source C++ toolkit for computing scores and other metrics for object tracking systems. 5 | 6 | Pending support for e.g. GeographicLib in kwiver, this release only supports pixel-coordinate AOIs. 7 | 8 | For more information on how KWANT achieves this goal, 9 | and how to use KWANT visit our `documentation site `_ 10 | 11 | Directory Structure and Provided Functionality 12 | ============================================== 13 | 14 | ======================= =========================================================== 15 | ``_ CMake helper scripts 16 | ``_ Documentation, manuals, release notes 17 | ``_ The scoring algorithms 18 | ``_ Various utility filters 19 | ======================= =========================================================== 20 | 21 | Building KWANT 22 | =============== 23 | 24 | Dependencies 25 | ------------ 26 | KWANT requires, at a minimum, Git, CMake, and a C++ compiler. 27 | 28 | KWANT is built on top of the `KWIVER `_ toolkit. 29 | which is in turn built on the `Fletch `_ super build system. 30 | 31 | You will need to have KWIVER already built for KWANT to use when building. 32 | 33 | 34 | Running CMake 35 | ------------- 36 | 37 | You may run cmake directly from a shell or cmd window. 38 | On unix systems, the ccmake tool allows for interactive selection of CMake options. 39 | Available for all platforms, the CMake GUI can set the source and build directories, options, 40 | "Configure" and "Generate" the build files all with the click of a few button. 41 | 42 | We recommend building KWANT out of its source directory to prevent mixing 43 | source files with compiled products. Create a build directory in parallel 44 | with the KWANT source directory for each desired configuration. For example : 45 | 46 | ========================== =================================================================== 47 | ``\KWANT\src`` contains the code from the git repository 48 | ``\KWANT\build\release`` contains the built files for the release configuration 49 | ``\KWANT\build\debug`` contains the built files for the debug configuration 50 | ========================== =================================================================== 51 | 52 | Basic CMake generation via command line 53 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 54 | 55 | The following example will pull and build Fletch and KWIVER along with the DIVA code base. 56 | It assumes your terminal/command is working in the ``\DIVA\build\release`` directory. :: 57 | 58 | # cmake usage : $ cmake -D 59 | $ cmake ../../src -DCMAKE_BUILD_TYPE=Release 60 | 61 | Using a prebuilt Fletch 62 | ~~~~~~~~~~~~~~~~~~~~~~~ 63 | 64 | If you would like to point DIVA to a prebuilt version of Fletch, specify the fletch_DIR flag to cmake. 65 | The fletch_DIR is the fletch build directory root, which contains the fletchConfig.cmake file. :: 66 | 67 | $ cmake ../../src -DCMAKE_BUILD_TYPE=Release -Dfletch_DIR:PATH= 68 | 69 | You must ensure that the specified build of Fletch has enabled all the appropriate flags for use by KWIVER and DIVA. 70 | The required flags can be found in this file : ``_ 71 | 72 | Using a prebuilt KWIVER 73 | ~~~~~~~~~~~~~~~~~~~~~~~ 74 | 75 | If you would like to point DIVA to a prebuilt version of KWIVER, specify the kwiver_DIR flag to cmake. 76 | The kwiver_DIR is the KWIVER build directory root, which contains the kwiver-config.cmake file. 77 | *NOTE* As KWIVER requires a Fletch directory, the build will ignore the fletch_DIR variable and use the Fletch that was used to build KWIVER. :: 78 | 79 | $ cmake ../../src -DCMAKE_BUILD_TYPE=Release -Dkwiver_DIR:PATH= 80 | 81 | You must ensure that the specified build of KWIVER was build with a fletch that was built with all necessary options. 82 | KWIVER must have also been built with all the appropriate flags for use by DIVA. 83 | The required flags can be found in this file : ``_ 84 | 85 | This framework requires `track_oracle` to be turned on in [kwiver](https://github.com/Kitware/kwiver). 86 | 87 | Compiling 88 | --------- 89 | 90 | Once your CMake generation has completed and created the build files, 91 | compile in the standard way for your build environment. On Linux 92 | this is typically running ``make``. Visual Studio users, open the /KWANT.sln 93 | 94 | Getting Help 95 | ============ 96 | 97 | Please join the 98 | `kwiver-users `_ 99 | mailing list to discuss DIVA/KWIVER or to ask for help with using DIVA/KWIVER. 100 | For announcements about DIVA and other projects built on KWIVER, please join the 101 | `kwiver-announce `_ 102 | mailing list. 103 | -------------------------------------------------------------------------------- /doc/manuals/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " applehelp to make an Apple Help Book" 34 | @echo " devhelp to make HTML files and a Devhelp project" 35 | @echo " epub to make an epub" 36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 37 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 39 | @echo " text to make text files" 40 | @echo " man to make manual pages" 41 | @echo " texinfo to make Texinfo files" 42 | @echo " info to make Texinfo files and run them through makeinfo" 43 | @echo " gettext to make PO message catalogs" 44 | @echo " changes to make an overview of all changed/added/deprecated items" 45 | @echo " xml to make Docutils-native XML files" 46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 47 | @echo " linkcheck to check all external links for integrity" 48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 49 | @echo " coverage to run coverage check of the documentation (if enabled)" 50 | 51 | clean: 52 | rm -rf $(BUILDDIR)/* 53 | 54 | html: 55 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 56 | @echo 57 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 58 | 59 | dirhtml: 60 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 61 | @echo 62 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 63 | 64 | singlehtml: 65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 66 | @echo 67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 68 | 69 | pickle: 70 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 71 | @echo 72 | @echo "Build finished; now you can process the pickle files." 73 | 74 | json: 75 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 76 | @echo 77 | @echo "Build finished; now you can process the JSON files." 78 | 79 | htmlhelp: 80 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 81 | @echo 82 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 83 | ".hhp project file in $(BUILDDIR)/htmlhelp." 84 | 85 | qthelp: 86 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 87 | @echo 88 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 89 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 90 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/KWANT.qhcp" 91 | @echo "To view the help file:" 92 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/KWANT.qhc" 93 | 94 | applehelp: 95 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 96 | @echo 97 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 98 | @echo "N.B. You won't be able to view it unless you put it in" \ 99 | "~/Library/Documentation/Help or install it in your application" \ 100 | "bundle." 101 | 102 | devhelp: 103 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 104 | @echo 105 | @echo "Build finished." 106 | @echo "To view the help file:" 107 | @echo "# mkdir -p $$HOME/.local/share/devhelp/KWANT" 108 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/KWANT" 109 | @echo "# devhelp" 110 | 111 | epub: 112 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 113 | @echo 114 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 115 | 116 | latex: 117 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 118 | @echo 119 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 120 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 121 | "(use \`make latexpdf' here to do that automatically)." 122 | 123 | latexpdf: 124 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 125 | @echo "Running LaTeX files through pdflatex..." 126 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 127 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 128 | 129 | latexpdfja: 130 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 131 | @echo "Running LaTeX files through platex and dvipdfmx..." 132 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 133 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 134 | 135 | text: 136 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 137 | @echo 138 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 139 | 140 | man: 141 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 142 | @echo 143 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 144 | 145 | texinfo: 146 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 147 | @echo 148 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 149 | @echo "Run \`make' in that directory to run these through makeinfo" \ 150 | "(use \`make info' here to do that automatically)." 151 | 152 | info: 153 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 154 | @echo "Running Texinfo files through makeinfo..." 155 | make -C $(BUILDDIR)/texinfo info 156 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 157 | 158 | gettext: 159 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 160 | @echo 161 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 162 | 163 | changes: 164 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 165 | @echo 166 | @echo "The overview file is in $(BUILDDIR)/changes." 167 | 168 | linkcheck: 169 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 170 | @echo 171 | @echo "Link check complete; look for any errors in the above output " \ 172 | "or in $(BUILDDIR)/linkcheck/output.txt." 173 | 174 | doctest: 175 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 176 | @echo "Testing of doctests in the sources finished, look at the " \ 177 | "results in $(BUILDDIR)/doctest/output.txt." 178 | 179 | coverage: 180 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 181 | @echo "Testing of coverage in the sources finished, look at the " \ 182 | "results in $(BUILDDIR)/coverage/python.txt." 183 | 184 | xml: 185 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 186 | @echo 187 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 188 | 189 | pseudoxml: 190 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 191 | @echo 192 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 193 | -------------------------------------------------------------------------------- /doc/manuals/README: -------------------------------------------------------------------------------- 1 | Generating Documentation 2 | ------------------------ 3 | 4 | In order to generate the files needed for the sphinx server, enter the following command: 5 | 6 | $ make html 7 | 8 | A local copy of the sphinx server can be started that will serve up the pages created by the previous command by entering the following command 9 | 10 | $ sphinx_server.py 11 | 12 | System Configuration 13 | -------------------- 14 | 15 | You will need to have Python installed on your system and in a bash or cmd shell that can execute python scripts 16 | 17 | You will need to install several packages in your python environment 18 | While not strictly required, it is recommended to create a virtual environment these required packages from other python packages. 19 | To generate a new virtual environment, first install/update the virtualenv and pip packages: 20 | 21 | sudo pip install -U virtualenv pip 22 | #sudo is not necessary on windows machines 23 | 24 | Now create a virtual environment using the virtualenv command. 25 | You can place the virtual environment directory wherever you want, but it should not be moved. 26 | The following command will generate a new directory called kwant_env in your home directory: 27 | 28 | virtualenv ~/kwant_env 29 | # On windows, it is recommended to create this directory in your user folder with out the ~ 30 | 31 | Enter the virtual environment: 32 | 33 | #Linux 34 | . ~/kwant_env/bin/activate 35 | #Windows, execute this script in your python cmd window 36 | kwant_env/bin/Scripts/activate.bat 37 | 38 | This call will tell you what environment python is currently in 39 | 40 | pip -V 41 | 42 | 43 | Now install your packages, they will only be associated with the kwant_env virtualenv 44 | 45 | pip install sphinx 46 | pip install sphinx_rtd_theme 47 | pip install livereload 48 | pip install breathe 49 | -------------------------------------------------------------------------------- /doc/manuals/_static/style.css: -------------------------------------------------------------------------------- 1 | .wy-nav-content { 2 | max-width: none; 3 | } -------------------------------------------------------------------------------- /doc/manuals/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "!layout.html" %} 2 | {% block extrahead %} 3 | 4 | {% endblock %} -------------------------------------------------------------------------------- /doc/manuals/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # KWANT documentation build configuration file 4 | # 5 | # This file is execfile()d with the current directory set to its 6 | # containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys 15 | import os 16 | import shlex 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | #sys.path.insert(0, os.path.abspath('.')) 22 | 23 | # -- General configuration ------------------------------------------------ 24 | 25 | # If your documentation needs a minimal Sphinx version, state it here. 26 | #needs_sphinx = '1.0' 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [ 32 | 'sphinx.ext.autodoc', 33 | 'sphinx.ext.doctest', 34 | 'sphinx.ext.intersphinx', 35 | 'sphinx.ext.todo', 36 | 'sphinx.ext.mathjax', 37 | 'sphinx.ext.ifconfig', 38 | 'sphinx.ext.viewcode', 39 | 'sphinx.ext.graphviz', 40 | 'breathe', 41 | ] 42 | 43 | # Breathe support - need to make directory name more flexible 44 | breathe_projects = { "kwant": "./_build/xml" } 45 | breathe_default_project = "kwant" 46 | 47 | # UNCOMMENT IF YOU WANT DOXYGEN TO RUN 48 | #import subprocess, os 49 | #read_the_docs_build = os.environ.get('READTHEDOCS', None) == 'True' 50 | #if read_the_docs_build: 51 | # subprocess.call('cd ../doxygen; doxygen', shell=True) 52 | 53 | # Add any paths that contain templates here, relative to this directory. 54 | templates_path = ['_templates'] 55 | 56 | # The suffix(es) of source filenames. 57 | # You can specify multiple suffix as a list of string: 58 | # source_suffix = ['.rst', '.md'] 59 | source_suffix = '.rst' 60 | 61 | # The encoding of source files. 62 | #source_encoding = 'utf-8-sig' 63 | 64 | # The master toctree document. 65 | master_doc = 'index' 66 | 67 | # General information about the project. 68 | project = u'KWANT' 69 | copyright = u'2017, Kitware, Inc.' 70 | author = u'Kitware, Inc.' 71 | 72 | # The version info for the project you're documenting, acts as replacement for 73 | # |version| and |release|, also used in various other places throughout the 74 | # built documents. 75 | # 76 | # The short X.Y version. 77 | version = '1' 78 | # The full version, including alpha/beta/rc tags. 79 | release = '1' 80 | 81 | # The language for content autogenerated by Sphinx. Refer to documentation 82 | # for a list of supported languages. 83 | # 84 | # This is also used if you do content translation via gettext catalogs. 85 | # Usually you set "language" from the command line for these cases. 86 | language = None 87 | 88 | # There are two options for replacing |today|: either, you set today to some 89 | # non-false value, then it is used: 90 | #today = '' 91 | # Else, today_fmt is used as the format for a strftime call. 92 | #today_fmt = '%B %d, %Y' 93 | 94 | # List of patterns, relative to source directory, that match files and 95 | # directories to ignore when looking for source files. 96 | exclude_patterns = ['_build'] 97 | 98 | # The reST default role (used for this markup: `text`) to use for all 99 | # documents. 100 | #default_role = None 101 | 102 | # If true, '()' will be appended to :func: etc. cross-reference text. 103 | #add_function_parentheses = True 104 | 105 | # If true, the current module name will be prepended to all description 106 | # unit titles (such as .. function::). 107 | #add_module_names = True 108 | 109 | # If true, sectionauthor and moduleauthor directives will be shown in the 110 | # output. They are ignored by default. 111 | #show_authors = False 112 | 113 | # The name of the Pygments (syntax highlighting) style to use. 114 | pygments_style = 'sphinx' 115 | 116 | # A list of ignored prefixes for module index sorting. 117 | #modindex_common_prefix = [] 118 | 119 | # If true, keep warnings as "system message" paragraphs in the built documents. 120 | #keep_warnings = False 121 | 122 | # If true, `todo` and `todoList` produce output, else they produce nothing. 123 | todo_include_todos = True 124 | 125 | 126 | # -- Options for HTML output ---------------------------------------------- 127 | 128 | # The theme to use for HTML and HTML Help pages. See the documentation for 129 | # a list of builtin themes. 130 | #html_theme = 'alabaster' 131 | html_theme = 'sphinx_rtd_theme' 132 | 133 | # Theme options are theme-specific and customize the look and feel of a theme 134 | # further. For a list of options available for each theme, see the 135 | # documentation. 136 | #html_theme_options = {} 137 | 138 | # Add any paths that contain custom themes here, relative to this directory. 139 | #html_theme_path = [] 140 | 141 | # The name for this set of Sphinx documents. If None, it defaults to 142 | # " v documentation". 143 | #html_title = None 144 | 145 | # A shorter title for the navigation bar. Default is the same as html_title. 146 | #html_short_title = None 147 | 148 | # The name of an image file (relative to this directory) to place at the top 149 | # of the sidebar. 150 | #html_logo = None 151 | 152 | # The name of an image file (within the static path) to use as favicon of the 153 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 154 | # pixels large. 155 | html_favicon = '../favicon.ico' 156 | 157 | # Add any paths that contain custom static files (such as style sheets) here, 158 | # relative to this directory. They are copied after the builtin static files, 159 | # so a file named "default.css" will overwrite the builtin "default.css". 160 | html_static_path = ['_static'] 161 | 162 | # Add any extra paths that contain custom files (such as robots.txt or 163 | # .htaccess) here, relative to this directory. These files are copied 164 | # directly to the root of the documentation. 165 | #html_extra_path = [] 166 | 167 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 168 | # using the given strftime format. 169 | #html_last_updated_fmt = '%b %d, %Y' 170 | 171 | # If true, SmartyPants will be used to convert quotes and dashes to 172 | # typographically correct entities. 173 | #html_use_smartypants = True 174 | 175 | # Custom sidebar templates, maps document names to template names. 176 | #html_sidebars = {} 177 | 178 | # Additional templates that should be rendered to pages, maps page names to 179 | # template names. 180 | #html_additional_pages = {} 181 | 182 | # If false, no module index is generated. 183 | #html_domain_indices = True 184 | 185 | # If false, no index is generated. 186 | #html_use_index = True 187 | 188 | # If true, the index is split into individual pages for each letter. 189 | #html_split_index = False 190 | 191 | # If true, links to the reST sources are added to the pages. 192 | #html_show_sourcelink = True 193 | 194 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 195 | #html_show_sphinx = True 196 | 197 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 198 | #html_show_copyright = True 199 | 200 | # If true, an OpenSearch description file will be output, and all pages will 201 | # contain a tag referring to it. The value of this option must be the 202 | # base URL from which the finished HTML is served. 203 | #html_use_opensearch = '' 204 | 205 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 206 | #html_file_suffix = None 207 | 208 | # Language to be used for generating the HTML full-text search index. 209 | # Sphinx supports the following languages: 210 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 211 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' 212 | #html_search_language = 'en' 213 | 214 | # A dictionary with options for the search language support, empty by default. 215 | # Now only 'ja' uses this config value 216 | #html_search_options = {'type': 'default'} 217 | 218 | # The name of a javascript file (relative to the configuration directory) that 219 | # implements a search results scorer. If empty, the default will be used. 220 | #html_search_scorer = 'scorer.js' 221 | 222 | # Output file base name for HTML help builder. 223 | htmlhelp_basename = 'KWANTdoc' 224 | 225 | # -- Options for LaTeX output --------------------------------------------- 226 | 227 | latex_elements = { 228 | # The paper size ('letterpaper' or 'a4paper'). 229 | #'papersize': 'letterpaper', 230 | 231 | # The font size ('10pt', '11pt' or '12pt'). 232 | #'pointsize': '10pt', 233 | 234 | # Additional stuff for the LaTeX preamble. 235 | #'preamble': '', 236 | 237 | # Latex figure (float) alignment 238 | #'figure_align': 'htbp', 239 | } 240 | 241 | # Grouping the document tree into LaTeX files. List of tuples 242 | # (source start file, target name, title, 243 | # author, documentclass [howto, manual, or own class]). 244 | latex_documents = [ 245 | (master_doc, 'KWANT.tex', u'KWANT Documentation', 246 | u'Kitware, Inc.', 'manual'), 247 | ] 248 | 249 | # The name of an image file (relative to this directory) to place at the top of 250 | # the title page. 251 | #latex_logo = None 252 | 253 | # For "manual" documents, if this is true, then toplevel headings are parts, 254 | # not chapters. 255 | #latex_use_parts = False 256 | 257 | # If true, show page references after internal links. 258 | #latex_show_pagerefs = False 259 | 260 | # If true, show URL addresses after external links. 261 | #latex_show_urls = False 262 | 263 | # Documents to append as an appendix to all manuals. 264 | #latex_appendices = [] 265 | 266 | # If false, no module index is generated. 267 | #latex_domain_indices = True 268 | 269 | 270 | # -- Options for manual page output --------------------------------------- 271 | 272 | # One entry per manual page. List of tuples 273 | # (source start file, name, description, authors, manual section). 274 | man_pages = [ 275 | (master_doc, 'kwant', u'KWANT Documentation', 276 | [author], 1) 277 | ] 278 | 279 | # If true, show URL addresses after external links. 280 | #man_show_urls = False 281 | 282 | 283 | # -- Options for Texinfo output ------------------------------------------- 284 | 285 | # Grouping the document tree into Texinfo files. List of tuples 286 | # (source start file, target name, title, author, 287 | # dir menu entry, description, category) 288 | texinfo_documents = [ 289 | (master_doc, 'KWANT', u'KWANT Documentation', 290 | author, 'KWANT', 'One line description of project.', 291 | 'Miscellaneous'), 292 | ] 293 | 294 | # Documents to append as an appendix to all manuals. 295 | #texinfo_appendices = [] 296 | 297 | # If false, no module index is generated. 298 | #texinfo_domain_indices = True 299 | 300 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 301 | #texinfo_show_urls = 'footnote' 302 | 303 | # If true, do not generate a @detailmenu in the "Top" node's menu. 304 | #texinfo_no_detailmenu = False 305 | 306 | 307 | # -- Options for Epub output ---------------------------------------------- 308 | 309 | # Bibliographic Dublin Core info. 310 | epub_title = project 311 | epub_author = author 312 | epub_publisher = author 313 | epub_copyright = copyright 314 | 315 | # The basename for the epub file. It defaults to the project name. 316 | #epub_basename = project 317 | 318 | # The HTML theme for the epub output. Since the default themes are not optimized 319 | # for small screen space, using the same theme for HTML and epub output is 320 | # usually not wise. This defaults to 'epub', a theme designed to save visual 321 | # space. 322 | #epub_theme = 'epub' 323 | 324 | # The language of the text. It defaults to the language option 325 | # or 'en' if the language is not set. 326 | #epub_language = '' 327 | 328 | # The scheme of the identifier. Typical schemes are ISBN or URL. 329 | #epub_scheme = '' 330 | 331 | # The unique identifier of the text. This can be a ISBN number 332 | # or the project homepage. 333 | #epub_identifier = '' 334 | 335 | # A unique identification for the text. 336 | #epub_uid = '' 337 | 338 | # A tuple containing the cover image and cover page html template filenames. 339 | #epub_cover = () 340 | 341 | # A sequence of (type, uri, title) tuples for the guide element of content.opf. 342 | #epub_guide = () 343 | 344 | # HTML files that should be inserted before the pages created by sphinx. 345 | # The format is a list of tuples containing the path and title. 346 | #epub_pre_files = [] 347 | 348 | # HTML files shat should be inserted after the pages created by sphinx. 349 | # The format is a list of tuples containing the path and title. 350 | #epub_post_files = [] 351 | 352 | # A list of files that should not be packed into the epub file. 353 | epub_exclude_files = ['search.html'] 354 | 355 | # The depth of the table of contents in toc.ncx. 356 | #epub_tocdepth = 3 357 | 358 | # Allow duplicate toc entries. 359 | #epub_tocdup = True 360 | 361 | # Choose between 'default' and 'includehidden'. 362 | #epub_tocscope = 'default' 363 | 364 | # Fix unsupported image types using the Pillow. 365 | #epub_fix_images = False 366 | 367 | # Scale large images. 368 | #epub_max_image_width = 0 369 | 370 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 371 | #epub_show_urls = 'inline' 372 | 373 | # If false, no index is generated. 374 | #epub_use_index = True 375 | 376 | # Example configuration for intersphinx: refer to the Python standard library. 377 | intersphinx_mapping = {'https://docs.python.org/': None} 378 | 379 | -------------------------------------------------------------------------------- /doc/manuals/index.rst: -------------------------------------------------------------------------------- 1 | .. KWANT documentation master file 2 | You can adapt this file completely to your liking, but it should at least 3 | contain the root `toctree` directive. 4 | 5 | Welcome to KWANT's documentation! 6 | ================================= 7 | 8 | Contents: 9 | 10 | .. toctree:: 11 | :maxdepth: 6 12 | 13 | introduction 14 | 15 | Indices and tables 16 | ================== 17 | 18 | * :ref:`genindex` 19 | * :ref:`modindex` 20 | * :ref:`search` 21 | 22 | 23 | .. |br| raw:: html 24 | 25 |
26 | -------------------------------------------------------------------------------- /doc/manuals/introduction.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | The Kitware Analytics Toolkit (KWANT) is an open source C++ toolkit for computing scores and other 5 | metrics for object tracking systems. 6 | 7 | Visit the `repository `_ on how to get and build the KWANT code base. 8 | 9 | The Scoring Framework 10 | --------------------- 11 | 12 | The scoring code essentially consists of two executables, 13 | ``score_tracks`` and ``score_events``. Other executables such as 14 | ``hadwav_score_human_events``, etc., are derived from one or the other 15 | of these (or both); their functionality is planned to be merged into 16 | the baseline executables. 17 | 18 | All the code uses [track_oracle](https://github.com/Kitware/kwiver/blob/master/track_oracle/README.markdown) in an attempt to 19 | be agnostic to the source file format. 20 | 21 | The options for [score_tracks](README.scoretracks.markdown) and 22 | [score_events](README.scoreevents.markdown) are list on their 23 | respective pages; the general concepts behind their operation are 24 | described below. 25 | 26 | Here, we use the term "event" to describe the association of a label 27 | with a track; the label typically describes the action of the actor 28 | whose track is being observed. The label vocabulary currently 29 | recognized is that of the VIRAT program plus generic "PersonMoving" 30 | and "VehicleMoving". The list of valid events can be found by running 31 | ``score_events`` with no arguments. 32 | 33 | Basic scoring pipeline 34 | ~~~~~~~~~~~~~~~~~~~~~~ 35 | 36 | Both ``score_tracks`` and ``score_events`` have the same basic sequence of 37 | steps: 38 | 39 | 1. The initial set of ground-truth tracks G0 and computed tracks C0 40 | are loaded. The assertion is that all tracks will have a timestamp 41 | associated with each frame; if the track format does not support 42 | timestamps, various options are available to synthesize them. This 43 | code mostly lives in ``score_tracks_loader.h`` and associated classes. 44 | 45 | 2. Various optional spatio-temporal and event filters are applied to 46 | produce the final set of ground-truth and computed tracks, G and C. 47 | See ``matching_args_type.h``. 48 | 49 | 3. An association matrix is computed by comparing the frames 50 | associated with each track in G with each track in C. Each pair of 51 | tracks is aligned based on timestamps and then checked for overlapping 52 | detections. Overlapping criteria are discussed below. The 53 | association matrix is computed by code rooted at ``score_phase.h`` and 54 | ``phase1_parameters.h``. 55 | 56 | 4. Once the association matrix is available, various metrics are 57 | computed; see below. 58 | 59 | Spatial Overlap Detection 60 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 61 | 62 | Two methods are supported for determining if a pair of time-aligned 63 | detections overlap: *image* and *radial* overlap. The default is 64 | image-based overlap. Setting the ``--radial-overlap`` option to a 65 | number greater than zero turns on radial overlap. 66 | 67 | Image-Based Overlap Detection 68 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 69 | 70 | If detections are described using bounding boxes, which are in turn 71 | defined in terms of image coordinates on the image frame, then 72 | image-based detection is used. By default, two detections are said to 73 | overlap if their boxes overlap by at least one pixel, although options 74 | exist to change this. 75 | 76 | In general, image-based overlap detection when scoring FMV tracking. 77 | 78 | Radial Overlap Detection 79 | ^^^^^^^^^^^^^^^^^^^^^^^^ 80 | 81 | When the ``--radial-overlap N`` option is given, where N is a number 82 | greater than 0, then two detections are declared to overlap if their 83 | centroids are no more than N meters apart. The detections are 84 | considered to be points, not boxes, and must be in lat/lon (such as a 85 | shapefile with geopoints or a kw18 file with world coordinates given 86 | as lat/lon.) 87 | 88 | Converting Detection Overlaps To Track Overlaps 89 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 90 | 91 | For any pair of tracks from G and C, once the number of overlaps has 92 | been determined, this is converted into a decision as to whether the 93 | tracks overlap. By default, they are said to overlap if a single 94 | detection is said to overlap, but again there are options to change 95 | this behavior. 96 | 97 | Scoring events 98 | ~~~~~~~~~~~~~~ 99 | 100 | When scoring an event, we assume that the event label applies to the 101 | entire track and that therefore if the event labels match and the 102 | tracks overlap, the events match. Some track formats (for example, 103 | XGTF) allow events to be defined over subsets of tracks; in these 104 | cases, we create new internal tracks for the subsets but record the 105 | original source track ID. 106 | 107 | Event label matching is (almost) always strict equivalence; there is 108 | no concept of an event hierarchy. The exceptions are the PersonMoving 109 | and VehicleMoving events, which are specific to VIRAT and derived in 110 | a post-hoc fashion from the ground-truth (since the PersonMoving and 111 | VehicleMoving events were not annotated.) 112 | 113 | 114 | Metrics 115 | ~~~~~~~ 116 | 117 | The track metrics produced by ``score_tracks`` are single points in 118 | Pd/FAR space; score_tracks does not have a concept of "partial match" 119 | which would serve as the operating point over which an ROC curve could 120 | be swept. Several overlap attributes could be used for this purpose, 121 | should the need or desire arise. 122 | 123 | The event metrics are computed by generating the same track overlap 124 | matrix as for track metrics, which establishes the underlying track 125 | overlap profile, and then sweeping an operating point based on the 126 | event "relevancy" to declare overlapping tracks as hits or misses, and 127 | thus generate an ROC curve and/or a P/R curve. The relevancy measure 128 | is typically either the event probability, when scoring detectors, or 129 | the retrieval rank, when scoring retrieval. 130 | 131 | Note that although the track association matrix is fixed when sweeping 132 | out an ROC curve for any particular event, the tracks making up the 133 | matrix can change depending on the event being scored. For example, 134 | an XGTF file can contain ground-truth for multiple events; when 135 | scoring "VehicleUTurn" events, the set of ground-truth UTurn tracks 136 | from the XGTF file will be different than those used when scoring 137 | (say) "PersonRunning." 138 | 139 | 140 | Track Metrics 141 | ^^^^^^^^^^^^^ 142 | 143 | What the output means 144 | ^^^^^^^^^^^^^^^^^^^^^ 145 | 146 | The scoring code is moderately verbose. You may see warnings 147 | regarding zero-area boxes and timebases, such as this: 148 | 149 | [...] 150 | WARN Zero-area-box: file lair-gt.csv track 8776 frame 0 box 151 | WARN Zero-area-box: file lair-gt.csv track 8776 frame 0 box 152 | [...] 153 | WARN Timebases heading in different directions: file lair-gt.csv track 8776 ... 154 | WARN Timebases heading in different directions: file lair-gt.csv track 8776 ... 155 | [..] 156 | 157 | They may be ignored. 158 | 159 | The metrics are output at the end: 160 | 161 | HADWAV Scoring Results: 162 | Detection-Pd: 0.205511 163 | Detection-FA: 697 164 | Detection-PFA: 0.0591631 165 | Frame-NFAR: 74.937 166 | Track-Pd: 0.876833 167 | Track-FA: 87 168 | Computed-track-PFA: 0.136364 169 | Track-NFAR: 21.9512 170 | Avg track (continuity, purity ): 1.56443, 0.986677 171 | Avg target (continuity, purity ): 2.52786, 0.365779 172 | Track-frame-precision: 0.626884 173 | 174 | 175 | The metrics are: 176 | 177 | - *Detection-Pd*: The ratio D/Td, where D is the number of computed 178 | detections associated with a true detection, and Td is the number 179 | of true detections. 180 | 181 | - *Detection-FA*: The number of computed detections which were not 182 | associated with any true detection. 183 | 184 | - *Detection-PFA*: The ratio Fd/Cd, where Fd is the Detection-FA count 185 | above and Cd is the total number of computed detections. 186 | 187 | - *Frame-NFAR*: Deprecated. 188 | 189 | - *Track-Pd*: The ratio Ct/Tt, where Ct is the number of computed 190 | tracks associated with a true track; Tt is the number of true 191 | tracks. 192 | 193 | - *Track-FA*: The number of computed tracks which were not associated 194 | with any true track. 195 | 196 | - *Computed-track-PFA*: The ratio Ft/C, where Ft is the Track-FA count 197 | above and C is the total number of computed tracks. 198 | 199 | - *Track-NFAR*: The track false alarm rate, normalized to (by default) 200 | tracks per minute per km^2. 201 | 202 | - *Avg track continuity*: The track continuity of a 203 | computed track C measures the number of ground-truth tracks 204 | associated with C. Ideal value is 1. 205 | 206 | - *Avg track purity*: The track purity of a computed track C is the 207 | percentage of detections in the lifetime of C which are associated 208 | with the "dominant" matching ground-truth track (if any.) The 209 | "dominant" matching ground-truth track is that ground-truth track 210 | which has the greatest number of associations with C. Ideal value 211 | is 100%. 212 | 213 | - *Avg target continuity*: Target continuity measures the number of 214 | computed tracks associated with the ground-truth track. 215 | 216 | - *Avg target purity*: Symmetric to track purity; measures the 217 | percentage of detections comprising a ground-truth track G which 218 | are associated with its dominant computed-track (if any). 219 | 220 | - *Track-frame-precision*: Deprecated. 221 | 222 | Event Metrics 223 | ^^^^^^^^^^^^^ 224 | 225 | -------------------------------------------------------------------------------- /doc/manuals/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | echo. coverage to run coverage check of the documentation if enabled 41 | goto end 42 | ) 43 | 44 | if "%1" == "clean" ( 45 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 46 | del /q /s %BUILDDIR%\* 47 | goto end 48 | ) 49 | 50 | 51 | REM Check if sphinx-build is available and fallback to Python version if any 52 | %SPHINXBUILD% 2> nul 53 | if errorlevel 9009 goto sphinx_python 54 | goto sphinx_ok 55 | 56 | :sphinx_python 57 | 58 | set SPHINXBUILD=python -m sphinx.__init__ 59 | %SPHINXBUILD% 2> nul 60 | if errorlevel 9009 ( 61 | echo. 62 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 63 | echo.installed, then set the SPHINXBUILD environment variable to point 64 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 65 | echo.may add the Sphinx directory to PATH. 66 | echo. 67 | echo.If you don't have Sphinx installed, grab it from 68 | echo.http://sphinx-doc.org/ 69 | exit /b 1 70 | ) 71 | 72 | :sphinx_ok 73 | 74 | 75 | if "%1" == "html" ( 76 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 80 | goto end 81 | ) 82 | 83 | if "%1" == "dirhtml" ( 84 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 88 | goto end 89 | ) 90 | 91 | if "%1" == "singlehtml" ( 92 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 93 | if errorlevel 1 exit /b 1 94 | echo. 95 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 96 | goto end 97 | ) 98 | 99 | if "%1" == "pickle" ( 100 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 101 | if errorlevel 1 exit /b 1 102 | echo. 103 | echo.Build finished; now you can process the pickle files. 104 | goto end 105 | ) 106 | 107 | if "%1" == "json" ( 108 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 109 | if errorlevel 1 exit /b 1 110 | echo. 111 | echo.Build finished; now you can process the JSON files. 112 | goto end 113 | ) 114 | 115 | if "%1" == "htmlhelp" ( 116 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 117 | if errorlevel 1 exit /b 1 118 | echo. 119 | echo.Build finished; now you can run HTML Help Workshop with the ^ 120 | .hhp project file in %BUILDDIR%/htmlhelp. 121 | goto end 122 | ) 123 | 124 | if "%1" == "qthelp" ( 125 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 129 | .qhcp project file in %BUILDDIR%/qthelp, like this: 130 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\KWANT.qhcp 131 | echo.To view the help file: 132 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\KWANT.ghc 133 | goto end 134 | ) 135 | 136 | if "%1" == "devhelp" ( 137 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. 141 | goto end 142 | ) 143 | 144 | if "%1" == "epub" ( 145 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 149 | goto end 150 | ) 151 | 152 | if "%1" == "latex" ( 153 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 157 | goto end 158 | ) 159 | 160 | if "%1" == "latexpdf" ( 161 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 162 | cd %BUILDDIR%/latex 163 | make all-pdf 164 | cd %~dp0 165 | echo. 166 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 167 | goto end 168 | ) 169 | 170 | if "%1" == "latexpdfja" ( 171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 172 | cd %BUILDDIR%/latex 173 | make all-pdf-ja 174 | cd %~dp0 175 | echo. 176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 177 | goto end 178 | ) 179 | 180 | if "%1" == "text" ( 181 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 182 | if errorlevel 1 exit /b 1 183 | echo. 184 | echo.Build finished. The text files are in %BUILDDIR%/text. 185 | goto end 186 | ) 187 | 188 | if "%1" == "man" ( 189 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 190 | if errorlevel 1 exit /b 1 191 | echo. 192 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 193 | goto end 194 | ) 195 | 196 | if "%1" == "texinfo" ( 197 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 198 | if errorlevel 1 exit /b 1 199 | echo. 200 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 201 | goto end 202 | ) 203 | 204 | if "%1" == "gettext" ( 205 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 206 | if errorlevel 1 exit /b 1 207 | echo. 208 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 209 | goto end 210 | ) 211 | 212 | if "%1" == "changes" ( 213 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 214 | if errorlevel 1 exit /b 1 215 | echo. 216 | echo.The overview file is in %BUILDDIR%/changes. 217 | goto end 218 | ) 219 | 220 | if "%1" == "linkcheck" ( 221 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 222 | if errorlevel 1 exit /b 1 223 | echo. 224 | echo.Link check complete; look for any errors in the above output ^ 225 | or in %BUILDDIR%/linkcheck/output.txt. 226 | goto end 227 | ) 228 | 229 | if "%1" == "doctest" ( 230 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 231 | if errorlevel 1 exit /b 1 232 | echo. 233 | echo.Testing of doctests in the sources finished, look at the ^ 234 | results in %BUILDDIR%/doctest/output.txt. 235 | goto end 236 | ) 237 | 238 | if "%1" == "coverage" ( 239 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage 240 | if errorlevel 1 exit /b 1 241 | echo. 242 | echo.Testing of coverage in the sources finished, look at the ^ 243 | results in %BUILDDIR%/coverage/python.txt. 244 | goto end 245 | ) 246 | 247 | if "%1" == "xml" ( 248 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 249 | if errorlevel 1 exit /b 1 250 | echo. 251 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 252 | goto end 253 | ) 254 | 255 | if "%1" == "pseudoxml" ( 256 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 257 | if errorlevel 1 exit /b 1 258 | echo. 259 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 260 | goto end 261 | ) 262 | 263 | :end 264 | -------------------------------------------------------------------------------- /doc/manuals/requirements.txt: -------------------------------------------------------------------------------- 1 | breathe 2 | -------------------------------------------------------------------------------- /doc/manuals/sphinx_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | This module is designed to used with _livereload to 5 | make it a little easier to write Sphinx documentation. 6 | Simply run the command:: 7 | python sphinx_server.py 8 | 9 | and browse to http://localhost:5500 10 | 11 | livereload_: https://pypi.python.org/pypi/livereload 12 | """ 13 | 14 | from livereload import Server, shell 15 | server = Server() 16 | server.watch('*.rst', shell('make html', cwd='.')) 17 | server.watch('examples/*.rst', shell('make html', cwd='.')) 18 | server.watch('conf.py', shell('make html', cwd='.')) 19 | server.serve(root='_build/html') 20 | -------------------------------------------------------------------------------- /dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile designed around using "score_tracks" utility from KWANT: 2 | # OS -> Ubuntu:18.04 3 | # ENTRYPOINT: "score_tracks" 4 | 5 | FROM ubuntu:18.04 6 | 7 | RUN apt-get update \ 8 | && apt-get install -y git \ 9 | build-essential \ 10 | cmake 11 | 12 | RUN git clone https://github.com/Kitware/fletch.git fletch-source \ 13 | && git clone https://github.com/Kitware/kwiver.git kwiver-source \ 14 | && git clone https://github.com/Kitware/kwant.git kwant-source \ 15 | && mkdir fletch-build \ 16 | && cd fletch-build \ 17 | && cmake ../fletch-source -Dfletch_ENABLE_Boost:BOOL=ON \ 18 | -Dfletch_ENABLE_Eigen:BOOL=ON \ 19 | -Dfletch_ENABLE_GeographicLib:BOOL=ON \ 20 | -Dfletch_ENABLE_TinyXML1:BOOL=ON \ 21 | -Dfletch_ENABLE_TinyXML2:BOOL=ON \ 22 | -Dfletch_ENABLE_VXL:BOOL=ON \ 23 | -Dfletch_ENABLE_YAMLcpp:BOOL=ON \ 24 | -Dfletch_ENABLE_ZLib:BOOL=ON \ 25 | -Dfletch_ENABLE_libjson:BOOL=ON \ 26 | -Dfletch_ENABLE_shapelib:BOOL=ON \ 27 | -Dfletch_ENABLE_libgeotiff:BOOL=ON \ 28 | -Dfletch_ENABLE_libtiff:BOOL=ON \ 29 | && make -j16 \ 30 | && mkdir /kwiver-build \ 31 | && cd /kwiver-build \ 32 | && cmake ../kwiver-source -Dfletch_DIR:PATH=/fletch-build \ 33 | -DKWIVER_ENABLE_ARROWS:BOOL=ON \ 34 | -DKWIVER_ENABLE_KPF:BOOL=ON \ 35 | -DKWIVER_ENABLE_TRACK_ORACLE:BOOL=ON \ 36 | -DKWIVER_ENABLE_VXL:BOOL=ON \ 37 | && make -j16 \ 38 | && mkdir /kwant-build \ 39 | && cd /kwant-build \ 40 | && cmake ../kwant-source -Dkwiver_DIR=/kwiver-build/ \ 41 | && make -j16 \ 42 | && rm -rf fletch-source \ 43 | && rm -rf kwiver-source \ 44 | && rm -rf kwant-source 45 | 46 | ENV VG_PLUGIN_PATH=/kwiver-build 47 | ENV PATH=/kwiver-build/bin:$PATH 48 | ENV LD_LIBRARY_PATH="/kwiver-build/lib:$LD_LIBRARY_PATH" 49 | ENV KWIVER_PLUGIN_PATH=/kwiver-build/lib/kwiver/modules:/kwiver-build/lib/kwiver/processes:$KWIVER_PLUGIN_PATH 50 | ENV KWIVER_CONFIG_PATH=/kwiver-build/share/kwiver/1.4.0/config 51 | ENV LD_LIBRARY_PATH=/fletch-build/install/lib:$LD_LIBRARY_PATH 52 | ENV GDAL_DATA=/share/gdal 53 | ENV PROJ_LIB=/share/proj 54 | ENV KWIVER_DEFAULT_LOG_LEVEL=WARN 55 | 56 | WORKDIR /kwant-build/bin 57 | 58 | ENTRYPOINT ["./score_tracks"] 59 | 60 | CMD ["-Help"] 61 | -------------------------------------------------------------------------------- /scoring_framework/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | OPTION(KWANT_ENABLE_MGRS "Enable track_oracle's lat/lon <-> MGRS capabilities (disable until further notice)" OFF ) 2 | 3 | if (KWANT_ENABLE_MGRS) 4 | message( FATAL_ERROR "Building the MGRS code is blocked until lat/lon <-> MGRS capabilities are available" ) 5 | #find_package( GeographicLib REQUIRED ) 6 | #include_directories ( ${GeographicLib_INCLUDE_DIR} ) 7 | #add_definitions(-DKWANT_ENABLE_MGRS) 8 | endif() 9 | 10 | ######################################## 11 | # timestamp utilities 12 | ######################################## 13 | set( timestamp_utilities_public_headers 14 | timestamp_utilities.h 15 | ) 16 | 17 | set( timestamp_utilities_sources 18 | timestamp_utilities.cxx 19 | ) 20 | 21 | kwiver_install_headers( 22 | ${timestamp_utilities_public_headers} 23 | SUBDIR scoring_framework 24 | ) 25 | 26 | kwiver_install_headers( 27 | ${CMAKE_CURRENT_BINARY_DIR}/timestamp_utilities_export.h 28 | SUBDIR scoring_framework 29 | NOPATH 30 | ) 31 | 32 | kwiver_add_library( timestamp_utilities 33 | ${timestamp_utilities_public_headers} 34 | ${timestamp_utilities_sources} 35 | ${CMAKE_CURRENT_BINARY_DIR}/timestamp_utilities_export.h 36 | ) 37 | 38 | target_link_libraries( timestamp_utilities 39 | PUBLIC track_oracle 40 | PRIVATE vital_logger 41 | vul 42 | ${Boost_DATE_TIME_LIBRARY} 43 | ) 44 | 45 | ######################################## 46 | # Default 47 | ######################################## 48 | 49 | set( score_core_public_headers 50 | score_core.h 51 | phase1_parameters.h 52 | quickfilter_box.h 53 | score_phase1.h 54 | matching_args_type.h 55 | time_window_filter.h 56 | virat_scenario_utilities.h 57 | ) 58 | 59 | set( score_core_sources 60 | phase1_parameters.cxx 61 | quickfilter_box.cxx 62 | score_phase1.cxx 63 | matching_args_type.cxx 64 | time_window_filter.cxx 65 | virat_scenario_utilities.cxx 66 | ) 67 | 68 | kwiver_install_headers( 69 | ${score_core_public_headers} 70 | SUBDIR scoring_framework 71 | ) 72 | 73 | kwiver_add_library( score_core 74 | ${score_core_public_headers} 75 | ${score_core_sources} 76 | ${CMAKE_CURRENT_BINARY_DIR}/score_core_export.h 77 | ) 78 | 79 | target_link_libraries( score_core 80 | PUBLIC track_oracle 81 | ${TRACK_ORACLE_SCORABLE_MGRS_LIBRARY} 82 | timestamp_utilities 83 | vul 84 | vgl 85 | data_terms 86 | scoring_aries_interface 87 | PRIVATE track_oracle_tokenizers 88 | vibrant_descriptors 89 | vital_logger 90 | ${Boost_DATE_TIME_LIBRARY} 91 | ${TinyXML_LIBRARY} 92 | ) 93 | 94 | set( score_tracks_loader_public_headers 95 | score_tracks_loader.h 96 | ) 97 | 98 | set( score_tracks_loader_sources 99 | score_tracks_loader.cxx 100 | ) 101 | 102 | kwiver_install_headers( 103 | ${score_tracks_loader_public_headers} 104 | SUBDIR scoring_framework 105 | ) 106 | 107 | kwiver_install_headers( 108 | ${CMAKE_CURRENT_BINARY_DIR}/score_tracks_loader_export.h 109 | SUBDIR scoring_framework 110 | NOPATH 111 | ) 112 | 113 | kwiver_add_library( score_tracks_loader 114 | ${score_tracks_loader_public_headers} 115 | ${score_tracks_loader_sources} 116 | ${CMAKE_CURRENT_BINARY_DIR}/score_tracks_loader_export.h 117 | ) 118 | 119 | target_link_libraries( score_tracks_loader 120 | PUBLIC score_core 121 | vul 122 | track_oracle_file_formats 123 | track_oracle_format_base 124 | track_kw18 125 | track_xgtf 126 | track_kwxml 127 | track_vpd 128 | track_kst 129 | track_comms_xml 130 | PRIVATE vital_logger 131 | ) 132 | 133 | ######################################## 134 | # HADWAV 135 | ######################################## 136 | 137 | set( score_tracks_hadwav_public_headers 138 | score_tracks_hadwav.h 139 | score_phase2_hadwav.h 140 | score_phase3_hadwav.h 141 | score_tracks_hadwav.h 142 | ) 143 | 144 | set( score_tracks_hadwav_sources 145 | score_phase2_hadwav.cxx 146 | score_phase3_hadwav.cxx 147 | ) 148 | 149 | kwiver_install_headers( 150 | ${score_tracks_hadwav_public_headers} 151 | SUBDIR scoring_framework 152 | ) 153 | 154 | kwiver_install_headers( 155 | ${CMAKE_CURRENT_BINARY_DIR}/score_tracks_hadwav_export.h 156 | SUBDIR scoring_framework 157 | NOPATH 158 | ) 159 | 160 | kwiver_add_library( score_tracks_hadwav 161 | ${score_tracks_hadwav_public_headers} 162 | ${score_tracks_hadwav_sources} 163 | ${CMAKE_CURRENT_BINARY_DIR}/score_tracks_hadwav_export.h 164 | ) 165 | 166 | target_link_libraries(score_tracks_hadwav 167 | PUBLIC score_core 168 | PRIVATE track_oracle 169 | vital_logger 170 | ) 171 | 172 | ######################################## 173 | # Scoring 174 | ######################################## 175 | 176 | if (fletch_ENABLED_libjson) 177 | set(scoring_json_src) 178 | set(json_lib ${LIBJSON_LIBRARY}) 179 | 180 | kwiver_add_executable( score_tracks score_tracks.cxx) 181 | target_link_libraries( score_tracks 182 | vital_logger 183 | score_tracks_loader 184 | score_tracks_hadwav 185 | track_oracle 186 | track_oracle_file_formats 187 | timestamp_utilities 188 | ${json_lib} ) 189 | endif() 190 | 191 | kwiver_add_executable( score_events score_events.cxx ) 192 | target_link_libraries( score_events 193 | vital_logger 194 | logging_map 195 | data_terms 196 | score_tracks_loader 197 | score_tracks_hadwav 198 | scoring_aries_interface 199 | timestamp_utilities track_oracle 200 | track_oracle_format_base 201 | track_oracle_file_formats 202 | vibrant_descriptors 203 | vul 204 | track_filter_kpf_activity 205 | kwiver_algo_kpf 206 | kpf_yaml 207 | kwiversys 208 | kpf_utils 209 | ${YAML_CPP_LIBRARIES} 210 | ${Boost_SYSTEM_LIBRARY} ) 211 | 212 | ######################################## 213 | # Helper code for testing 214 | ######################################## 215 | 216 | set( track_synthesizer_public_headers 217 | track_synthesizer.h 218 | ) 219 | 220 | set( track_synthesizer_sources 221 | track_synthesizer.cxx 222 | ) 223 | 224 | kwiver_install_headers( 225 | ${track_synthesizer_public_headers} 226 | SUBDIR scoring_framework 227 | ) 228 | 229 | kwiver_install_headers( 230 | ${CMAKE_CURRENT_BINARY_DIR}/track_synthesizer_export.h 231 | SUBDIR scoring_framework 232 | NOPATH 233 | ) 234 | 235 | kwiver_add_library( track_synthesizer 236 | ${track_synthesizer_public_headers} 237 | ${track_synthesizer_sources} 238 | ${CMAKE_CURRENT_BINARY_DIR}/track_synthesizer_export.h 239 | ) 240 | target_link_libraries( track_synthesizer 241 | PUBLIC track_oracle 242 | score_core 243 | PRIVATE vital_logger 244 | vnl 245 | ) 246 | -------------------------------------------------------------------------------- /scoring_framework/activity_phase1_parameters.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2012 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #ifndef INCL_ACTIVITY_PHASE1_PARAMETERS_H 8 | #define INCL_ACTIVITY_PHASE1_PARAMETERS_H 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace vidtk 15 | { 16 | 17 | struct activity_phase1_parameters 18 | { 19 | public: 20 | double min_percentage_of_event_matches; 21 | double min_number_of_event_maches; 22 | //The min overlap diff(max(gt_begin,cp_begin),min(gt_end,cp_end))/diff(gt_begin, gt_end) 23 | double min_percentage_time_overlap; 24 | double min_number_of_frames_overlap; 25 | 26 | 27 | activity_phase1_parameters() 28 | : min_percentage_of_event_matches(0), 29 | min_number_of_event_maches(1), 30 | min_percentage_time_overlap(0), 31 | min_number_of_frames_overlap( 1 ) 32 | {} 33 | }; 34 | 35 | } // namespace vidtk 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /scoring_framework/event_phase1_parameters.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2012 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #ifndef INCL_EVENT_PHASE1_PARAMETERS_H 8 | #define INCL_EVENT_PHASE1_PARAMETERS_H 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace vidtk 15 | { 16 | 17 | struct event_phase1_parameters 18 | { 19 | public: 20 | //The min overlap diff(max(gt_begin,cp_begin),min(gt_end,cp_end))/diff(gt_begin, gt_end) 21 | double min_percentage_fragment_ctrack_and_gtrack_overlap; 22 | double min_percentage_track_matches; 23 | double min_number_of_track_matches; 24 | //The min overlap diff(max(gt_begin,cp_begin),min(gt_end,cp_end))/diff(gt_begin, gt_end) 25 | double min_percentage_event_time_overlap; 26 | double min_number_of_track_segments_frames_overlap_matches; 27 | double min_number_of_event_frame_overlap; 28 | //For a match to be considered the best, both the gt and cp must have it has their first match 29 | bool do_bi_directional_matching_tp; 30 | bool do_bi_directional_matching_fp; 31 | 32 | 33 | event_phase1_parameters() 34 | : min_percentage_fragment_ctrack_and_gtrack_overlap(0), 35 | min_percentage_track_matches(0), 36 | min_number_of_track_matches(1), 37 | min_number_of_track_segments_frames_overlap_matches( 1 ), 38 | min_number_of_event_frame_overlap(1), 39 | do_bi_directional_matching_tp(true), 40 | do_bi_directional_matching_fp(true) 41 | {} 42 | }; 43 | 44 | } // namespace vidtk 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /scoring_framework/matching_args_type.cxx: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2010-2017 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #include "matching_args_type.h" 8 | 9 | #include 10 | 11 | #include 12 | static kwiver::vital::logger_handle_t main_logger( kwiver::vital::get_logger( __FILE__ ) ); 13 | 14 | using std::istringstream; 15 | using std::pair; 16 | using std::string; 17 | 18 | namespace kwiver { 19 | namespace kwant { 20 | 21 | bool 22 | matching_args_type 23 | ::sanity_check() const 24 | { 25 | bool ret = true; 26 | bool use_radial = (this->radial_overlap() >= 0.0); 27 | bool use_spatial = 28 | this->bbox_expansion.set() 29 | || (this->min_bound_area() > 0.0) 30 | || this->min_pcent_gt_ct.set() 31 | || this->iou.set(); 32 | if ( use_radial && use_spatial ) 33 | { 34 | LOG_ERROR( main_logger, "Both spatial (bounding box) and radial overlap options have been set.\n" << 35 | "Spatial and radial overlap testing are mutually exclusive; please select\n" << 36 | "only one method.\n" ); 37 | ret = false; 38 | } 39 | 40 | // non-const because vul_arg_base.option() is not const 41 | matching_args_type* mutable_me = const_cast< matching_args_type * >( this ); 42 | 43 | if (this->min_pcent_gt_ct.set() && this->iou.set()) 44 | { 45 | LOG_ERROR( main_logger, string("\n") 46 | << "Can't specify both " << mutable_me->min_pcent_gt_ct.option() << " and " 47 | << mutable_me->iou.option() << "\n\n" ); 48 | ret = false; 49 | } 50 | 51 | // pass_nonzero_overlaps and radial_overlap don't make sense when specified together 52 | if ( use_radial && (this->pass_nonzero_overlaps())) 53 | { 54 | LOG_ERROR( main_logger, string("\n") 55 | << "Can't specify both " << mutable_me->radial_overlap.option() << " and " 56 | << mutable_me->pass_nonzero_overlaps.option() << ".\n\n" 57 | << "Passing non-zero overlaps allows separation\n" 58 | << "of spatial overlaps into two categories: a strong test used to decide\n" 59 | << "if the tracks overlap at all, and a weak test to determine if individual\n" 60 | << "detections overlap. Radial overlaps are binary; we don't have a distinction\n" 61 | << "between 'strong' and 'weak' readial overlaps yet.\n\n" ); 62 | ret = false; 63 | } 64 | 65 | string pcent_opt = mutable_me->min_pcent_gt_ct.option(); 66 | string min_bounding_area_opt = mutable_me->min_bound_area.option(); 67 | string min_filter_frames_opt = mutable_me->min_frames_arg.option(); 68 | if (this->min_pcent_gt_ct() == "help") 69 | { 70 | LOG_ERROR( main_logger, string( "\n" ) << 71 | "The " << pcent_opt << " option sets the minimum area, as a percentage of the box, which\n" 72 | "must be overlapped in either the ground-truth box, computed box, or both, for the\n" 73 | "overlap to count as a hit.\n\n" 74 | "If set, this option overrides the " << min_bounding_area_opt << " option.\n\n" 75 | "This option is specified as two numbers separated by a colon, e.g. '20.5:5'.\n" 76 | "The first is the percentage of the ground-truth box; the second is the percentage\n" 77 | "of the computed box. To set one value as don't care, set it to 0; for example,\n" 78 | "'54.9:0' means as long as 54.9 percent of the ground-truth box is overlapped, any amount\n" 79 | "of overlap in the computed box is allowed; similarly, '0:10' means as long as 10 percent\n" 80 | "of the computed box is overlapped, any overlap in the ground-truth box is accepted.\n\n" 81 | "If the separating character is 'd' instead of ':', overlap area computations are dumped to cerr.\n" 82 | "\n" 83 | "If " << pcent_opt << " is specified, " << min_filter_frames_opt << " must be explicitly set\n" 84 | "to state how many frames must pass the " << pcent_opt << " criteria before the track-to-track\n" 85 | "overlap is accepted.\n" ); 86 | ret = false; 87 | } 88 | 89 | if (this->aoi_string() == "help") 90 | { 91 | LOG_INFO( main_logger, string( "\n" ) << 92 | "Spatial AOIs may be specified in one of three formats:\n" 93 | "\n" 94 | "1) Pixel AOI: 'WxH+x+y', e.g. '240x191+100+100'\n" 95 | "2) lon/lat 2-corner: 'NW_lon,NW_lat:SE_lon,SE_lat', e.g. '-72,43:-71,42'\n" 96 | "3) lon/lat 4-corner: 4 pairs of (lon,lat), e.g. '-72,43:-72,42:-71,42:-71,43'\n" 97 | "\n" 98 | "Pixel AOIs always construct axis-aligned bounding boxes.\n" 99 | "\n" 100 | "A lon/lat 2-corner box is expanded into a lon/lat-aligned 4-corner box\n" 101 | "and processed in the same way as arbitrary 4-corner boxes.\n" 102 | "\n" 103 | "A lon/lat 4-corner box is processed by converting each corner into UTM\n" 104 | "and constructing a convex hull, rather than an northing/easting aligned\n" 105 | "box. If no single UTM zone contains all four corners, an exception is thrown.\n" 106 | "\n" ); 107 | ret = false; 108 | } 109 | 110 | if (this->min_pcent_gt_ct.set() && (! this->min_frames_arg.set() )) 111 | { 112 | LOG_ERROR( main_logger, string("\n") << 113 | "When using using the " << pcent_opt << " option, the " << min_filter_frames_opt << " option\n" 114 | "must be explicitly set. Use 'help' as the argument to " << pcent_opt << " for details.\n" ); 115 | ret = false; 116 | } 117 | 118 | return ret; 119 | } 120 | 121 | bool 122 | matching_args_type 123 | ::parse_min_pcent_gt_ct( pair& p, bool& debug_flag ) const 124 | { 125 | istringstream iss( this->min_pcent_gt_ct() ); 126 | double d1, d2; 127 | char c; 128 | if ( (iss >> d1 >> c >> d2)) 129 | { 130 | p.first = d1; 131 | p.second = d2; 132 | debug_flag = (c == 'd'); 133 | LOG_INFO( main_logger, "minimum gt/ct overlap computation enabled; gt percentage: " << p.first 134 | << "; ct percentage: " << p.second << "; debug mode: " << debug_flag ); 135 | return true; 136 | } 137 | else 138 | { 139 | LOG_ERROR( main_logger, "Couldn't parse '" << this->min_pcent_gt_ct() 140 | << "' as a minimum gt/ct overlap argument; try 'help' for help" ); 141 | return false; 142 | } 143 | } 144 | 145 | bool 146 | matching_args_type 147 | ::parse_min_frames_arg() 148 | { 149 | const string& s = this->min_frames_arg(); 150 | size_t p = s.find_first_of( "p" ); 151 | // the policy is absolute if no 'p' is in the string 152 | this->min_frames_policy.first = (p == string::npos); 153 | istringstream iss( s.substr( 0, p )); 154 | if ( ! ( iss >> this->min_frames_policy.second )) 155 | { 156 | LOG_ERROR( main_logger, "Couldn't parse min-frames value from '" << s.substr(0,p) << "'?" ); 157 | return false; 158 | } 159 | return true; 160 | } 161 | 162 | } // ...kwant 163 | } // ...kwiver 164 | -------------------------------------------------------------------------------- /scoring_framework/matching_args_type.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2010-2017 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #ifndef INCL_MATCHING_ARGS_TYPE_H 8 | #define INCL_MATCHING_ARGS_TYPE_H 9 | 10 | /// 11 | /// This type defines the various options passed to the phase1 object 12 | /// to control what it means for two detections to overlap. 13 | /// 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | namespace kwiver { 23 | namespace kwant { 24 | 25 | struct SCORE_CORE_EXPORT matching_args_type 26 | { 27 | vul_arg frame_alignment_secs; 28 | vul_arg bbox_expansion; 29 | vul_arg aoi_string; 30 | vul_arg min_bound_area; 31 | vul_arg min_frames_arg; 32 | vul_arg min_pcent_gt_ct; 33 | vul_arg iou; 34 | vul_arg radial_overlap; 35 | vul_arg pass_nonzero_overlaps; 36 | std::pair< bool, double > min_frames_policy; // first: true if absolute, false if percentage 37 | matching_args_type() 38 | : frame_alignment_secs( "--frame-align", "timestamp difference (secs) between aligned frames in truth and test data", 1.0 / 2.0 ), 39 | bbox_expansion( "--expand-bbox", "expand bounding boxes in meters (if gsd given) or pixels (if not)" ), 40 | aoi_string( "--aoi", "Scoring AOI: either pixel, 2-corner lon/lat, or 4-corner lon/lat; 'help' for more details" ), 41 | min_bound_area( "--match-overlap-lower-bound", "frame overlap must be > this to match", 0.0 ), 42 | min_frames_arg( "--match-frames-lower-bound", "if != 0, at least this many frames must overlap for track to match; 'p' for %age of gt", "0" ), 43 | min_pcent_gt_ct( "--min-pcent-gt-ct", "%age of gt/ct box which must be overlapped; set to 'help' for more info; overrides match-overlap-lower-bound" ), 44 | iou( "--iou", "intersection-over-union: ratio of overlap to union of bounding boxes; overrides match-overlap-lower-bound; test is >=" ), 45 | radial_overlap( "--radial-overlap", "-1.0 to disable; otherwise, distance in meters between detections to match", -1.0 ), 46 | pass_nonzero_overlaps( "--pass-nonzero-overlaps", "if set, ALL frames with nonzero overlaps will be used for stats" ), 47 | min_frames_policy( std::make_pair( false, 0.0 )) 48 | {} 49 | 50 | bool sanity_check() const; 51 | bool parse_min_pcent_gt_ct( std::pair& p, bool& debug_flag ) const; 52 | bool parse_min_frames_arg(); 53 | }; 54 | 55 | } // ...kwant 56 | } // ...kwiver 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /scoring_framework/phase1_parameters.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2010-2017 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #ifndef INCL_PHASE1_PARAMETERS_H 8 | #define INCL_PHASE1_PARAMETERS_H 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #ifdef KWANT_ENABLE_MGRS 19 | #include 20 | #endif 21 | 22 | namespace kwiver { 23 | namespace kwant { 24 | 25 | namespace kwto = ::kwiver::track_oracle; 26 | 27 | struct matching_args_type; 28 | 29 | struct SCORE_CORE_EXPORT phase1_frame_window 30 | { 31 | ts_type f0, f1; 32 | bool is_set; 33 | phase1_frame_window(): f0(0), f1(0), is_set( false ) {} 34 | phase1_frame_window( ts_type F0, ts_type F1 ): f0(F0), f1(F1), is_set( true ) {} 35 | }; 36 | 37 | struct SCORE_CORE_EXPORT mgrs_aoi 38 | { 39 | vgl_polygon aoi; 40 | int zone; 41 | mgrs_aoi(): zone(-1) {} 42 | mgrs_aoi( const vgl_polygon& a, int z ): aoi(a), zone(z) {} 43 | }; 44 | 45 | struct SCORE_CORE_EXPORT phase1_parameters 46 | { 47 | bool expand_bbox; 48 | 49 | double bbox_expansion; 50 | 51 | double frame_alignment_time_window_usecs; 52 | 53 | // (Jon: What does this mean? I'm assuming it means: if a point p is in the 54 | // AOI and aoiInclusive is true, then the point counts; if a point is outside 55 | // the AOI and aoiInclusive is false, then the point counts. -- Roddy) 56 | bool aoiInclusive; //Is AOI inclusive or exclusive? 57 | 58 | // AOI processing: AOIs may be specified in either pixels (b_aoi) 59 | // or lat/lon (mgrs_aoi), but not both. mgrs_aoi_list is only checked when 60 | // radial_overlap >= 0. If the mgrs_aoi_list is empty, then no AOI 61 | // was requested. 62 | // 63 | // If no (pixel) AOI is set then the entire frame is an AOI. 64 | // 65 | vgl_box_2d b_aoi; 66 | std::vector< mgrs_aoi > mgrs_aoi_list; 67 | 68 | // if set, results will only be computed in this frame window 69 | phase1_frame_window frame_window; 70 | 71 | bool perform_sanity_checks; 72 | 73 | // on a single frame, area must be > min_bound_matching_area to match 74 | double min_bound_matching_area; 75 | 76 | // (f,s) = min_frames_policy.(first,second) 77 | // if (f == true) (absolute): entire track must have > s frames to match 78 | // if (f == false) (percentage): number of matches must be > s% of ground truth length 79 | std::pair< bool, double > min_frames_policy; 80 | 81 | // percentage of (gt, ct) boxes which must be overlapped for it to be a hit; <0 for don't care 82 | std::pair min_pcent_overlap_gt_ct; 83 | 84 | // intersection-over-union; not compatible with min_pcent_overlap_gt_ct 85 | double iou; 86 | 87 | // if true, dump statistics 88 | bool debug_min_pcent_overlap_gt_ct; 89 | 90 | // radial overlap: -1 (or anything <0) to disable and use bounding boxes. Otherwise, 91 | // distance in meters between two detections for them to match. 92 | double radial_overlap; 93 | 94 | // when using radial overlap, to test for inclusion in an AOI, convert point-only 95 | // detections into a square box this many pixels on a side, centered on the detection. 96 | // Not used when radial_overlap < 0. 97 | unsigned point_detection_box_size; 98 | 99 | // if true, all non-zero overlaps will passed downstream for 100 | // computing e.g. purity and continuity. If false, only those 101 | // frames passing the frame filters (min_bound_matching_area, 102 | // min_matching_frames, min_pcent_overlap_gt_ct) are passed 103 | // downstream. Invalid if radial overlap requested. 104 | bool pass_all_nonzero_overlaps; 105 | 106 | 107 | phase1_parameters() 108 | : expand_bbox(false), 109 | bbox_expansion(0.0), 110 | frame_alignment_time_window_usecs( 1.0 / 30.0 * 1.0e6 ), 111 | aoiInclusive(false), 112 | perform_sanity_checks(true), 113 | min_bound_matching_area(0.0), 114 | min_frames_policy( std::make_pair( true, 0.0 )), 115 | min_pcent_overlap_gt_ct( std::make_pair(-1.0, -1.0) ), 116 | iou ( -1.0 ), 117 | debug_min_pcent_overlap_gt_ct( false ), 118 | radial_overlap( -1.0 ), 119 | pass_all_nonzero_overlaps( false ) 120 | {} 121 | 122 | explicit phase1_parameters(double expansion) 123 | : expand_bbox(true), 124 | bbox_expansion(expansion), 125 | frame_alignment_time_window_usecs( 1.0 / 30.0 * 1.0e6 ), 126 | aoiInclusive(false), 127 | perform_sanity_checks(true), 128 | min_bound_matching_area(0.0), 129 | min_frames_policy( std::make_pair( true, 0.0 )), 130 | min_pcent_overlap_gt_ct( std::make_pair(-1.0, -1.0) ), 131 | iou( -1.0 ), 132 | debug_min_pcent_overlap_gt_ct( false ), 133 | radial_overlap( -1.0 ), 134 | pass_all_nonzero_overlaps( false ) 135 | {} 136 | 137 | bool processMatchingArgs( const matching_args_type& m ); 138 | 139 | void setAOI(vgl_box_2d bbox, bool inclusive ); 140 | bool setAOI( const std::string& aoi_string ); 141 | 142 | void set_frame_window( ts_type f0, ts_type f1 ); 143 | 144 | // remove tracks which, even after bounding box expansion, do not 145 | // have an "AOI match" (in the AOI if inclusive, outside if exclusive) 146 | // If frame window is set, also tosses tracks which are entirely outside 147 | // the frame window. 148 | // 149 | // Return the min and max timestamps in the filtered track list, for 150 | // later normalization. 151 | 152 | std::pair filter_track_list_on_aoi( const kwto::track_handle_list_type& in, 153 | kwto::track_handle_list_type& out ); 154 | 155 | private: 156 | enum AOI_STATUS {NO_AOI_USED, PIXEL_AOI, GEO_AOI}; 157 | 158 | AOI_STATUS get_aoi_status() const; 159 | bool frame_within_pixel_aoi( const kwto::frame_handle_type& f ) const; 160 | bool frame_within_geo_aoi( const kwto::frame_handle_type& f ) const; 161 | 162 | }; 163 | 164 | } // ...kwant 165 | } // ...kwiver 166 | 167 | #endif 168 | -------------------------------------------------------------------------------- /scoring_framework/quickfilter_box.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2012-2016 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #ifndef INCL_QUICKFILTER_BOX_H 8 | #define INCL_QUICKFILTER_BOX_H 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | #ifdef KWANT_ENABLE_MGRS 18 | #include 19 | #endif 20 | 21 | namespace kwiver { 22 | namespace kwant { 23 | 24 | namespace kwto = ::kwiver::track_oracle; 25 | 26 | struct phase1_parameters; 27 | 28 | // 29 | // This structure enables quick-filtering on spatial extent 30 | // by holding a bounding box containing the entire track; for 31 | // any two tracks, if these boxes do not intersect, then we can 32 | // skip a temporally-ordered frame-by-frame comparison. 33 | // 34 | // The coordinate system of the track-level bounding box is determined 35 | // by the overlap criterion: spatial overlap is in image coordinates; 36 | // radial overlap is in MGRS. (Tracks which cross MGRS boundaries 37 | // will have no quick-filter box and thus will always pass the quick- 38 | // filter test.) 39 | // 40 | // One instance of this schema is associated with a track; the 41 | // appropriate add_ method is called for each frame; at the end, 42 | // you have the bounding box of the track in the appropriate coord 43 | // system. (The coord system is not set explicitly, but instead 44 | // set the first time an add_ method is called.) 45 | // 46 | // The implementation is an awkward example of a track oracle union. 47 | // 48 | 49 | struct SCORE_CORE_EXPORT quickfilter_box_type: public kwto::track_base< quickfilter_box_type > 50 | { 51 | enum {COORD_NONE=0, COORD_IMG, COORD_MGRS}; 52 | // what coordinate system is the bounding box? 53 | kwto::track_field< int >& coord_system; 54 | 55 | // valid only if coord_system is COORD_IMG. 56 | kwto::track_field< vgl_box_2d >& img_box; 57 | 58 | #ifdef KWANT_ENABLE_MGRS 59 | // valid only if coord_system is COORD_MGRS. 60 | // It's tempting to have a vgl_box_2d of northing/easting 61 | // to avoid having to duplicate all vgl_box_2d's nice logic for 62 | // adding to a bounding box, but on the other hand scorable_mgrs 63 | // already handles arbitrating between MGRS zones. 64 | kwto::track_field< kwto::scorable_mgrs >& sw_point; 65 | kwto::track_field< kwto::scorable_mgrs >& ne_point; 66 | #endif 67 | 68 | // relevant only if coord_system is COORD_MGRS. 69 | // True if all points added so far have at least one 70 | // compatible zone. Initialized to true; once false, stays 71 | // false. 72 | kwto::track_field< bool >& mgrs_valid_latch; 73 | 74 | quickfilter_box_type(): 75 | coord_system( Track.add_field< int >( "qf_box_coord_system" )), 76 | img_box( Track.add_field< vgl_box_2d >( "qf_box_img_box" )), 77 | #ifdef KWANT_ENABLE_MGRS 78 | sw_point( Track.add_field< kwto::scorable_mgrs >( "qf_box_sw_point" )), 79 | ne_point( Track.add_field< kwto::scorable_mgrs >( "qf_box_ne_point" )), 80 | #endif 81 | mgrs_valid_latch( Track.add_field( "mgrs_valid_latch" )) 82 | {} 83 | 84 | // these are initialized to invalid; set to (truth, computed) 85 | // external IDs to trigger debugging 86 | static std::pair< unsigned, unsigned > debug_track_ids; 87 | 88 | // client's main function to add instances of this data 89 | // structure to the track list 90 | static void add_quickfilter_boxes( const kwto::track_handle_list_type& t, 91 | const phase1_parameters& params ); 92 | 93 | #ifdef KWANT_ENABLE_MGRS 94 | // incorporates s such that {sw,ne}_point is the convex hull of the points 95 | // and s. Throws if coord_system == coord_img. 96 | void add_scorable_mgrs( const kwto::track_handle_type& t, 97 | kwto::scorable_mgrs s ); 98 | #endif 99 | 100 | // Adds box to the box already associated with the track. 101 | // Throws if coord_system == coord_mgrs. 102 | void add_image_box( const kwto::track_handle_type& t, 103 | vgl_box_2d box ); 104 | 105 | #ifdef KWANT_ENABLE_MGRS 106 | // If either (or both) of t1/t2 have coord_system != coord_mgrs, return -1. 107 | // If either (or both) of t1/t2 contain invalid {se,ne} points, return -1. 108 | // If t1/t2 do not have compatible zones for both sets of {sw,ne}_points, return 0. 109 | // Otherwise, t1/t2 have compatible MGRS boxes; return area of overlap (or 0 if none.) 110 | // (The idea is that -1 means "you need to examine t1 and t2 on a frame-by-frame 111 | // basis", while >=0 is a valid result.) 112 | double mgrs_box_intersect( const kwto::track_handle_type& t1, 113 | const kwto::track_handle_type& t2 ); 114 | 115 | #endif 116 | double img_box_intersect( const kwto::track_handle_type& t1, 117 | const kwto::track_handle_type& t2 ); 118 | 119 | // return >=0 if valid boxes could be compared; return -1 if no 120 | // quickfilter decision could be made. 121 | double quickfilter_check( const kwto::track_handle_type& t1, 122 | const kwto::track_handle_type& t2, 123 | bool use_radial_overlap ); 124 | 125 | }; 126 | 127 | } // ...kwant 128 | } // ...kwiver 129 | 130 | #endif 131 | -------------------------------------------------------------------------------- /scoring_framework/score_core.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2010-2016 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #ifndef INCL_SCORE_CORE_H 8 | #define INCL_SCORE_CORE_H 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | // The central scoring framework assumption is: Everything you need to 21 | // know in order to score two sets of tracks can be localized to two 22 | // locations: the track itself and the phase-specific contexts. For example: 23 | // timestamps and bounding boxes are in the track. Normalization constants 24 | // and AOI filters and so forth are in the contexts. 25 | // 26 | // This file defines the track-specific knowledge. As long as your track 27 | // defines these three fields, we can score it. 28 | // 29 | 30 | namespace kwiver { 31 | namespace kwant { 32 | 33 | namespace kwto = ::kwiver::track_oracle; 34 | 35 | 36 | typedef std::pair< kwto::track_handle_type, kwto::track_handle_type > track2track_type; 37 | typedef unsigned long long ts_type; 38 | typedef std::pair ts_frame_range; 39 | 40 | // state of the frame-has-been-matched flag: outside aoi, in_aoi_unmatched, in_aoi_matched 41 | // order so that IN_AOI_UNMATCHED is zero (default). 42 | enum FRAME_MATCH_STATE { IN_AOI_UNMATCHED = 0, OUTSIDE_AOI, IN_AOI_MATCHED }; 43 | 44 | struct SCORE_CORE_EXPORT scorable_track_type: public kwto::track_base< scorable_track_type > 45 | { 46 | kwto::track_field< kwto::dt::tracking::external_id > external_id; 47 | kwto::track_field< kwto::dt::tracking::bounding_box > bounding_box; 48 | kwto::track_field< kwto::dt::tracking::frame_number > timestamp_frame; 49 | kwto::track_field< kwto::dt::tracking::timestamp_usecs > timestamp_usecs; 50 | kwto::track_field< int >& frame_has_been_matched; 51 | kwto::track_field< unsigned >& frames_in_aoi; 52 | scorable_track_type() 53 | : frame_has_been_matched( Frame.add_field< int >( "frame_has_been_matched" )), 54 | frames_in_aoi(Track.add_field< unsigned >("frames_in_aoi")) 55 | { 56 | Track.add_field( external_id ); 57 | Frame.add_field( bounding_box ); 58 | Frame.add_field( timestamp_frame ); 59 | Frame.add_field( timestamp_usecs ); 60 | } 61 | }; 62 | 63 | } // ...kwiver 64 | } // ...kwant 65 | 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /scoring_framework/score_frames_aipr.cxx: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2010 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #include "score_frames_aipr.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | using std::map; 22 | using std::vector; 23 | 24 | using namespace vidtk; 25 | 26 | void get_frame_indexed_map(vector& trks, 27 | map< int, vector >& o_trks, 28 | int* num_frames, 29 | double /*min_overlap_ratio*/ = 0) 30 | { 31 | vector::iterator iter; 32 | for(iter = trks.begin(); iter != trks.end(); ++iter) 33 | { 34 | vector hist = (*iter)->history(); 35 | vector::iterator iter2; 36 | for(iter2 = hist.begin(); iter2 != hist.end(); ++iter2) 37 | { 38 | track_state_sptr ts = (*iter2); 39 | int frame_num = ts->time_.frame_number(); 40 | (*num_frames) = ( *num_frames) > frame_num ? (*num_frames) : frame_num+1; 41 | vidtk::track_sptr to_push = new vidtk::track; 42 | to_push->set_id((*iter)->id()); 43 | to_push->add_state(ts); 44 | o_trks[frame_num].push_back(to_push); 45 | } 46 | } 47 | } 48 | 49 | void compute_frame_based_metrics(vector& fbm, 50 | vector& /*gt_tracks*/, 51 | vector& /*comp_tracks*/, 52 | map< int, vector > gt_fi_map, 53 | map< int, vector > comp_fi_map, 54 | int num_frames, 55 | double min_overlap_ratio = 0) 56 | { 57 | vector< map > > gt_frame_level_associations; 58 | vector< map< int, vector > > comp_frame_level_associations; 59 | 60 | //Create frame level associations 61 | for(int i = 0; i < num_frames; i++) 62 | { 63 | vector::iterator iter; 64 | //Add an assocation map for every frame 65 | map< int, vector > tmp1; 66 | gt_frame_level_associations.push_back(tmp1); 67 | map< int, vector > tmp2; 68 | comp_frame_level_associations.push_back(tmp2); 69 | 70 | //initialize vectors to 0 in association maps 71 | for(iter = comp_fi_map[i].begin(); iter != comp_fi_map[i].end(); ++iter) 72 | { 73 | vidtk::track_sptr comp = (*iter); 74 | if(comp_frame_level_associations[i].find(comp->id()) == comp_frame_level_associations[i].end()) 75 | { 76 | comp_frame_level_associations[i][comp->id()].clear(); 77 | } 78 | } 79 | 80 | for(iter = gt_fi_map[i].begin(); iter != gt_fi_map[i].end(); ++iter) 81 | { 82 | vector::iterator iter2; 83 | vidtk::track_sptr gt = (*iter); 84 | //initialize vectors to 0 in association maps 85 | if(gt_frame_level_associations[i].find(gt->id()) == gt_frame_level_associations[i].end()) 86 | { 87 | gt_frame_level_associations[i][gt->id()].clear(); 88 | } 89 | for(iter2 = comp_fi_map[i].begin(); iter2 != comp_fi_map[i].end(); ++iter2) 90 | { 91 | vidtk::track_sptr comp = (*iter2); 92 | //initialize vectors to 0 in association maps 93 | if(comp_frame_level_associations[i].find(comp->id()) == comp_frame_level_associations[i].end()) 94 | { 95 | comp_frame_level_associations[i][comp->id()].clear(); 96 | } 97 | vgl_box_2d intersection = vgl_intersection(gt->history()[0]->amhi_bbox_,comp->history()[0]->amhi_bbox_); 98 | 99 | double overlap_score = 0; 100 | if( vgl_area( intersection ) != 0) 101 | { 102 | overlap_score = ( vgl_area( gt->history()[0]->amhi_bbox_) + 103 | vgl_area( comp->history()[0]->amhi_bbox_) - 104 | vgl_area( intersection )) 105 | / vgl_area( intersection ); 106 | } 107 | 108 | if(!intersection.is_empty() && overlap_score >= min_overlap_ratio) 109 | { 110 | gt_frame_level_associations[i][gt->id()].push_back(comp->id()); 111 | comp_frame_level_associations[i][comp->id()].push_back(gt->id()); 112 | } 113 | } 114 | } 115 | } 116 | 117 | //compute metrics 118 | for(int i = 0; i < num_frames; i++) 119 | { 120 | map< int,vector >::iterator iter; 121 | frame_based_metrics to_push; 122 | to_push.detection_probability_count = 0; 123 | to_push.detection_probability_presence = 0; 124 | to_push.false_negatives_count = 0; 125 | to_push.false_positives_count = 0; 126 | to_push.merge_fraction = 0; 127 | to_push.num_merge = 0; 128 | to_push.num_merge_mult = 0; 129 | to_push.num_split = 0; 130 | to_push.num_split_mult = 0; 131 | to_push.precision_count = 0; 132 | to_push.precision_presence = 0; 133 | to_push.split_fraction = 0; 134 | to_push.true_positives_count = 0; 135 | to_push.true_positives_presence = 0; 136 | to_push.gt_with_associations = 0; 137 | to_push.gt_without_associations = 0; 138 | 139 | fbm.push_back(to_push); 140 | //Compute false_negatives, true_positives, and splits 141 | 142 | for( iter = gt_frame_level_associations[i].begin(); iter != gt_frame_level_associations[i].end(); ++iter) 143 | { 144 | //The number of computed tracks associated with this ground truth 145 | int num_comp_assoc = (*iter).second.size(); 146 | 147 | if(num_comp_assoc == 0) 148 | { 149 | fbm[i].false_negatives_count++; 150 | continue; 151 | } 152 | fbm[i].gt_with_associations++; 153 | //The number of ground truth tracks this ground truth tracks associations are associated with 154 | int num_gt_assoc = comp_frame_level_associations[i][(*iter).second[0]].size(); 155 | 156 | if( num_gt_assoc == 1 && num_comp_assoc == 1) 157 | { 158 | fbm[i].true_positives_count++; 159 | fbm[i].true_positives_presence++; 160 | } 161 | 162 | if ( num_comp_assoc > 1 ) 163 | { 164 | fbm[i].true_positives_count++; 165 | fbm[i].false_positives_count+=num_comp_assoc-1; 166 | fbm[i].true_positives_presence++; 167 | fbm[i].num_split++; 168 | fbm[i].num_split_mult += (num_comp_assoc - 1); 169 | } 170 | } 171 | //compute false positives, false negatives and merges 172 | for( iter = comp_frame_level_associations[i].begin(); iter != comp_frame_level_associations[i].end(); ++iter) 173 | { 174 | //The number of ground truth tracks associated with this ground truth 175 | int num_gt_assoc = (*iter).second.size(); 176 | if(num_gt_assoc == 0) 177 | { 178 | fbm[i].false_positives_count++; 179 | continue; 180 | } 181 | 182 | if ( num_gt_assoc > 1 ) 183 | { 184 | fbm[i].true_positives_count++; 185 | fbm[i].false_negatives_count+=num_gt_assoc-1; 186 | fbm[i].true_positives_presence+=num_gt_assoc; 187 | fbm[i].num_merge++; 188 | fbm[i].num_merge_mult += (num_gt_assoc - 1); 189 | } 190 | } 191 | 192 | //Compute ratios 193 | 194 | fbm[i].gt_without_associations = gt_frame_level_associations[i].size() - fbm[i].gt_with_associations; 195 | if(fbm[i].gt_with_associations == 0 || 196 | gt_frame_level_associations[i].size() == 0 || 197 | comp_frame_level_associations[i].size() + fbm[i].num_merge - fbm[i].num_split == 0) 198 | { 199 | continue; 200 | } 201 | fbm[i].detection_probability_presence = static_cast(fbm[i].true_positives_presence)/gt_frame_level_associations[i].size(); 202 | fbm[i].detection_probability_count= static_cast(fbm[i].true_positives_count)/gt_frame_level_associations[i].size(); 203 | 204 | fbm[i].precision_presence = fbm[i].true_positives_presence/static_cast(comp_frame_level_associations[i].size() + fbm[i].num_merge - fbm[i].num_split); 205 | fbm[i].precision_count = fbm[i].true_positives_presence/static_cast(comp_frame_level_associations[i].size() + fbm[i].num_merge - fbm[i].num_split); 206 | 207 | fbm[i].split_fraction = fbm[i].num_split/static_cast(fbm[i].gt_with_associations); 208 | fbm[i].merge_fraction = fbm[i].num_merge/static_cast(fbm[i].gt_with_associations); 209 | } 210 | } 211 | 212 | void compute_metrics(vector& fbm, 213 | vector& gt_tracks, 214 | vector& comp_tracks, 215 | double min_overlap_ratio) 216 | { 217 | //Frame Indexed Maps 218 | map< int, vector > gt_fi_map; 219 | map< int, vector > comp_fi_map; 220 | 221 | int num_frames = 0; 222 | 223 | get_frame_indexed_map(gt_tracks,gt_fi_map,&num_frames,min_overlap_ratio); 224 | get_frame_indexed_map(comp_tracks,comp_fi_map,&num_frames,min_overlap_ratio); 225 | 226 | compute_frame_based_metrics(fbm, gt_tracks, comp_tracks, gt_fi_map, comp_fi_map, num_frames,min_overlap_ratio); 227 | } 228 | -------------------------------------------------------------------------------- /scoring_framework/score_frames_aipr.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2010 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #ifndef score_frames_aipr_h_ 8 | #define score_frames_aipr_h_ 9 | 10 | #include 11 | #include 12 | 13 | struct frame_based_metrics 14 | { 15 | double true_positives_count; 16 | double false_negatives_count; 17 | double false_positives_count; 18 | 19 | double true_positives_presence; 20 | 21 | double detection_probability_count; 22 | double detection_probability_presence; 23 | 24 | double precision_count; 25 | double precision_presence; 26 | 27 | double num_split; 28 | //If more than 2 tracks occur in the split 29 | double num_split_mult; 30 | double split_fraction; 31 | 32 | double num_merge; 33 | //If more than 2 tracks occur in the merge 34 | double num_merge_mult; 35 | double merge_fraction; 36 | 37 | double gt_with_associations; 38 | double gt_without_associations; 39 | }; 40 | 41 | 42 | void compute_metrics(std::vector& fbm, 43 | std::vector& gt_tracks, 44 | std::vector& comp_tracks, 45 | double min_overlap_ratio = 0); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /scoring_framework/score_phase1.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2010-2016 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #ifndef INCL_SCORE_PHASE1_H 8 | #define INCL_SCORE_PHASE1_H 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | // 24 | // In phase 1, each ground truth track is compared to each computed track 25 | // independently of all other possible pairings and reduced to a matrix 26 | // of track-to-track score objects. Ideally, all further scoring can 27 | // be based off these objects (plus the optional phase contexts) without 28 | // needing to ever look again at the tracks themselves. If you need to 29 | // look at the tracks, then whatever it was you were looking for should 30 | // probably go in this structure instead. 31 | 32 | 33 | namespace kwiver { 34 | namespace kwant { 35 | 36 | namespace kwto = ::kwiver::track_oracle; 37 | 38 | // 39 | // the track2track_score contains the results of compairing a single 40 | // pair of tracks. 41 | // 42 | 43 | kwto::frame_handle_list_type 44 | sort_frames_by_field( kwto::track_handle_type track_id, const std::string& name); 45 | 46 | 47 | struct SCORE_CORE_EXPORT track2track_frame_overlap_record 48 | { 49 | public: 50 | kwto::frame_handle_type truth_frame; 51 | kwto::frame_handle_type computed_frame; 52 | unsigned int fL_frame_num; // frame number of first overlapping frame 53 | unsigned int fR_frame_num; // frame number of second overlapping frame 54 | double truth_area; // area of ground-truth bounding box 55 | double computed_area; // area of computed bounding box 56 | double overlap_area; // area of overlap between the two 57 | double centroid_distance; // distance between centroid of boxes 58 | double center_bottom_distance; // distance between "feet" of boxes 59 | bool in_aoi; // if the result is computed on two boxes with an AOI match (see below) 60 | track2track_frame_overlap_record() 61 | : fL_frame_num(0), 62 | fR_frame_num(0), 63 | truth_area(0.0), 64 | computed_area(0.0), 65 | overlap_area(0.0), 66 | centroid_distance(0.0), 67 | center_bottom_distance(0.0), 68 | in_aoi(true) 69 | { 70 | } 71 | }; 72 | 73 | struct SCORE_CORE_EXPORT track2track_score 74 | { 75 | public: 76 | unsigned spatial_overlap_total_frames; 77 | ts_frame_range overlap_frame_range; 78 | std::vector< track2track_frame_overlap_record > frame_overlaps; 79 | track2track_score() 80 | : spatial_overlap_total_frames(0), 81 | overlap_frame_range(std::numeric_limits::max(),std::numeric_limits::max()) 82 | { 83 | } 84 | 85 | // fill in the values given truth track t and computed track c 86 | // returns false if tracks are not in the AOI 87 | bool compute( kwto::track_handle_type t, 88 | kwto::track_handle_type c, 89 | const phase1_parameters& params ); 90 | 91 | // line up the two frame lists with a tolerance of match_window 92 | // and return a list of aligned frame handles 93 | std::vector< std::pair< kwto::frame_handle_type, kwto::frame_handle_type > > 94 | align_frames( const kwto::frame_handle_list_type& f1, 95 | const kwto::frame_handle_list_type& f2, 96 | double match_window ); 97 | 98 | // given two frames, return their spatial overlap 99 | track2track_frame_overlap_record compute_spatial_overlap( kwto::frame_handle_type f1, 100 | kwto::frame_handle_type f2, 101 | const phase1_parameters& params ); 102 | 103 | #ifdef KWANT_ENABLE_MGRS 104 | // given two frames, return their radial overlap (throw if param not set) 105 | track2track_frame_overlap_record compute_radial_overlap( kwto::frame_handle_type f1, 106 | kwto::frame_handle_type f2, 107 | const phase1_parameters& params ); 108 | 109 | #endif 110 | 111 | bool within_window( const kwto::frame_handle_list_type& f1, 112 | const kwto::frame_handle_list_type& f2, 113 | unsigned f1_ptr, 114 | unsigned f2_ptr, 115 | double match_window ); 116 | 117 | kwto::descriptor_overlap_type create_overlap_descriptor() const; 118 | void add_self_to_event_label_descriptor( kwto::descriptor_event_label_type& delt ) const; 119 | 120 | private: 121 | // cached for the descriptor 122 | kwto::track_handle_type cached_truth_track, cached_comp_track; 123 | 124 | bool move_a_up_to_b( unsigned& index, 125 | const kwto::frame_handle_list_type& lagging_list, 126 | unsigned fixed_index, 127 | const kwto::frame_handle_list_type& fixed_list, 128 | double match_window ); 129 | }; 130 | 131 | struct SCORE_CORE_EXPORT track2track_phase1 132 | { 133 | public: 134 | // key: (gt_handle, computed_handle) value: resulting t2t_score 135 | std::map< track2track_type, track2track_score > t2t; 136 | 137 | track2track_phase1() 138 | {} 139 | explicit track2track_phase1( const phase1_parameters& new_params): 140 | params(new_params) 141 | {} 142 | 143 | void compute_all( const kwto::track_handle_list_type& t, 144 | const kwto::track_handle_list_type& c ); 145 | 146 | void compute_all_detection_mode( const kwto::track_handle_list_type& t, 147 | const kwto::track_handle_list_type& c ); 148 | 149 | bool compute_single( kwto::track_handle_type t, kwto::track_handle_type c); 150 | 151 | void debug_dump( const kwto::track_handle_list_type& gt_list, 152 | const kwto::track_handle_list_type& ct_list, 153 | const std::string& fn_prefix, 154 | ts_type ts_offset ) const; 155 | 156 | void debug_dump( std::ostream& os ); 157 | 158 | std::vector< track2track_type > matching_keys( const track2track_type& probe ) const; 159 | 160 | ts_type min_ts() const; 161 | 162 | phase1_parameters params; 163 | }; 164 | 165 | } // ...kwant 166 | } // ...kwiver 167 | 168 | #endif 169 | -------------------------------------------------------------------------------- /scoring_framework/score_phase2_aipr.cxx: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2011 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #include "score_tracks_aipr.h" 8 | 9 | 10 | using std::map; 11 | using std::vector; 12 | 13 | namespace vidtk 14 | { 15 | 16 | bool ranges_overlap(ts_frame_range r1, ts_frame_range r2) 17 | { 18 | return (r1.first >= r2.first && r1.first <= r2.second) || 19 | (r1.second >= r2.first && r1.second <= r2.second) || 20 | (r2.first >= r1.first && r2.first <= r1.second) || 21 | (r2.second >= r1.first && r2.second <= r1.second); 22 | } 23 | 24 | void 25 | track2track_phase2_aipr 26 | ::compute( const track_handle_list_type& t, const track_handle_list_type& c, const track2track_phase1& p1 ) 27 | { 28 | // MITRE's "target" == ground truth 29 | // MITRE's "track" == computed track 30 | 31 | 32 | this->n_true_tracks = t.size(); 33 | this->n_computed_tracks = c.size(); 34 | 35 | //Initialize t2c and c2t so each ground truth will have an entry even if 36 | //there are no associated computed tracks. This is needed for phase 3 37 | track_handle_list_type::const_iterator truth_iter = t.begin(); 38 | for(; truth_iter != t.end(); truth_iter++) 39 | { 40 | this->t2c[(*truth_iter)] = track_handle_list_type(); 41 | } 42 | track_handle_list_type::const_iterator comp_iter = c.begin(); 43 | for(; comp_iter != c.end(); comp_iter++) 44 | { 45 | this->c2t[(*comp_iter)] = track_handle_list_type(); 46 | } 47 | 48 | //Add associations but as they come in check old ones to make sure they are not in 49 | //conflict. If they are in conflict remove the one with the bigger association value 50 | for(map::const_iterator p1_t2t_iter = p1.t2t.begin(); 51 | p1_t2t_iter != p1.t2t.end(); 52 | ++p1_t2t_iter) 53 | { 54 | //set the value of the first t2t to be the first ground truth found in p1 55 | this->t2t[p1_t2t_iter->first] = track2track_scalars_aipr(); 56 | 57 | //Give each blank association a value and a range 58 | double sum_of_L2_norms = 0; 59 | vector::const_iterator frame_overlap_iter; 60 | for(frame_overlap_iter = p1_t2t_iter->second.frame_overlaps.begin(); 61 | frame_overlap_iter != p1_t2t_iter->second.frame_overlaps.end(); 62 | ++frame_overlap_iter) 63 | { 64 | sum_of_L2_norms += frame_overlap_iter->centroid_distance; 65 | } 66 | track_handle_type t_id = p1_t2t_iter->first.first; 67 | track_handle_type c_id = p1_t2t_iter->first.second; 68 | 69 | if(p1_t2t_iter->second.spatial_overlap_total_frames > 0) 70 | { 71 | this->t2t[p1_t2t_iter->first].association_value = (sum_of_L2_norms/p1_t2t_iter->second.spatial_overlap_total_frames); 72 | this->t2t[p1_t2t_iter->first].associtaion_range = p1_t2t_iter->second.overlap_frame_range; 73 | this->t2t[p1_t2t_iter->first].associated_frame_count = p1_t2t_iter->second.spatial_overlap_total_frames; 74 | this->t2t[p1_t2t_iter->first].computed_associated_with_target = true; 75 | 76 | //check to see if an assoction with this ground truth always exists 77 | track_handle_list_type::iterator t2c_iter = this->t2c[t_id].begin(); 78 | vector t2c_ids_to_remove; 79 | for(;t2c_iter != this->t2c[t_id].end(); t2c_iter++) 80 | { 81 | track_handle_type other_cid = *t2c_iter; 82 | track2track_type other_key; 83 | other_key.first = t_id; 84 | other_key.second = other_cid; 85 | 86 | if(ranges_overlap(this->t2t[other_key].associtaion_range,this->t2t[p1_t2t_iter->first].associtaion_range)) 87 | { 88 | //these comp tracks both are associated with a single groundtruth 89 | //the lower association score will be the one chosen to track it 90 | 91 | // if the association values are equal, choose the longer track 92 | bool associate_with_other_key = false; 93 | if (this->t2t[other_key].association_value < this->t2t[p1_t2t_iter->first].association_value) 94 | { 95 | associate_with_other_key = true; 96 | } 97 | else if (this->t2t[other_key].association_value == this->t2t[p1_t2t_iter->first].association_value) 98 | { 99 | if (this->t2t[other_key].associated_frame_count > this->t2t[p1_t2t_iter->first].associated_frame_count) 100 | { 101 | associate_with_other_key = true; 102 | } 103 | } 104 | 105 | if( associate_with_other_key ) 106 | { 107 | this->t2t[p1_t2t_iter->first].association_value = -1; 108 | this->t2t[p1_t2t_iter->first].computed_associated_with_target = false; 109 | } 110 | else 111 | { 112 | this->t2t[other_key].association_value = -1; 113 | this->t2t[other_key].computed_associated_with_target = false; 114 | //Don't mess with the vector currently being iterated through 115 | //Remove these id's after the loop is done 116 | t2c_ids_to_remove.push_back(other_key); 117 | this->c2t[other_cid].erase(remove( this->c2t[other_cid].begin(), this->c2t[other_cid].end(), t_id )); 118 | } 119 | } 120 | } 121 | //remove old values if they exist 122 | vector::iterator remove_id_iter; 123 | for(remove_id_iter = t2c_ids_to_remove.begin(); remove_id_iter != t2c_ids_to_remove.end(); ++remove_id_iter) 124 | { 125 | track_handle_type tmp_tid = remove_id_iter->first; 126 | track_handle_type tmp_cid = remove_id_iter->second; 127 | this->t2c[tmp_tid].erase(remove( this->t2c[tmp_tid].begin(), this->t2c[tmp_tid].end(), tmp_cid )); 128 | } 129 | } 130 | if(this->t2t[p1_t2t_iter->first].association_value != -1) 131 | { 132 | this->c2t[c_id].push_back(t_id); 133 | this->t2c[t_id].push_back(c_id); 134 | } 135 | else 136 | { 137 | if ( this->c2t.find( c_id ) == this->c2t.end() ) 138 | { 139 | this->c2t[ c_id ] = track_handle_list_type(); 140 | } 141 | if ( this->t2c.find( t_id ) == this->t2c.end() ) 142 | { 143 | this->t2c[ t_id ] = track_handle_list_type(); 144 | } 145 | } 146 | } 147 | } 148 | 149 | } //namespace vidtk 150 | -------------------------------------------------------------------------------- /scoring_framework/score_phase2_aipr.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2010 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #ifndef INCL_SCORE_PHASE2_AIPR_H 8 | #define INCL_SCORE_PHASE2_AIPR_H 9 | 10 | // Scoring, phase 2: this is the phase which reduces the matrix of track-to-track 11 | // metrics to the raw ingredients for phase 3, the actual metrics phase. 12 | // This is the phase in which, for example, assignments are optimized based on 13 | // the phase 1 results. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace vidtk 22 | { 23 | 24 | struct track2track_phase1; 25 | 26 | struct track2track_scalars_aipr 27 | { 28 | public: 29 | bool computed_associated_with_target; 30 | ts_frame_range associtaion_range; 31 | size_t associated_frame_count; 32 | double association_value; 33 | track2track_scalars_aipr() 34 | : computed_associated_with_target( false ), 35 | associated_frame_count( 0 ), 36 | association_value( -1 ) 37 | {} 38 | }; 39 | 40 | struct track2track_phase2_aipr 41 | { 42 | public: 43 | std::map< track2track_type, track2track_scalars_aipr> t2t; 44 | std::map< track_handle_type, track_handle_list_type > c2t; // key = computed ID; val = list of associated truth tracks 45 | std::map< track_handle_type, track_handle_list_type > t2c; // key = truth track ID; val = list of associated computed tracks 46 | size_t n_true_tracks; 47 | size_t n_computed_tracks; 48 | 49 | void compute( const track_handle_list_type& t, const track_handle_list_type& c, const track2track_phase1& p1 ); 50 | }; 51 | 52 | } // namespace vidtk 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /scoring_framework/score_phase2_hadwav.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2010-2016 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #ifndef INCL_SCORE_PHASE2_HADWAV_H 8 | #define INCL_SCORE_PHASE2_HADWAV_H 9 | 10 | // Scoring, phase 2: this is the phase which reduces the matrix of track-to-track 11 | // metrics to the raw ingredients for phase 3, the actual metrics phase. 12 | // This is the phase in which, for example, assignments are optimized based on 13 | // the phase 1 results. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | namespace kwiver { 23 | namespace kwant { 24 | 25 | namespace kwto = ::kwiver::track_oracle; 26 | 27 | struct track2track_phase1; 28 | 29 | struct SCORE_TRACKS_HADWAV_EXPORT track2track_scalars_hadwav 30 | { 31 | public: 32 | // true if the target and computed track-match 33 | bool computed_associated_with_target; 34 | unsigned int computed_frames_on_target; 35 | 36 | // true if the target dominates the computed 37 | // ... Any computed track is dominated by zero or one target 38 | // ... any target track can dominate multiple computed tracks 39 | bool computed_is_dominated_by_target; 40 | 41 | // true if the computed dominates the target 42 | // ... Any target is dominated by zero or one computed track 43 | // ... any computed track can dominate multiple targets 44 | bool target_is_dominated_by_computed; 45 | 46 | track2track_scalars_hadwav() 47 | : computed_associated_with_target( false ), 48 | computed_frames_on_target( 0 ), 49 | computed_is_dominated_by_target( false ), 50 | target_is_dominated_by_computed( false ) 51 | {} 52 | }; 53 | 54 | struct SCORE_TRACKS_HADWAV_EXPORT track2track_phase2_hadwav 55 | { 56 | public: 57 | std::map< track2track_type, track2track_scalars_hadwav> t2t; 58 | std::map< kwto::track_handle_type, kwto::track_handle_list_type > c2t; // key = computed ID; val = list of associated truth tracks 59 | std::map< kwto::track_handle_type, kwto::track_handle_list_type > t2c; // key = truth track ID; val = list of associated computed tracks 60 | size_t n_true_tracks; 61 | size_t n_computed_tracks; 62 | 63 | double framePD; 64 | double frameFA; 65 | double trackFramePrecision; 66 | double detectionPD; 67 | size_t detectionFalseAlarms; 68 | double detectionPFalseAlarm; 69 | bool verbose; 70 | 71 | void compute( const kwto::track_handle_list_type& t, 72 | const kwto::track_handle_list_type& c, 73 | const track2track_phase1& p1 ); 74 | void debug_dump( std::ostream& os ); 75 | 76 | explicit track2track_phase2_hadwav( bool v = false ) : 77 | n_true_tracks(0), 78 | n_computed_tracks(0), 79 | framePD(0.0), 80 | frameFA(0.0), 81 | trackFramePrecision(0.0), 82 | detectionPD(0.0), 83 | detectionFalseAlarms(0), 84 | detectionPFalseAlarm(0), 85 | verbose(v) 86 | {} 87 | }; 88 | 89 | } // ...kwant 90 | } // ...kwiver 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /scoring_framework/score_phase3_aipr.cxx: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2010-2013 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #include "score_tracks_aipr.h" 8 | 9 | #include 10 | 11 | 12 | using std::make_pair; 13 | using std::map; 14 | 15 | 16 | #undef VIDTK_DEFAULT_LOGGER 17 | #define VIDTK_DEFAULT_LOGGER __vidtk_logger_auto_score_phase3_aipr_cxx__ 18 | VIDTK_LOGGER("score_phase3_aipr_cxx"); 19 | 20 | 21 | namespace vidtk 22 | { 23 | 24 | void 25 | overall_phase3_aipr 26 | ::compute( const track2track_phase2_aipr& t2t ) 27 | { 28 | 29 | map< track_handle_type, track_handle_list_type >::const_iterator c2t_iter; 30 | map< track_handle_type, track_handle_list_type >::const_iterator t2c_iter; 31 | int num_gt_with_associations = 0; 32 | int range_gt_with_associations = 0; 33 | int range_all_gt = 0; 34 | int range_comp_with_associations = 0; 35 | size_t tcf_sum = 0; 36 | 37 | for(c2t_iter = t2t.c2t.begin(); c2t_iter != t2t.c2t.end(); ++c2t_iter) 38 | { 39 | track_handle_type c_id = c2t_iter->first; 40 | track_handle_list_type t_vector = c2t_iter->second; 41 | scorable_track_type track; 42 | int c_range = track_oracle::get_n_frames( c_id ); 43 | if(t_vector.size() != 0) 44 | { 45 | range_comp_with_associations += c_range; 46 | } 47 | if(t_vector.size() > 1) 48 | { 49 | this->num_identity_switch += 1; 50 | } 51 | } 52 | 53 | for(t2c_iter = t2t.t2c.begin(); t2c_iter != t2t.t2c.end(); ++t2c_iter) 54 | { 55 | track_handle_type t_id = t2c_iter->first; 56 | track_handle_list_type c_vector = t2c_iter->second; 57 | scorable_track_type track; 58 | int t_range = track_oracle::get_n_frames( t_id ); 59 | range_all_gt += t_range; 60 | if(c_vector.size() >= 1) 61 | { 62 | num_gt_with_associations++; 63 | range_gt_with_associations += t_range; 64 | } 65 | 66 | // the TCF numerator is the sum of amount of overlapping frames with this 67 | // ground-truth track. 68 | for (size_t ci = 0; ci < c_vector.size(); ++ci) 69 | { 70 | track2track_type key = make_pair( t_id, c_vector[ci] ); 71 | map< track2track_type, track2track_scalars_aipr>::const_iterator probe = 72 | t2t.t2t.find( key ); 73 | if ( probe != t2t.t2t.end() ) 74 | { 75 | // LOG_INFO( "TCF: ci " << ci << " adding " << probe->second.associated_frame_count << ""); 76 | tcf_sum += probe->second.associated_frame_count; 77 | } 78 | else 79 | { 80 | LOG_ERROR( "AIPR TCF error: truth track missing t2t entry for claimed overlapping computed track"); 81 | } 82 | } 83 | 84 | //numerator of NTF is the summation of comp associated with a ground truth * the length of the ground truth 85 | this->normalized_track_fragmentation += c_vector.size() * t_range; 86 | //add the number unique computed id's 87 | this->track_fragmentation += c_vector.size(); 88 | } 89 | double num_computed_tracks = t2t.c2t.size(); 90 | //Don't divide by 0 91 | if (num_computed_tracks != 0) 92 | { 93 | this->identity_switch = static_cast (this->num_identity_switch) / num_computed_tracks; 94 | } 95 | else 96 | { 97 | this->identity_switch = -1; 98 | } 99 | if (num_gt_with_associations != 0) 100 | { 101 | //the denominator of track_fragmentation is the number of ground truths with any associations 102 | this->track_fragmentation /= static_cast (num_gt_with_associations); 103 | } 104 | else 105 | { 106 | this->track_fragmentation = -1; 107 | } 108 | if ( range_gt_with_associations != 0 ) 109 | { 110 | //the denominator of normalized_track_fragmentation is the frame range of ground truths with any associations 111 | this->normalized_track_fragmentation /= static_cast (range_gt_with_associations); 112 | } 113 | else 114 | { 115 | this->normalized_track_fragmentation = -1; 116 | } 117 | if ( range_all_gt != 0 ) 118 | { 119 | //sum of all frame ranges of associated computed tracks over the sum of all frame ranges of ground truth tracks 120 | // from 2006 paper: tcf = 121 | // sum over all ground truth G of: 122 | // sum over all computed tracks T overlapping G of: 123 | // number of frames in overlap of (T,G) 124 | // 125 | // divded by 126 | // 127 | // sum over all ground truth G of: 128 | // length of G 129 | // 130 | this->track_completeness_factor = 1.0 * tcf_sum / range_all_gt; 131 | } 132 | else 133 | { 134 | this->track_completeness_factor = -1; 135 | } 136 | 137 | 138 | } 139 | 140 | } // namespace vidtk 141 | -------------------------------------------------------------------------------- /scoring_framework/score_phase3_aipr.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2010 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #ifndef INCL_SCORE_PHASE3_AIPR_H 8 | #define INCL_SCORE_PHASE3_AIPR_H 9 | 10 | // In phase 3, the actual metrics are produced. 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace vidtk 19 | { 20 | 21 | struct track2track_phase2_aipr; 22 | 23 | struct overall_phase3_aipr 24 | { 25 | public: 26 | 27 | //number of computed tracks associated with multiple ground truths 28 | int num_identity_switch; 29 | //number of computed tracks associated with multiple ground truths divided by total number of computed tracks 30 | double identity_switch; 31 | 32 | //sum of all frame ranges of associated computed tracks over the sum of all frame ranges of ground truth tracks 33 | double track_completeness_factor; 34 | 35 | //Sum of the number of computed id's associated with a ground truth divided by the number of ground truths with associations 36 | double track_fragmentation; 37 | 38 | //sum of comp associated with a ground truth * the length of the ground truth all divided by the frame ranges of ground truths with associations 39 | double normalized_track_fragmentation; 40 | 41 | public: 42 | overall_phase3_aipr(): 43 | num_identity_switch(0), 44 | identity_switch(0), 45 | track_completeness_factor(0), 46 | track_fragmentation(0), 47 | normalized_track_fragmentation(0) 48 | {} 49 | void compute( const track2track_phase2_aipr& t2t ); 50 | }; 51 | 52 | } //namespace vidtk 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /scoring_framework/score_phase3_hadwav.cxx: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2010-2016 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #include "score_tracks_hadwav.h" 8 | 9 | #include 10 | 11 | #include 12 | static kwiver::vital::logger_handle_t main_logger( kwiver::vital::get_logger( __FILE__ ) ); 13 | 14 | using std::make_pair; 15 | using std::map; 16 | using std::ostringstream; 17 | 18 | using kwiver::track_oracle::track_field; 19 | using kwiver::track_oracle::track_handle_type; 20 | 21 | namespace kwiver { 22 | namespace kwant { 23 | 24 | per_track_phase3_hadwav 25 | overall_phase3_hadwav 26 | ::compute_per_track( p2it p, const track2track_phase2_hadwav& p2_results, bool seeking_across_truth ) 27 | { 28 | static scorable_track_type local_track_view; 29 | 30 | unsigned dominant_size = 0; 31 | track_handle_type dominant_index; 32 | for (unsigned i=0; isecond.size(); ++i) 33 | { 34 | track_handle_type this_index = p->second[i]; 35 | track2track_type key; 36 | // 37 | // TODO: make this semantically consistent 38 | // 39 | if ( ! seeking_across_truth) 40 | { 41 | key = make_pair( this_index, p->first ); 42 | } 43 | else 44 | { 45 | key = make_pair( p->first, this_index ); 46 | } 47 | map< track2track_type, track2track_scalars_hadwav >::const_iterator j = p2_results.t2t.find( key ); 48 | if ( j == p2_results.t2t.end() ) 49 | { 50 | LOG_ERROR( main_logger, "Logic error: phase 3 deduced key of " << key.first.row << ", " << key.second.row << " not in phase 2 results?"); 51 | throw("Whoops"); 52 | } 53 | unsigned this_size = j->second.computed_frames_on_target; 54 | if (( i == 0 ) || ( this_size > dominant_size )) 55 | { 56 | dominant_size = this_size; 57 | dominant_index = this_index; 58 | } 59 | } 60 | 61 | per_track_phase3_hadwav stats; 62 | stats.continuity = p->second.size(); 63 | unsigned lifetime = local_track_view( p->first ).frames_in_aoi(); 64 | // LOG_INFO( main_logger, "dominant size, lifetime: " << dominant_size << "," << lifetime << ""); 65 | stats.purity = (lifetime == 0) ? 0.0 : 1.0*dominant_size / lifetime; 66 | stats.dominant_track_id = local_track_view( dominant_index ).external_id(); 67 | stats.dominant_track_size = dominant_size; 68 | stats.dominated_track_lifetime = lifetime; 69 | // MITRE definition is "over the life of the given {track,target}", implying 70 | // it's okay to cap this at 1.0 71 | if (stats.purity > 1.0) stats.purity = 1.0; 72 | if ( this->verbose ) 73 | { 74 | LOG_INFO( main_logger, "C/P of "); 75 | if (seeking_across_truth) LOG_INFO( main_logger, "target "); else LOG_INFO( main_logger, "track "); 76 | unsigned int domid = local_track_view( dominant_index ).external_id(); 77 | LOG_INFO( main_logger, local_track_view( p->first ).external_id() 78 | << " (dominated by " << domid << "; size " << dominant_size << "; lifetime " << lifetime << ")" 79 | << " : cont " << stats.continuity 80 | << " purity " << stats.purity << ""); 81 | } 82 | 83 | return stats; 84 | } 85 | 86 | 87 | 88 | void 89 | overall_phase3_hadwav 90 | ::compute( const track2track_phase2_hadwav& t2t ) 91 | { 92 | // compute MITRE's "track" metrics (i.e. computed tracks) 93 | map numCTOnFrame; 94 | unsigned purity_counter = 0; 95 | unsigned continuity_counter = 0; 96 | for ( p2it iter = t2t.c2t.begin(); iter != t2t.c2t.end(); ++iter ) 97 | { 98 | per_track_phase3_hadwav stats = this->compute_per_track( iter, t2t, /* looping over truth = */ false ); 99 | this->mitre_tracks[ iter->first ] = stats; 100 | if( stats.continuity != 0 ) 101 | { 102 | this->avg_track_continuity += stats.continuity; 103 | continuity_counter++; 104 | } 105 | if( stats.purity != 0 ) 106 | { 107 | this->avg_track_purity += stats.purity; 108 | purity_counter++; 109 | } 110 | } 111 | if ( t2t.n_computed_tracks > 0 ) 112 | { 113 | if (continuity_counter > 0) 114 | { 115 | this->avg_track_continuity /= static_cast( continuity_counter ); 116 | } 117 | if (purity_counter > 0) 118 | { 119 | this->avg_track_purity /= static_cast( purity_counter ); 120 | } 121 | } 122 | LOG_INFO( main_logger, "CP (track) avg over " << t2t.n_computed_tracks << ""); 123 | 124 | // compute MITRE's "target" metrics (i.e. ground truth ) 125 | map numGTOnFrame; 126 | for ( p2it iter = t2t.t2c.begin(); iter != t2t.t2c.end(); ++iter ) 127 | { 128 | per_track_phase3_hadwav stats = this->compute_per_track( iter, t2t, /* looping over truth = */ true ); 129 | this->mitre_targets[ iter->first ] = stats; 130 | this->avg_target_continuity += stats.continuity; 131 | this->avg_target_purity += stats.purity; 132 | } 133 | if ( t2t.n_true_tracks > 0 ) 134 | { 135 | this->avg_target_continuity /= (1.0 * t2t.n_true_tracks ); 136 | this->avg_target_purity /= (1.0 * t2t.n_true_tracks ); 137 | } 138 | LOG_INFO( main_logger, "CP (target) avg over " << t2t.n_computed_tracks << ""); 139 | 140 | track_field< kwiver::track_oracle::dt::utility::state_flags > state_flags; 141 | 142 | // compute overall Pd/FA (or FAR) 143 | unsigned n_hit_true_tracks = 0; 144 | for (p2it iter = t2t.t2c.begin(); iter != t2t.t2c.end(); ++iter ) 145 | { 146 | { 147 | ostringstream oss; 148 | oss << iter->second.size(); 149 | state_flags( iter->first.row).set_flag( "n-matched", oss.str() ); 150 | } 151 | 152 | if ( ! iter->second.empty() ) 153 | { 154 | ++n_hit_true_tracks; 155 | } 156 | 157 | } 158 | unsigned n_unassigned_computed_tracks = 0; 159 | LOG_INFO( main_logger, "t2t.c2t is " << t2t.c2t.size() << ""); 160 | for (p2it iter = t2t.c2t.begin(); iter != t2t.c2t.end(); ++iter ) 161 | { 162 | { 163 | ostringstream oss; 164 | oss << iter->second.size(); 165 | state_flags( iter->first.row).set_flag( "n-matched", oss.str() ); 166 | } 167 | if ( this->verbose ) 168 | { 169 | LOG_INFO( main_logger, "FAR: computed " << iter->first.row << " has " << iter->second.size() << ""); 170 | } 171 | if ( iter->second.empty() ) ++n_unassigned_computed_tracks; 172 | } 173 | LOG_INFO( main_logger, "trackPD: " << n_hit_true_tracks << " / " << t2t.n_true_tracks << ""); 174 | this->trackPd = (t2t.t2c.empty()) ? 0.0 : 1.0 * n_hit_true_tracks / t2t.n_true_tracks; 175 | LOG_INFO( main_logger, "trackFA: " << n_unassigned_computed_tracks << ""); 176 | this->trackFA = (t2t.c2t.empty()) ? 0.0 : 1.0 * n_unassigned_computed_tracks; 177 | } 178 | 179 | const map< track_handle_type, per_track_phase3_hadwav >& 180 | overall_phase3_hadwav 181 | ::get_mitre_track_stats() const 182 | { 183 | return this->mitre_tracks; 184 | } 185 | 186 | const map< track_handle_type, per_track_phase3_hadwav >& 187 | overall_phase3_hadwav 188 | ::get_mitre_target_stats() const 189 | { 190 | return this->mitre_targets; 191 | } 192 | 193 | } // ...kwant 194 | } // ...kwiver 195 | -------------------------------------------------------------------------------- /scoring_framework/score_phase3_hadwav.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2010-2016 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #ifndef INCL_SCORE_PHASE3_HADWAV_H 8 | #define INCL_SCORE_PHASE3_HADWAV_H 9 | 10 | // In phase 3, the actual metrics are produced. 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace kwiver { 20 | namespace kwant { 21 | 22 | namespace kwto = ::kwiver::track_oracle; 23 | 24 | struct track2track_phase2_hadwav; 25 | 26 | struct SCORE_TRACKS_HADWAV_EXPORT per_track_phase3_hadwav 27 | { 28 | public: 29 | double continuity; 30 | double purity; 31 | unsigned dominant_track_id; 32 | unsigned dominant_track_size; 33 | unsigned dominated_track_lifetime; 34 | per_track_phase3_hadwav() 35 | : continuity(0.0), purity(0.0), dominant_track_id(0), dominant_track_size(0), dominated_track_lifetime(0) 36 | {} 37 | }; 38 | 39 | struct SCORE_TRACKS_HADWAV_EXPORT overall_phase3_hadwav 40 | { 41 | private: 42 | typedef std::map< kwto::track_handle_type, kwto::track_handle_list_type >::const_iterator p2it; 43 | 44 | std::map< kwto::track_handle_type, per_track_phase3_hadwav > mitre_tracks; 45 | std::map< kwto::track_handle_type, per_track_phase3_hadwav > mitre_targets; 46 | 47 | public: 48 | double avg_track_continuity; 49 | double avg_track_purity; 50 | double avg_target_continuity; 51 | double avg_target_purity; 52 | double trackPd; 53 | double trackFA; 54 | bool verbose; 55 | 56 | overall_phase3_hadwav(): 57 | avg_track_continuity(0.0), 58 | avg_track_purity(0.0), 59 | avg_target_continuity(0.0), 60 | avg_target_purity(0.0), 61 | trackPd(0.0), 62 | trackFA(0.0), 63 | verbose(false) 64 | { 65 | } 66 | 67 | per_track_phase3_hadwav compute_per_track( p2it p, const track2track_phase2_hadwav& t2t, bool seeking_across_truth ); 68 | void compute( const track2track_phase2_hadwav& t2t ); 69 | const std::map< kwto::track_handle_type, per_track_phase3_hadwav >& get_mitre_track_stats() const; 70 | const std::map< kwto::track_handle_type, per_track_phase3_hadwav >& get_mitre_target_stats() const; 71 | }; 72 | 73 | } // ...kwant 74 | } // ...kwiver 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /scoring_framework/score_tracks_aipr.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2010 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #ifndef INCL_SCORE_TRACKS_AIPR_H 8 | #define INCL_SCORE_TRACKS_AIPR_H 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #endif 15 | 16 | -------------------------------------------------------------------------------- /scoring_framework/score_tracks_hadwav.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2010 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #ifndef INCL_SCORE_TRACKS_HADWAV_H 8 | #define INCL_SCORE_TRACKS_HADWAV_H 9 | 10 | #include "score_phase1.h" 11 | #include "score_phase2_hadwav.h" 12 | #include "score_phase3_hadwav.h" 13 | 14 | #endif 15 | 16 | -------------------------------------------------------------------------------- /scoring_framework/score_tracks_loader.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2011-2016 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #ifndef INCL_SCORE_TRACKS_LOADER_H 8 | #define INCL_SCORE_TRACKS_LOADER_H 9 | 10 | /// There are some constraints that need to be satisfied when 11 | /// loading tracks for scoring, mostly dealing with ensuring that 12 | /// each frame has a timestamp. This API provides a uniform set of 13 | /// vul_args for the client to initialize and the ability to load 14 | /// single tracks or sets of tracks, estimate timestamps from 15 | /// computed tracks back to ground truth tracks, etc. 16 | /// 17 | /// The client should create one instance of this type before calling 18 | /// vul_arg_parse, and then call process() after calling vul_arg_parse, 19 | /// and if process() returns true, then the ground-truth and computed track 20 | /// lists are guaranteed to have timestamps. 21 | /// 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | namespace kwiver { 33 | namespace kwant { 34 | 35 | struct SCORE_TRACKS_LOADER_EXPORT input_args_type 36 | { 37 | // both of these must be set 38 | // each one of: 39 | // - a filename 40 | // - a string '@filelist', filelist is a file listing the track files 41 | // - a virat scenario file 42 | 43 | vul_arg< std::string > computed_tracks_fn; 44 | vul_arg< std::string > truth_tracks_fn; 45 | 46 | // if set, will override path components in file when loading 47 | // Example: if --truth-path=/data/foo/bar, then: 48 | // 49 | // --truth-tracks sample.kw18 => /data/foo/bar/sample.kw18 50 | // --truth-tracks /tmp/sample.kw18 => /data/foo/bar/sample.kw18 51 | // 52 | // ...ditto for all filenames in a '@filelist' set or a scenario 53 | 54 | vul_arg< std::string > computed_path; 55 | vul_arg< std::string > truth_path; 56 | 57 | // XGTF does not have timestamps. Since we align on timestamps, 58 | // when xgtf files are used, we need to add timestamps by starting 59 | // with a timestamp for frame 0 and linearly stamping subsequent frames 60 | // using the fps. 61 | 62 | vul_arg< double > computed_fps; 63 | vul_arg< double > truth_fps; 64 | 65 | // So where does the frame 0 come from? 66 | // (1) it may be explicitly listed in an xgtf_timestamps file 67 | // (2) it may be explicitly stated on the command line 68 | // (3) it may be estimated from the computed tracks 69 | // (4) it may be fabricated if we know a priori the XGTF aligns with the computed track 70 | // 71 | // (1) must be used when there are multiple XGTF files. 72 | // 73 | // (2) and (3) can only be used for single files. 74 | // 75 | // (4) is mutually exclusive from (1), (2), or (3). If --paired-gtct is used, 76 | // then we require a one-to-one pairing between computed and ground truth, 77 | // and that this pairing is maintained in the order files are listed in the @filelist 78 | // files. We assume each (gt, ct) pair is internally aligned on timestamps and 79 | // fabricate the timestamps by adding an offset to each timestamp in (gt, ct); this 80 | // offset is the highest timestamp in the previous pair plus some padding. This will 81 | // only break any attempts to rederive timestamps based on estimating a t0 timestamp 82 | // based on the frame rate and a timestamp for a non-t0 frame, which is why (4) 83 | // is exclusive from (3). 84 | 85 | vul_arg< std::string > xgtf_timestamps_fn; 86 | vul_arg< std::string > xgtf_base_ts; 87 | 88 | vul_arg< bool > paired_gtct; 89 | vul_arg< bool > promote_pvmoving; 90 | vul_arg< std::string > qid; 91 | 92 | vul_arg< std::string > track_style_filter; 93 | vul_arg< bool > ts_from_fn; 94 | vul_arg< std::string > apix_debug_fn; 95 | 96 | // this argument is a string "M:N", M and N both integers >= 0 (e.g. "4:9".) 97 | // M and N are used to filter truth and computed tracks, respectively; only tracks 98 | // with at least that many states are kept. Defaults to 0:0 (all tracks.) 99 | 100 | vul_arg< std::string > track_length_filter; 101 | 102 | // Define a time window in either frames or timestamp_usecs; only keep tracks 103 | // wholly within this window. 104 | vul_arg< std::string > time_window; 105 | 106 | // Some file formats, e.g. CSV, do not have a fixed source for the latitude 107 | // and longitude information required for setting MGRS data for radial overlap 108 | // computation. Allow the user to specify these fields as a colon-separated 109 | // pair of strings, e.g. "longitude:latitude" or "world_y:world_x" 110 | vul_arg< std::string > mgrs_lon_lat_fields; 111 | 112 | // If set, assume kw18 will have a 19th column read into the relevancy slot 113 | // for each frame. (Computed tracks only.) 114 | vul_arg< bool > kw19_hack; 115 | 116 | // When scoring detections, we don't have a separate detection-only 117 | // data structure; just break them up into single-frame tracks. 118 | vul_arg< bool > detection_mode; 119 | 120 | // this flag is not set directly by an input_args command line variable, 121 | // but instead is set by the main program via other variables (such as 122 | // e.g. --radial-overlap). When set, process() tries to compute MGRS geolocation 123 | // data (currently available only from APIX or KW18-with-world-coords.) If 124 | // it fails to do so, process() returns false. 125 | 126 | bool compute_mgrs_data; 127 | 128 | 129 | input_args_type() 130 | : computed_tracks_fn( "--computed-tracks", "Computed tracks file, or @filelist reads list of files" ), 131 | truth_tracks_fn( "--truth-tracks", "Truth tracks files, scenario, or @filelist reads list of files" ), 132 | computed_path( "--computed-path", "All computed files will be loaded from this directory, overriding other paths", "" ), 133 | truth_path( "--truth-path", "All truth files will be loaded from this directory, overriding other paths", "" ), 134 | computed_fps( "--computed-fps", "Computed tracks frames-per-second", 29.97 / 3 ), 135 | truth_fps( "--truth-fps", "Truth tracks frames-per-second", 29.97 ), 136 | xgtf_timestamps_fn( "--xgtf-ts-file", "File of XGTF timestamps (format: xgtf-basename, fps, timestamp)" ), 137 | xgtf_base_ts( "--xgtf-base-ts", "Base xgtf timestamp (usecs), unset to probe computed tracks, or 'probe' to probe, report, and exit" ), 138 | paired_gtct( "--paired-gtct", "Require paired gt/ct and fabricate timestamps to separate the sets (excludes xgtf-ts options)" ), 139 | promote_pvmoving( "--promote-pvmoving", "Set if XGTF activities should be promoted to PVMoving" ), 140 | qid( "--qid", "The query ID to read computed tracks from", "" ), 141 | track_style_filter( "--track-style", "For KWXML, keep only tracks of the named style (both ground truth and computed" ), 142 | ts_from_fn( "--fn2ts", "For any file format which does not define timestamps, convert frame number to timestamps (in seconds)" ), 143 | apix_debug_fn( "--apix-log", "For APIX tracks, log tracks as read to this file", "" ), 144 | track_length_filter("--track-length-filter", "Only keep (truth:computed) tracks with at least this many states (default: all tracks)", "0:0" ), 145 | time_window( "--time-window", "Only select tracks within a time window; 'help' for more details" ), 146 | mgrs_lon_lat_fields("--mgrs-ll-fields", "For e.g. CSV files, pull longitude / latitude from these fields", "world_x:world_y" ), 147 | kw19_hack( "--kw19-hack", "If set, read confidence / probability / etc. from 19th column (computed only)" ), 148 | detection_mode( "--detection-mode", "Convert truth and computed tracks to single-frame tracks to score as detections" ), 149 | compute_mgrs_data( false ) 150 | {} 151 | 152 | 153 | bool process( kwiver::track_oracle::track_handle_list_type& computed_tracks, 154 | kwiver::track_oracle::track_handle_list_type& truth_tracks ); 155 | 156 | }; 157 | 158 | } // ...kwant 159 | } // ...kwiver 160 | 161 | #endif 162 | -------------------------------------------------------------------------------- /scoring_framework/test_phase1.cxx: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2010 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | 19 | using std::istringstream; 20 | using std::string; 21 | 22 | using namespace vidtk; 23 | 24 | const string track_1 = 25 | "1 4 1 5 5 0 1 5 5 3 3 7 7 16 5 5 0 10\n"; 26 | 27 | const string track_2 = 28 | "1 4 2 5 6 0 1 5 6 3 4 7 8 16 5 6 0 11\n" 29 | "1 4 3 5 7 0 1 5 7 3 5 7 9 16 5 7 0 12\n" 30 | "1 4 4 5 8 0 0 5 8 3 6 7 10 16 5 8 0 13\n"; 31 | 32 | 33 | int 34 | test_t1_t1() 35 | { 36 | testlib_test_start( "testOneFrameToSelf" ); 37 | istringstream iss( track_1 ); 38 | track_list_type t; 39 | track_kw18_reader::read( iss, t ); 40 | 41 | TEST( "track_1 has one track", (t.size() == 1) ) 42 | track2track_phase1 p1; 43 | p1.compute( t, t ); 44 | TEST( "t2t has one entry", (p1.t2t.size() == 1) ); 45 | return testlib_test_summary(); 46 | } 47 | -------------------------------------------------------------------------------- /scoring_framework/time_window_filter.cxx: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2013-2016 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #include "time_window_filter.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | static kwiver::vital::logger_handle_t main_logger( kwiver::vital::get_logger( __FILE__ ) ); 20 | 21 | using std::istringstream; 22 | using std::numeric_limits; 23 | using std::ostringstream; 24 | using std::runtime_error; 25 | using std::string; 26 | using std::swap; 27 | 28 | using kwiver::track_oracle::track_handle_type; 29 | using kwiver::track_oracle::frame_handle_list_type; 30 | using kwiver::track_oracle::oracle_entry_handle_type; 31 | using kwiver::track_oracle::track_oracle_core; 32 | using kwiver::track_oracle::track_field; 33 | 34 | 35 | namespace { // anon 36 | 37 | unsigned long long 38 | parse_window_bound( const string& s, 39 | const string& unit_code, 40 | unsigned long long default_value ) 41 | { 42 | if (s.empty()) return default_value; 43 | 44 | unsigned long long ret; 45 | istringstream iss( s ); 46 | if ( (unit_code == "f") || // frames 47 | (unit_code == "t") ) // usecs 48 | { 49 | if ( ! ( iss >> ret )) 50 | { 51 | throw runtime_error( "Couldn't parse time window bound from '"+s+"' (unit code " + unit_code +") ?" ); 52 | } 53 | } 54 | else if (unit_code == "T") // secs 55 | { 56 | double tmp; 57 | if ( ! ( iss >> tmp )) 58 | { 59 | throw runtime_error( "Couldn't parse time window bound from '"+s+"' (unit code " + unit_code +") ?" ); 60 | } 61 | ret = static_cast< unsigned long long >( tmp * 1.0e6 ); 62 | } 63 | else 64 | { 65 | throw runtime_error( "Bad time window unit code '"+s+"'; expected one of [tfF]" ); 66 | } 67 | 68 | return ret; 69 | } 70 | 71 | } // anon 72 | 73 | namespace kwiver { 74 | namespace kwant { 75 | 76 | string 77 | time_window_filter 78 | ::help_text() 79 | { 80 | return string( 81 | "A time window specifies that only tracks within the given time\n" 82 | "bound are scored. This time window applies to both computed and\n" 83 | "ground-truth tracks.\n" 84 | "\n" 85 | "Windows may be either 'inclusive' or 'exclusive'. An inclusive window\n" 86 | "will accept any track with at least one state within the window. An exclusive\n" 87 | "window will only accept tracks whose every state is within the window.\n" 88 | "\n" 89 | "Time window filtering occurs BEFORE any track-minimum-length filters are\n" 90 | "applied.\n" 91 | "\n" 92 | "The format is: '(flag)(field)[min]:[max]' ; flag, field and the colon are mandatory;\n" 93 | "min and max are optional; if left out, lowest and highest values in the\n" 94 | "dataset are used.\n" 95 | "\n" 96 | "Flag values are:\n" 97 | "'i' for inclusive\n" 98 | "'x' for exclusive\n" 99 | "\n" 100 | "Field values are:\n" 101 | "'f' for frame numbers\n" 102 | "'t' / 'T' for timestamps in usecs / secs\n" 103 | "\n" 104 | "Examples:\n" 105 | "'if100:300' keeps all tracks with states within frames 100-300.\n" 106 | "'xt:6150002' keeps all tracks wholly contained from the beginning to 6150002 *usecs*.\n" 107 | "'iT500:' keeps all tracks at least partially contained from 500 *seconds* to the end.\n" 108 | "'it:' keeps all tracks, and is identical to 'iT:' and 'if:' (and exclusive versions.)\n" ); 109 | } 110 | 111 | time_window_filter 112 | ::time_window_filter() 113 | : valid(false) 114 | { 115 | } 116 | 117 | bool 118 | time_window_filter 119 | ::set_from_string( const string& s ) 120 | { 121 | vul_reg_exp re( "^([ix])([ftT])([0-9\\.]*):([0-9\\.]*)$" ); 122 | 123 | this->valid = false; 124 | if (re.find( s )) 125 | { 126 | this->inclusive = (re.match(1) == "i" ); 127 | this->units_are_frames = (re.match(2) == "f"); 128 | this->min = parse_window_bound( re.match(3), re.match(2), numeric_limits< unsigned long long >::min() ); 129 | this->max = parse_window_bound( re.match(4), re.match(2), numeric_limits< unsigned long long >::max() ); 130 | if (this->min > this->max) 131 | { 132 | LOG_INFO( main_logger, "time_window_filter min / max " << this->min << " / " << this->max << 133 | " out of order; swapping" ); 134 | swap( this->min, this->max ); 135 | } 136 | this->valid = true; 137 | } 138 | else 139 | { 140 | LOG_ERROR( main_logger, "Couldn't set time window filter from '" << s << "'" ); 141 | } 142 | return this->valid; 143 | } 144 | 145 | bool 146 | time_window_filter 147 | ::track_passes_filter( const track_handle_type& t ) const 148 | { 149 | track_field< unsigned long long > ts_usecs( "timestamp_usecs" ); 150 | track_field< unsigned > ts_frame( "frame_number" ); 151 | 152 | frame_handle_list_type frames = track_oracle_core::get_frames( t ); 153 | for (size_t i=0; iunits_are_frames) 160 | { 161 | if ( ! ts_frame.exists( row )) 162 | { 163 | LOG_WARN( main_logger, "Time window is on frames but track does not have frame numbers? Rejecting" ); 164 | return false; 165 | } 166 | unsigned frame_number = ts_frame( row ); 167 | in_window = (this->min <= frame_number) && (frame_number <= this->max); 168 | } 169 | else 170 | { 171 | if ( ! ts_usecs.exists( row )) 172 | { 173 | LOG_WARN( main_logger, "Time window is on timestamps but track does not have timestamps? Rejecting" ); 174 | return false; 175 | } 176 | unsigned long long ts = ts_usecs( row ); 177 | in_window = (this->min <= ts) && (ts <= this->max); 178 | } 179 | if ( ( ! in_window ) && ( ! this->inclusive )) 180 | { 181 | // we're outside the window, and the window is exclusive; return false 182 | return false; 183 | } 184 | if ( in_window && this->inclusive ) 185 | { 186 | // we're inside the window, and the window is inclusive-- return true 187 | return true; 188 | } 189 | } // each frame 190 | 191 | if (this->inclusive) 192 | { 193 | // if the window is inclusive, and we get here, then every frame was outside the window-- return false 194 | return false; 195 | } 196 | else 197 | { 198 | // if the window is exclusive, and we got here, then every frame is inside the window-- return true; 199 | return true; 200 | } 201 | } 202 | 203 | bool 204 | time_window_filter 205 | ::is_valid() const 206 | { 207 | return this->valid; 208 | } 209 | 210 | string 211 | time_window_filter_factory 212 | ::help_text() 213 | { 214 | return "The special values 'G', 'g', 'C', 'c', 'M', and 'm' are also accepted; these autocompute to\n" 215 | "the timestamp bounds of (G)roundtruth or (C)omputed. 'G' and 'C' produce\n" 216 | "exclusive windows (every state must be in the window); 'g' and 'c' produce\n" 217 | "inclusive window (any state may be in the window).\n" 218 | "'M' and 'm' produce exclusive and inclusive windows, respectively, based on the\n" 219 | "maximum lowest and minimum highest timestamp between the computed and ground-truth sets.\n"; 220 | } 221 | 222 | bool 223 | time_window_filter_factory 224 | ::code_is_special( const string& code ) 225 | { 226 | return 227 | (code == "G") || 228 | (code == "C") || 229 | (code == "g") || 230 | (code == "c") || 231 | (code == "M") || 232 | (code == "m"); 233 | } 234 | 235 | time_window_filter 236 | time_window_filter_factory 237 | ::from_stats( const string& code, 238 | const timestamp_utilities::track_timestamp_stats_type& tstats, 239 | const timestamp_utilities::track_timestamp_stats_type& cstats ) 240 | { 241 | ostringstream oss; 242 | char flag; 243 | if ((code == "G") || (code == "g")) 244 | { 245 | if (code == "G") 246 | { 247 | LOG_INFO( main_logger, "Time window filter will be EXCLUSIVE and derived from ground-truth..." ); 248 | flag = 'x'; 249 | } 250 | else 251 | { 252 | LOG_INFO( main_logger, "Time window filter will be INCLUSIVE and derived from ground-truth..." ); 253 | flag = 'i'; 254 | } 255 | oss << flag << "t" << tstats.minmax_ts.first << ":" << tstats.minmax_ts.second; 256 | LOG_INFO( main_logger, "Advisory time window frames " << tstats.minmax_fn.first << " : " << tstats.minmax_fn.second ); 257 | } 258 | else if ((code == "C") || (code == "c" )) 259 | { 260 | if (code == "C") 261 | { 262 | LOG_INFO( main_logger, "Time window filter will be EXCLUSIVE and derived from computed..." ); 263 | flag = 'x'; 264 | } 265 | else 266 | { 267 | LOG_INFO( main_logger, "Time window filter will be INCLUSIVE and derived from computed..." ); 268 | flag = 'i'; 269 | } 270 | oss << flag << "t" << cstats.minmax_ts.first << ":" << cstats.minmax_ts.second; 271 | LOG_INFO( main_logger, "Advisory time window frames " << cstats.minmax_fn.first << " : " << cstats.minmax_fn.second ); 272 | } 273 | else if ((code == "M") || (code == "m" )) 274 | { 275 | if (code == "M") 276 | { 277 | LOG_INFO( main_logger, "Time window filter will be EXCLUSIVE and derived from the minimum window of both track sets..." ); 278 | flag = 'x'; 279 | } 280 | else 281 | { 282 | LOG_INFO( main_logger, "Time window filter will be INCLUSIVE and derived from the minimum window of both track sets..." ); 283 | flag = 'i'; 284 | } 285 | ts_type lower_bound, upper_bound; 286 | unsigned lower_bound_frame, upper_bound_frame; 287 | if (tstats.minmax_ts.first < cstats.minmax_ts.first ) 288 | { 289 | lower_bound = cstats.minmax_ts.first; 290 | lower_bound_frame = cstats.minmax_fn.first; 291 | LOG_INFO( main_logger, "Using lower bound from computed; " << tstats.minmax_ts.first - lower_bound << " usecs (" 292 | << tstats.minmax_fn.first - lower_bound_frame << " frames) ahead of ground-truth "); 293 | } 294 | else 295 | { 296 | lower_bound = tstats.minmax_ts.first; 297 | lower_bound_frame = tstats.minmax_fn.first; 298 | LOG_INFO( main_logger, "Using lower bound from ground-truth; " << cstats.minmax_ts.first - lower_bound << " usecs (" 299 | << cstats.minmax_fn.first - lower_bound_frame << " frames) ahead of computed "); 300 | } 301 | if (tstats.minmax_ts.second < cstats.minmax_ts.second ) 302 | { 303 | upper_bound = tstats.minmax_ts.second; 304 | upper_bound_frame = tstats.minmax_fn.second; 305 | LOG_INFO( main_logger, "Using upper bound from ground-truth; " << cstats.minmax_ts.second - upper_bound << " usecs (" 306 | << cstats.minmax_fn.second - upper_bound_frame << " frames) below computed "); 307 | } 308 | else 309 | { 310 | upper_bound = cstats.minmax_ts.second; 311 | upper_bound_frame = cstats.minmax_fn.second; 312 | LOG_INFO( main_logger, "Using upper bound from computed..." << tstats.minmax_ts.second - upper_bound << " usecs (" 313 | << tstats.minmax_ts.second - upper_bound_frame << " frames) below ground-truth " ); 314 | } 315 | if (upper_bound < lower_bound ) 316 | { 317 | LOG_ERROR( main_logger, "Upper bound less than upper bound... are computed and ground-truth from the same run?" ); 318 | return time_window_filter(); 319 | } 320 | oss << flag << "t" << lower_bound << ":" << upper_bound; 321 | LOG_INFO( main_logger, "Advisory time window frames " << lower_bound_frame << " : " << upper_bound_frame ); 322 | } 323 | else 324 | { 325 | LOG_ERROR( main_logger, "time_window_filter_factory called with invalid code '" << code << "'" ); 326 | return time_window_filter(); 327 | } 328 | time_window_filter twf; 329 | bool rc = twf.set_from_string( oss.str() ); 330 | if ( ! rc ) throw runtime_error( "Couldn't set twf from code '" + code + "': '" + oss.str() + "'" ); 331 | LOG_INFO( main_logger, "Set to '" << oss.str() << "'" ); 332 | return twf; 333 | } 334 | 335 | } // ...kwant 336 | } // ...kwiver 337 | -------------------------------------------------------------------------------- /scoring_framework/time_window_filter.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2013-2016 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #ifndef INCL_TIME_WINDOW_FILTER_H 8 | #define INCL_TIME_WINDOW_FILTER_H 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace kwiver { 18 | namespace kwant { 19 | 20 | class SCORE_CORE_EXPORT time_window_filter 21 | { 22 | public: 23 | 24 | time_window_filter(); 25 | bool set_from_string( const std::string& s ); 26 | bool track_passes_filter( const kwiver::track_oracle::track_handle_type& t ) const; 27 | bool is_valid() const; 28 | static std::string help_text(); 29 | 30 | private: 31 | bool units_are_frames; // true if frames, false if timestamp_usecs 32 | bool inclusive; // true if inclusive, false if exclusive 33 | bool valid; // true if constructed 34 | 35 | // relying on the fact that unsigned long long can hold 36 | // both frames and timestamp_usecs 37 | unsigned long long min; 38 | unsigned long long max; 39 | }; 40 | 41 | struct SCORE_CORE_EXPORT time_window_filter_factory 42 | { 43 | static std::string help_text(); 44 | static bool code_is_special( const std::string& code ); 45 | static time_window_filter from_stats( const std::string& code, 46 | const timestamp_utilities::track_timestamp_stats_type& tstats, 47 | const timestamp_utilities::track_timestamp_stats_type& cstats ); 48 | }; 49 | 50 | 51 | } // ...kwant 52 | } // ...kwiver 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /scoring_framework/timestamp_utilities.cxx: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2013-2016 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #include "timestamp_utilities.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | 21 | #include 22 | static kwiver::vital::logger_handle_t main_logger( kwiver::vital::get_logger( __FILE__ ) ); 23 | 24 | using std::getline; 25 | using std::ifstream; 26 | using std::istringstream; 27 | using std::make_pair; 28 | using std::max; 29 | using std::min; 30 | using std::ostream; 31 | using std::pair; 32 | using std::runtime_error; 33 | using std::string; 34 | 35 | using kwiver::track_oracle::track_handle_type; 36 | using kwiver::track_oracle::track_handle_list_type; 37 | using kwiver::track_oracle::frame_handle_type; 38 | using kwiver::track_oracle::frame_handle_list_type; 39 | using kwiver::track_oracle::track_oracle_core; 40 | using kwiver::track_oracle::oracle_entry_handle_type; 41 | 42 | namespace kwiver { 43 | 44 | namespace kwant { 45 | 46 | namespace timestamp_utilities { 47 | 48 | track_timestamp_stats_type 49 | ::track_timestamp_stats_type() 50 | : timestamp_usecs( "timestamp_usecs" ), 51 | frame_number( "frame_number" ) 52 | { 53 | this->reset(); 54 | } 55 | 56 | track_timestamp_stats_type 57 | ::track_timestamp_stats_type( const track_timestamp_stats_type& other ) 58 | : timestamp_usecs( "timestamp_usecs" ), 59 | frame_number( "frame_number" ), 60 | is_empty( other.is_empty ), 61 | has_frame_numbers( other.has_frame_numbers ), 62 | has_timestamps( other.has_timestamps ), 63 | all_have_frame_numbers( other.all_have_frame_numbers ), 64 | all_have_timestamps( other.all_have_timestamps ), 65 | minmax_fn( other.minmax_fn ), 66 | minmax_ts( other.minmax_ts ), 67 | ts_fn_count( other.ts_fn_count ) 68 | { 69 | } 70 | 71 | 72 | void 73 | track_timestamp_stats_type 74 | ::reset() 75 | { 76 | this->is_empty = true; 77 | this->has_timestamps = false; 78 | this->has_frame_numbers = false; 79 | this->all_have_timestamps = true; 80 | this->all_have_frame_numbers = true; 81 | this->ts_fn_count = make_pair( 0, 0 ); 82 | } 83 | 84 | track_timestamp_stats_type 85 | ::track_timestamp_stats_type( const track_handle_list_type& tracks ) 86 | : timestamp_usecs( "timestamp_usecs" ), 87 | frame_number( "frame_number" ) 88 | { 89 | this->reset(); 90 | this->set_from_tracks( tracks ); 91 | } 92 | 93 | track_timestamp_stats_type 94 | ::track_timestamp_stats_type( const track_handle_type& t ) 95 | : timestamp_usecs( "timestamp_usecs" ), 96 | frame_number( "frame_number" ) 97 | { 98 | this->reset(); 99 | this->set_from_track( t ); 100 | } 101 | 102 | track_timestamp_stats_type 103 | ::track_timestamp_stats_type( const frame_handle_list_type& f ) 104 | : timestamp_usecs( "timestamp_usecs" ), 105 | frame_number( "frame_number" ) 106 | { 107 | this->reset(); 108 | this->set_from_frames( f ); 109 | } 110 | 111 | void 112 | track_timestamp_stats_type 113 | ::combine_with_other( const track_timestamp_stats_type& other ) 114 | { 115 | // quick exit if other is empty 116 | if (other.is_empty) 117 | { 118 | return; 119 | } 120 | 121 | // quick copy if we're empty 122 | if (this->is_empty) 123 | { 124 | this->is_empty = other.is_empty; 125 | this->has_timestamps = other.has_timestamps; 126 | this->has_frame_numbers = other.has_frame_numbers; 127 | this->all_have_timestamps = other.all_have_timestamps; 128 | this->all_have_frame_numbers = other.all_have_frame_numbers; 129 | this->minmax_fn = other.minmax_fn; 130 | this->minmax_ts = other.minmax_ts; 131 | this->ts_fn_count = other.ts_fn_count; 132 | 133 | return; 134 | } 135 | 136 | // 137 | // both this and other are not empty 138 | // 139 | 140 | if (other.has_timestamps) 141 | { 142 | if ( ! this->has_timestamps ) 143 | { 144 | this->minmax_ts = other.minmax_ts; 145 | this->has_timestamps = true; 146 | } 147 | else 148 | { 149 | this->minmax_ts.first = min( this->minmax_ts.first, other.minmax_ts.first ); 150 | this->minmax_ts.second = max( this->minmax_ts.second, other.minmax_ts.second ); 151 | } 152 | } 153 | 154 | if (other.has_frame_numbers) 155 | { 156 | if ( ! this->has_frame_numbers ) 157 | { 158 | this->minmax_fn = other.minmax_fn; 159 | this->has_frame_numbers = true; 160 | } 161 | else 162 | { 163 | this->minmax_fn.first = min( this->minmax_fn.first, other.minmax_fn.first ); 164 | this->minmax_fn.second = max( this->minmax_fn.second, other.minmax_fn.second ); 165 | } 166 | } 167 | 168 | this->all_have_timestamps = this->all_have_timestamps && other.all_have_timestamps; 169 | this->all_have_frame_numbers = this->all_have_frame_numbers && other.all_have_frame_numbers; 170 | } 171 | 172 | void 173 | track_timestamp_stats_type 174 | ::set_from_tracks( const track_handle_list_type& tracks ) 175 | { 176 | for (size_t i=0; iset_from_track( tracks[i] ); 179 | } 180 | } 181 | 182 | void 183 | track_timestamp_stats_type 184 | ::set_from_track( const track_handle_type& t ) 185 | { 186 | this->set_from_frames( track_oracle_core::get_frames( t )); 187 | } 188 | 189 | void 190 | track_timestamp_stats_type 191 | ::set_from_frames( const frame_handle_list_type& frames ) 192 | { 193 | for (size_t i=0; iupdate_from_frame( frames[i] ); 196 | } 197 | } 198 | 199 | void 200 | track_timestamp_stats_type 201 | ::update_from_frame( const frame_handle_type& f ) 202 | { 203 | const oracle_entry_handle_type& row = f.row; 204 | this->is_empty = false; 205 | 206 | pair< bool, ts_type > ts_probe = this->timestamp_usecs.get( row ); 207 | if ( ts_probe.first ) 208 | { 209 | ++this->ts_fn_count.first; 210 | ts_type v = ts_probe.second; 211 | if ( ! this->has_timestamps ) 212 | { 213 | this->has_timestamps = true; 214 | this->minmax_ts = make_pair( v, v ); 215 | } 216 | else 217 | { 218 | this->minmax_ts.first = min( this->minmax_ts.first, v ); 219 | this->minmax_ts.second = max( this->minmax_ts.second, v ); 220 | } 221 | } 222 | else 223 | { 224 | this->all_have_timestamps = false; 225 | } 226 | 227 | pair< bool, unsigned > fn_probe = this->frame_number.get( row ); 228 | if ( fn_probe.first ) 229 | { 230 | ++this->ts_fn_count.second; 231 | unsigned v = fn_probe.second; 232 | if ( ! this->has_frame_numbers ) 233 | { 234 | this->has_frame_numbers = true; 235 | this->minmax_fn = make_pair( v, v ); 236 | } 237 | else 238 | { 239 | this->minmax_fn.first = min( this->minmax_fn.first, v ); 240 | this->minmax_fn.second = max( this->minmax_fn.second, v ); 241 | } 242 | } 243 | else 244 | { 245 | this->all_have_frame_numbers = false; 246 | } 247 | } 248 | 249 | ostream& 250 | operator<<( ostream& os, 251 | const track_timestamp_stats_type& tts ) 252 | { 253 | if (tts.is_empty) 254 | { 255 | os << "Is_empty"; 256 | } 257 | else 258 | { 259 | os << "Has fn/ts? " << tts.has_frame_numbers << " " << tts.has_timestamps 260 | << " ; all have fn/ts? " << tts.all_have_frame_numbers << " " << tts.all_have_timestamps 261 | << " ;"; 262 | os << " #ts/fn: " << tts.ts_fn_count.first << " : " 263 | << tts.ts_fn_count.second << " ; "; 264 | os << " fn range "; 265 | if (tts.has_frame_numbers) 266 | { 267 | os << tts.minmax_fn.first << " : " << tts.minmax_fn.second << " "; 268 | } 269 | else 270 | { 271 | os << " na "; 272 | } 273 | if (tts.has_timestamps) 274 | { 275 | os << tts.minmax_ts.first << " : " << tts.minmax_ts.second; 276 | } 277 | else 278 | { 279 | os << " na"; 280 | } 281 | } 282 | return os; 283 | } 284 | 285 | 286 | timestamp_generator 287 | ::timestamp_generator() 288 | : timestamp_usecs( "timestamp_usecs" ), 289 | frame_number( "frame_number" ), 290 | valid( false ), 291 | fps( 0.0 ), 292 | base_ts( 0 ) 293 | { 294 | } 295 | 296 | timestamp_generator 297 | ::timestamp_generator( double f, ts_type b ) 298 | : timestamp_usecs( "timestamp_usecs" ), 299 | frame_number( "frame_number" ), 300 | valid( true ), 301 | fps( f ), 302 | base_ts( b ) 303 | { 304 | } 305 | 306 | ts_type 307 | timestamp_generator 308 | ::fn_to_ts( unsigned fn ) const 309 | { 310 | if ( ! this->valid ) throw runtime_error( "timestamp_generator::fn_to_ts with invalid generator"); 311 | double seconds_of_frames = fn / this->fps; 312 | ts_type usecs_of_frames = static_cast< ts_type >( seconds_of_frames * 1.0e6 ); 313 | return this->base_ts + usecs_of_frames; 314 | } 315 | 316 | ts_type 317 | timestamp_generator 318 | ::get_base_ts() const 319 | { 320 | if ( ! this->valid ) throw runtime_error( "timestamp_generator::base_to_ts with invalid generator"); 321 | return this->base_ts; 322 | } 323 | 324 | void 325 | timestamp_generator 326 | ::set_timestamps( const track_handle_list_type& tracks ) const 327 | { 328 | for (size_t i=0; iset_timestamps( tracks[i] ); 331 | } 332 | } 333 | 334 | void 335 | timestamp_generator 336 | ::set_timestamps( const track_handle_type& track ) const 337 | { 338 | this->set_timestamps( track_oracle_core::get_frames( track )); 339 | } 340 | 341 | void 342 | timestamp_generator 343 | ::set_timestamps( const frame_handle_list_type& frames ) const 344 | { 345 | for (size_t i=0; i probe = this->frame_number.get( frames[i].row ); 348 | if ( probe.first ) 349 | { 350 | this->timestamp_usecs( frames[i].row ) = this->fn_to_ts( probe.second ); 351 | } 352 | else 353 | { 354 | throw runtime_error( "timestamp_generator: tried to set a timestamp on a frame without a frame_number" ); 355 | } 356 | } 357 | } 358 | 359 | timestamp_generator 360 | timestamp_generator_factory 361 | ::from_tts( const track_timestamp_stats_type& tts, 362 | double fps_tts, 363 | double fps ) 364 | { 365 | // can only calculate from the tts if it has both frame numbers and 366 | // timestamps 367 | if ( tts.is_empty || (! ( tts.has_frame_numbers && tts.has_timestamps ))) 368 | { 369 | LOG_ERROR( main_logger, "timestamp_generator from_tts: tts needs both frame numbers and timestamps" ); 370 | return timestamp_generator(); 371 | } 372 | 373 | // take the minimum ts, assume it's referring to the same frame as the min frame number 374 | double offset_in_seconds_from_frame_zero = tts.minmax_fn.first / fps_tts; 375 | ts_type offset_in_usecs = static_cast< ts_type >( offset_in_seconds_from_frame_zero * 1.0e6 ); 376 | 377 | if ( offset_in_usecs > tts.minmax_ts.first ) 378 | { 379 | LOG_ERROR( main_logger, "timestamp_generator_factory::from_tts: min frame " << tts.minmax_fn.first 380 | << " @" << fps_tts << " fps has ts offset " << offset_in_usecs 381 | << " greater than min timestamp " << tts.minmax_ts.first 382 | << "; would result in a base_ts < 0; returning invalid timestamp_generator" ); 383 | return timestamp_generator(); 384 | } 385 | 386 | ts_type base_ts = tts.minmax_ts.first - offset_in_usecs; 387 | return timestamp_generator( fps, base_ts ); 388 | } 389 | 390 | 391 | pair< bool, timestamp_generator_map_type > 392 | timestamp_generator_factory 393 | ::from_timebase_file( const string& fn ) 394 | { 395 | ifstream is( fn.c_str() ); 396 | if ( ! is ) 397 | { 398 | LOG_ERROR( main_logger, "Couldn't open timebase file '" << fn << "'" ); 399 | return make_pair( false, timestamp_generator_map_type() ); 400 | } 401 | timestamp_generator_map_type m; 402 | string tmp; 403 | while (getline(is, tmp)) 404 | { 405 | istringstream iss(tmp); 406 | string tsfn; 407 | double fps; 408 | ts_type base_ts; 409 | if ( ! ( iss >> tsfn >> fps >> base_ts )) 410 | { 411 | LOG_ERROR( main_logger, "Couldn't parse fn / fps / base_ts from '" << tmp << "'"); 412 | return make_pair( false, timestamp_generator_map_type() ); 413 | } 414 | 415 | tsfn = vul_file::basename( tsfn ); 416 | m[ tsfn ] = timestamp_generator( fps, base_ts ); 417 | } 418 | return make_pair( true, m ); 419 | } 420 | 421 | 422 | bool 423 | timestamp_generator_factory 424 | ::from_virat_scenario( const string& uri, 425 | const string& start_ts_str, 426 | double fps, 427 | timestamp_generator_map_type& m ) 428 | { 429 | // timestamp expected format expected as e.g. "2009-03-19T18:26:59.666Z" 430 | vul_reg_exp re_ts("([0-9]+)\\-([0-9]+)\\-([0-9]+)T([0-9]+):([0-9]+):([0-9]+)\\.([0-9]+)Z"); 431 | if ( ! re_ts.find( start_ts_str )) 432 | { 433 | LOG_ERROR( main_logger, "Couldn't parse a scenario timestamp for " << uri << " from '" << start_ts_str << "'"); 434 | return false; 435 | } 436 | 437 | string time_str( 438 | vul_sprintf( "%04d-%02d-%02d %02d:%02d:%02d.%03d", 439 | boost::lexical_cast( re_ts.match(1) ), // year 440 | boost::lexical_cast( re_ts.match(2) ), // month 441 | boost::lexical_cast( re_ts.match(3) ), // day 442 | boost::lexical_cast( re_ts.match(4) ), // hour 443 | boost::lexical_cast( re_ts.match(5) ), // minute 444 | boost::lexical_cast( re_ts.match(6) ), // second 445 | boost::lexical_cast( re_ts.match(7) )).c_str() ); // fractional seconds 446 | 447 | boost::posix_time::ptime ts( boost::posix_time::time_from_string(time_str) ); 448 | boost::posix_time::ptime epoch( boost::gregorian::date(1970,1,1)); 449 | boost::posix_time::time_duration diff = ts - epoch; 450 | 451 | m[ uri ] = timestamp_generator( fps, diff.total_microseconds() ); 452 | 453 | return true; 454 | } 455 | 456 | } // ...timestamp_utilities 457 | 458 | } // ...kwant 459 | } // ...kwiver 460 | -------------------------------------------------------------------------------- /scoring_framework/timestamp_utilities.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2013-2016 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | // Small collection of static utilities for use with timestamps. 8 | 9 | #ifndef INCL_TIMESTAMP_UTILITIES_H 10 | #define INCL_TIMESTAMP_UTILITIES_H 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace kwiver { 21 | namespace kwant { 22 | 23 | namespace timestamp_utilities { 24 | 25 | namespace kwto = ::kwiver::track_oracle; 26 | 27 | // 28 | // This object records the minimum and maxmimum values of timestamps 29 | // and frame numbers for arbitrary collections of tracks. 30 | // 31 | // This seems to be an intersection of two recurring patterns: a 32 | // frame iterator / visitor (aka the three set_from_* methods) and 33 | // an algorithm (min/max) applied for (in this case) two track_fields. 34 | // 35 | // The implementation needs to be stricter: it allows a set of frames 36 | // which have intermittent frame_numbers and/or timestamps, which could 37 | // violate the timestamp_generator_factory's assumption that the min_ts 38 | // and min_frame_number refer to the same frame. Hmm. 39 | // 40 | 41 | struct TIMESTAMP_UTILITIES_EXPORT track_timestamp_stats_type 42 | { 43 | private: 44 | kwto::track_field< ts_type > timestamp_usecs; 45 | kwto::track_field< unsigned > frame_number; 46 | 47 | public: 48 | track_timestamp_stats_type(); 49 | track_timestamp_stats_type( const track_timestamp_stats_type& other ); 50 | explicit track_timestamp_stats_type( const kwto::track_handle_list_type& tracks ); 51 | explicit track_timestamp_stats_type( const kwto::track_handle_type& t ); 52 | explicit track_timestamp_stats_type( const kwto::frame_handle_list_type& f ); 53 | void combine_with_other( const track_timestamp_stats_type& other ); 54 | void set_from_tracks( const kwto::track_handle_list_type& tracks ); 55 | void set_from_track( const kwto::track_handle_type& t ); 56 | void set_from_frames( const kwto::frame_handle_list_type& frames ); 57 | void update_from_frame( const kwto::frame_handle_type& f ); 58 | void reset(); 59 | 60 | bool is_empty; 61 | bool has_frame_numbers; 62 | bool has_timestamps; 63 | bool all_have_frame_numbers; 64 | bool all_have_timestamps; 65 | std::pair minmax_fn; 66 | std::pair minmax_ts; 67 | std::pair ts_fn_count; 68 | 69 | }; 70 | 71 | TIMESTAMP_UTILITIES_EXPORT std::ostream& operator<<( std::ostream& os, const track_timestamp_stats_type& tts ); 72 | 73 | // 74 | // A timestamp_generator writes (or re-writes) the timestamps for a 75 | // track (or tracks) based on the assertion that frame 0 should have 76 | // the value of [base_ts] and then extrapolating linearly forward 77 | // based on the [fps] rate. 78 | // 79 | // Various ways to initialize a timestamp_generator are found in 80 | // the timestamp_generator_factory. 81 | // 82 | // We use this to assign fabricated timestamps to track formats which do not 83 | // have them, for example, XGTF. Currently, we need to do this because we 84 | // align tracks based on timestamp rather than frame number. (When the global 85 | // alignment branch lands, this will become less critical when we can align 86 | // on arbitrary fields.) 87 | // 88 | // Note the reappearance of the frame visitor pattern in the three set_* 89 | // methods. 90 | // 91 | 92 | class TIMESTAMP_UTILITIES_EXPORT timestamp_generator 93 | { 94 | public: 95 | 96 | timestamp_generator(); 97 | timestamp_generator( double fps, ts_type base_ts ); 98 | void set( double fps, ts_type base_ts ); 99 | ts_type fn_to_ts( unsigned fn ) const; 100 | ts_type get_base_ts() const; 101 | void set_timestamps( const kwto::track_handle_list_type& tracks ) const; 102 | void set_timestamps( const kwto::track_handle_type& track ) const; 103 | void set_timestamps( const kwto::frame_handle_list_type& frames ) const; 104 | 105 | private: 106 | mutable kwto::track_field< ts_type > timestamp_usecs; 107 | mutable kwto::track_field< unsigned > frame_number; 108 | bool valid; 109 | double fps; 110 | ts_type base_ts; // usecs 111 | }; 112 | 113 | // When loading from a timebase file, we only remember the basename 114 | // (because the the only way to use a timebase file is with an @file of 115 | // full paths to the files.) When we load a VIRAT scenario, we pass out 116 | // the complete pathname, because the scenario provides both the timestamps 117 | // and the full paths; the input_source_type copies this full path to its 118 | // src_fn member and then resets this to basename(full_path) for uniformity 119 | // of lookups. (Whew.) 120 | 121 | typedef std::map< std::string, timestamp_generator > timestamp_generator_map_type; 122 | typedef std::map< std::string, timestamp_generator >::const_iterator timestamp_generator_cit; 123 | 124 | struct TIMESTAMP_UTILITIES_EXPORT timestamp_generator_factory 125 | { 126 | // create a timestamp_generator from a track_timestamp_stats (TTS) 127 | // object requires two FPS values: one for the generator itself, one 128 | // to use with the TTS to backtrack to the base timestamp 129 | static timestamp_generator from_tts( const track_timestamp_stats_type& tts, 130 | double fps_tts, 131 | double fps ); 132 | 133 | // create a timestamp_generator_map from a timebase.dat file. 134 | // Returns a pair of (bool, map of filename -> timestamp_generator); 135 | // the bool is true if the map is valid, false if not (if, for example, 136 | // the file couldn't be read or something like that.) 137 | static std::pair< bool, timestamp_generator_map_type > from_timebase_file( const std::string& fn ); 138 | 139 | // Given the URI and time strings from a VIRAT scenario GroundTruth element, 140 | // insert the corresponding timestamp_generator into the map. 141 | static bool from_virat_scenario( const std::string& uri, 142 | const std::string& start_ts_str, 143 | double fps, 144 | timestamp_generator_map_type& m ); 145 | 146 | }; 147 | 148 | } // ...timestamp_utilities 149 | 150 | } // ...kwant 151 | } // ...kwiver 152 | 153 | #endif 154 | -------------------------------------------------------------------------------- /scoring_framework/track_synthesizer.cxx: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2013-2016 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #include "track_synthesizer.h" 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | static kwiver::vital::logger_handle_t main_logger( kwiver::vital::get_logger( __FILE__ ) ); 14 | 15 | using std::map; 16 | using std::ostringstream; 17 | using std::runtime_error; 18 | using std::sqrt; 19 | using std::string; 20 | 21 | using kwiver::track_oracle::frame_handle_type; 22 | using kwiver::track_oracle::track_handle_list_type; 23 | using kwiver::track_oracle::track_handle_type; 24 | 25 | namespace kwiver { 26 | namespace kwant { 27 | 28 | track_synthesizer_params 29 | ::track_synthesizer_params( double b, double o, double f) 30 | : box_side_length(b), overlap_distance(o), fps(f) 31 | { 32 | if (overlap_distance >= box_side_length) 33 | { 34 | ostringstream oss; 35 | oss << "track_synthesizer_params: overlap distance " << overlap_distance 36 | << " must be < box side length " << box_side_length << "\n"; 37 | throw runtime_error( oss.str().c_str() ); 38 | } 39 | }; 40 | 41 | // 42 | // There are only six distinct box topologies, once you factor out 43 | // the track ID assignment: 44 | // 45 | // (1) '.' - no boxes 46 | // (2) 'a', 'b', 'c' - one box 47 | // (3) 'd', 'e', - two non-overlapping boxes 48 | // (4) 'f' - three non-overlapping boxes 49 | // (5) 'x', 'y', 'z' - two overlapping boxes 50 | // (6) 'X', 'Y', 'Z' - two overlapping and one non-overlapping 51 | // (7) '#' - three overlapping boxes 52 | // 53 | // 54 | // Once the track_synthesizer_params are fixed, we can precompute the 55 | // layout of all the boxes, which are then stored in the box_pool and 56 | // mapped to track IDs based on the box descriptor code. The box_pool 57 | // tracks are labeled: 58 | // 59 | // I, J, K: three non-overlapping boxes 60 | // L, M: two (mutually) overlapping boxes which overlap I such that 61 | // all their centroids are overlap_distance apart. 62 | // 63 | // (I,L,M) describe an equilateral triangle; J and K sit off to the 64 | // side. The relative positioning of I,L, and M is the reason why 65 | // having tracks 1&2 intersect, and 1&3 intersect, but 1&3 be disjoint 66 | // is not supported. 67 | // 68 | // For example, the descriptor characters map to track IDs thusly: 69 | // 70 | // 'c': I->3 (could be J or K as well) 71 | // 'e': I->2, J->3 (again, there are other possibilities) 72 | // 'X': I->1, L->2 73 | // '#': I->1, L->2, M->3 74 | // 75 | 76 | box_pool 77 | ::box_pool( const track_synthesizer_params& params ) 78 | { 79 | double s = params.box_side_length; 80 | double no_overlap_offset = 10*s; 81 | double offset = 0.0; 82 | // three non-overlapping boxes 83 | this->boxes[ 'I' ] = vgl_box_2d( offset, offset+s, 0.0, s ); 84 | offset += no_overlap_offset; 85 | this->boxes[ 'J' ] = vgl_box_2d( offset, offset+s, 0.0, s ); 86 | offset += no_overlap_offset; 87 | this->boxes[ 'K' ] = vgl_box_2d( offset, offset+s, 0.0, s ); 88 | 89 | // two boxes overlapping with 'I' such that the centroid 90 | // distances are all ov 91 | double ov = params.overlap_distance; 92 | this->boxes[ 'L' ] = vgl_box_2d( 0, s, ov, ov+s ); 93 | 94 | // a little trig to position 'M' 95 | double x_offset = -1.0 * sqrt( 3.0/4.0*(ov*ov) ); // -1.0 just to move it off to the left 96 | double y_offset = ov / 2.0; 97 | this->boxes[ 'M' ] = vgl_box_2d( x_offset, x_offset+s, y_offset, y_offset+s ); 98 | } 99 | 100 | vgl_box_2d 101 | box_pool 102 | ::get_box( char box_code ) const 103 | { 104 | map< char, vgl_box_2d >::const_iterator i = boxes.find( box_code ); 105 | if (i == boxes.end() ) 106 | { 107 | ostringstream oss; 108 | oss << "Bad box character '" << box_code << "'"; 109 | throw runtime_error( oss.str().c_str() ); 110 | } 111 | return i->second; 112 | } 113 | 114 | // 115 | // Given a track description character, add 116 | // the box(es) to the frame on the track(s). 117 | // 118 | 119 | bool 120 | track_synthesizer 121 | ::create_boxes( char c, 122 | unsigned frame_number, 123 | ts_type ts ) 124 | { 125 | // a list of up to three XD pairs, where 126 | // X = {I,J,K,L,M} 127 | // D = {1,2,3} 128 | string mapping; 129 | switch (c) 130 | { 131 | case '.': mapping = ""; break; 132 | case 'a': mapping = "I1"; break; 133 | case 'b': mapping = "I2"; break; 134 | case 'c': mapping = "I3"; break; 135 | case 'd': mapping = "I1J2"; break; 136 | case 'e': mapping = "I2J3"; break; 137 | case 'f': mapping = "I1J3"; break; 138 | case 'g': mapping = "I1J2K3"; break; 139 | case 'x': mapping = "I1L2"; break; 140 | case 'X': mapping = "I1L2J3"; break; 141 | case 'y': mapping = "I1L3"; break; 142 | case 'Y': mapping = "I1L3J2"; break; 143 | case 'z': mapping = "I2L3"; break; 144 | case 'Z': mapping = "I2L3J1"; break; 145 | case '#': mapping = "I1L2M3"; break; 146 | default: 147 | LOG_ERROR( main_logger, "bad box description character '" << c << "'" ); 148 | return false; 149 | } 150 | 151 | // move down the string picking off pairings of box codes to track IDs 152 | size_t cursor = 0; 153 | while (cursor < mapping.size()) 154 | { 155 | unsigned box_code = mapping[cursor++]; 156 | unsigned track_id = mapping[cursor++] - '0'; 157 | this->add_box_to_track( track_id, frame_number, ts, this->bp.get_box( box_code ) ); 158 | } 159 | 160 | return true; 161 | } 162 | 163 | // 164 | // Add a single box to the track. 165 | // If the track does not exst in our track list, add it. 166 | // 167 | 168 | void 169 | track_synthesizer 170 | ::add_box_to_track( unsigned id, 171 | unsigned frame_number, 172 | ts_type ts, 173 | const vgl_box_2d& box ) 174 | { 175 | scorable_track_type t; 176 | 177 | // where is the track in our track list? 178 | track_handle_type this_track; 179 | for (size_t i=0; (! this_track.is_valid()) && (itracks.size()); ++i) 180 | { 181 | if ( t( this->tracks[i] ).external_id() == id ) this_track = this->tracks[i]; 182 | } 183 | if ( ! this_track.is_valid() ) 184 | { 185 | this_track = t.create(); 186 | t( this_track ).external_id() = id; 187 | this->tracks.push_back( this_track ); 188 | } 189 | 190 | // create a frame 191 | frame_handle_type f = t( this_track ).create_frame(); 192 | t[ f ].bounding_box() = box; 193 | t[ f ].timestamp_frame() = frame_number; 194 | t[ f ].timestamp_usecs() = ts; 195 | } 196 | 197 | bool 198 | track_synthesizer 199 | ::make_tracks( const string& track_descriptor_string, 200 | track_handle_list_type& output_tracks ) 201 | { 202 | output_tracks.clear(); 203 | this->tracks.clear(); 204 | ts_type clock_tick_usecs = static_cast( 1.0 / this->params.fps * 1.0e6 ); 205 | ts_type ts = 0; 206 | for (unsigned frame_number = 0; frame_number < track_descriptor_string.size(); ++frame_number ) 207 | { 208 | if (! this->create_boxes( track_descriptor_string[ frame_number ], 209 | frame_number, 210 | ts )) 211 | { 212 | return false; 213 | } 214 | ts += clock_tick_usecs; 215 | } 216 | output_tracks = this->tracks; 217 | return true; 218 | } 219 | 220 | } // ...kwant 221 | } // ...kwiver 222 | -------------------------------------------------------------------------------- /scoring_framework/track_synthesizer.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2013-2016 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #ifndef INCL_TRACK_SYNTHESIZER_H 8 | #define INCL_TRACK_SYNTHESIZER_H 9 | 10 | /// 11 | /// This factory generates simple tracks for the purposes of testing 12 | /// track scoring code. The tracks are instances of scorable_track_type. 13 | /// 14 | /// The user passes a string to the factory which describes how up to 15 | /// three tracks (ids 1, 2, and 3) can intersect. Each character in 16 | /// the string represents a frame and may be one of: 17 | /// 18 | /// '.' - no frames at all 19 | /// 'a', 'b', 'c' - a single box from one of tracks {1, 2, 3} 20 | /// 'd', 'e', 'f' - non-overlapping boxes from tracks { (1,2), (2,3), (1,3) } 21 | /// 'g', - three non-overlapping boxes from all tracks (1,2,3) 22 | /// 'x' - tracks 1 & 2 intersect, 3 not present 23 | /// 'X' - tracks 1 & 2 intersect, 3 present but does not intersect 24 | /// 'y' - tracks 1 & 3 intersect, 2 not present 25 | /// 'Y' - tracks 1 & 3 intersect, 2 present but does not intersect 26 | /// 'z' - tracks 2 & 3 intersect, 1 not present 27 | /// 'Z' - tracks 2 & 3 intersect, 1 present but does not intersect 28 | /// '#' - tracks 1, 2, and 3 all intersect 29 | /// 30 | /// Note that having 1 & 2 intersect, 2 & 3 intersect, 31 | /// but 1 & 3 *not* intersect is not supported. (See comments 32 | /// in box_pool below for the reason why.) 33 | /// 34 | /// 35 | /// ************* 36 | /// ************* 37 | /// NOTE! 38 | /// ************* 39 | /// ************* 40 | /// 41 | /// 'a', 'b', 'c' are TRACK IDs, NOT spatial locations. 42 | /// See track_synthesizer.cxx for an explanation why if 43 | /// you have a track string "aaaa", and another track "ccaa", 44 | /// there is 100% spatial overlap between the two strings. 45 | /// (Basically the three pre-set spatial locations are 46 | /// allocated on a first-come, first-serve per-frame basis.) 47 | /// 48 | /// Some examples: 49 | /// 50 | /// "....aaa...bbb.." 51 | /// : tracks 1 and 2 are each three frames long and do not intersect 52 | /// 53 | /// "....aaxbb.." 54 | /// : tracks 1 and 2 are each three frames long and intersect on frame 6 55 | /// 56 | /// "..a.b.c.a.b.c.X.Z.##.a.b.c.." 57 | /// : tracks 1, 2, and 3 move along independently for a while, start 58 | /// to merge, overlap for two frames, then move apart again 59 | /// 60 | 61 | #include 62 | #include 63 | 64 | namespace kwiver { 65 | namespace kwant { 66 | 67 | namespace kwto = ::kwiver::track_oracle; 68 | 69 | // 70 | // spatio-temporal parameters for the detections 71 | // 72 | // overlapping is achieved by sliding box #2 down 73 | // relative to box #1. When three boxes overlap, the 74 | // third box is positioned so that all the centroids 75 | // are positioned in an equilateral triangle. The 76 | // idea is that the overlap_distance also functions as 77 | // the radial overlap distance parameter for non-spatial 78 | // overlap detection. The drawback is that this makes it 79 | // trickier to precisely predict the amount of spatial 80 | // overlap. 81 | // 82 | struct TRACK_SYNTHESIZER_EXPORT track_synthesizer_params 83 | { 84 | double box_side_length; // boxes are square 85 | double overlap_distance; // must be in range [0, box_side_length) 86 | double fps; // for computing timestamps 87 | track_synthesizer_params( double b, double o, double f); 88 | }; 89 | 90 | 91 | class TRACK_SYNTHESIZER_EXPORT box_pool 92 | { 93 | public: 94 | explicit box_pool( const track_synthesizer_params& params ); 95 | vgl_box_2d get_box( char box_code ) const; 96 | 97 | private: 98 | std::map< char, vgl_box_2d > boxes; 99 | }; 100 | 101 | 102 | // each call to make_tracks generates independent sets of 103 | // tracks (track IDs restart at zero, etc.) 104 | 105 | class TRACK_SYNTHESIZER_EXPORT track_synthesizer 106 | { 107 | public: 108 | explicit track_synthesizer( const track_synthesizer_params& p ) 109 | : params( p ), bp( p ) 110 | {} 111 | 112 | bool make_tracks( const std::string& track_description_string, 113 | kwto::track_handle_list_type& output_tracks ); 114 | 115 | private: 116 | track_synthesizer_params params; 117 | box_pool bp; 118 | kwto::track_handle_list_type tracks; 119 | 120 | bool create_boxes( char c, unsigned frame_number, ts_type ts ); 121 | void add_box_to_track( unsigned id, unsigned frame_number, ts_type ts, const vgl_box_2d& box ); 122 | 123 | }; 124 | 125 | } // ...kwant 126 | } // ...kwiver 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /scoring_framework/virat_scenario_utilities.cxx: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2013-2016 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #include "virat_scenario_utilities.h" 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | static kwiver::vital::logger_handle_t main_logger( kwiver::vital::get_logger( __FILE__ ) ); 21 | 22 | using std::make_pair; 23 | using std::map; 24 | using std::ostringstream; 25 | using std::pair; 26 | using std::runtime_error; 27 | using std::string; 28 | using std::vector; 29 | 30 | namespace kwiver { 31 | namespace kwant { 32 | 33 | namespace virat_scenario_utilities { 34 | 35 | bool 36 | fn_is_virat_scenario( const string& fn ) 37 | { 38 | vector< string > tokens = track_oracle::xml_tokenizer::first_n_tokens( fn, 5 ); 39 | // an XML document is a scenario if we see the " >& qid2activity_map, 49 | timestamp_utilities::timestamp_generator_map_type& xgtf_map, 50 | double fps ) 51 | { 52 | string err_prefix( "virat_scenario_utilities::process on '" + fn + "': " ); 53 | bool is_scenario = fn_is_virat_scenario( fn ); 54 | if ( ! is_scenario ) 55 | { 56 | LOG_ERROR( main_logger, err_prefix << "not a scenario file?" ); 57 | return false; 58 | } 59 | 60 | TiXmlDocument doc( fn.c_str() ); 61 | if ( ! doc.LoadFile() ) return false; 62 | 63 | TiXmlNode* xmlRoot = doc.RootElement(); 64 | if ( ! xmlRoot ) 65 | { 66 | LOG_ERROR( main_logger, err_prefix << "couldn't find XML root?" ); 67 | return false; 68 | } 69 | 70 | // only interested in GroundTruth or TestCase nodes 71 | size_t tc_kept_count(0), tc_ignored_count(0); 72 | const string gtStr( "GroundTruth" ); 73 | const string tcStr( "TestCase" ); 74 | TiXmlNode* xmlObject = 0; 75 | while ( (xmlObject = xmlRoot->IterateChildren( xmlObject )) ) 76 | { 77 | if (xmlObject->Value() == gtStr) 78 | { 79 | TiXmlElement* xmlE = xmlObject->ToElement(); 80 | if ( ! xmlE ) 81 | { 82 | LOG_ERROR( main_logger, err_prefix << "couldn't convert to element?" ); 83 | return false; 84 | } 85 | const char *attr_uri = xmlE->Attribute( "uri" ); 86 | const char *attr_start_ts = xmlE->Attribute( "start") ; 87 | if ( (!attr_uri) || (!attr_start_ts)) { 88 | LOG_ERROR( main_logger, err_prefix << "at least one of uri or start was null at " << xmlE->Row() << ""); 89 | return false; 90 | } 91 | if ( ! timestamp_utilities::timestamp_generator_factory::from_virat_scenario( attr_uri, attr_start_ts, fps, xgtf_map )) 92 | { 93 | // assume timestamp_utilities routine logged exact error 94 | return false; 95 | } 96 | } 97 | else if (xmlObject->Value() == tcStr) 98 | { 99 | TiXmlElement* xmlE = xmlObject->ToElement(); 100 | if ( ! xmlE ) 101 | { 102 | LOG_ERROR( main_logger, "Couldn't convert xmlObject to element?" ); 103 | return false; 104 | } 105 | const char *attr_class = xmlE->Attribute( "class" ); 106 | 107 | // Don't count the 'CommandTestCase' stanza 108 | if (attr_class && (string(attr_class) == "CommandTestCase")) continue; 109 | 110 | bool keep_this = false; 111 | TiXmlHandle h( xmlObject ); 112 | TiXmlElement* query = h.FirstChild( "VIRATQuery" ).ToElement(); 113 | TiXmlElement* answer = h.FirstChild( "AnswerKey" ).ToElement(); 114 | string attr_class_str = 115 | (attr_class) 116 | ? string( attr_class ) 117 | : ""; 118 | bool attr_class_good = 119 | (attr_class_str == "HistQueryTestCase") || 120 | (attr_class_str == "IQRHistQueryTestCase" ); 121 | if (attr_class_good && query && answer ) 122 | { 123 | const char *attr_mtype = query->Attribute( "mtype" ); 124 | string attr_mtype_str = 125 | (attr_mtype) 126 | ? string( attr_mtype ) 127 | : ""; 128 | if ( (attr_mtype_str == "hist" ) || ( attr_mtype_str == "histIQR") ) 129 | { 130 | keep_this = true; 131 | } 132 | } 133 | 134 | if ( ! keep_this ) 135 | { 136 | LOG_INFO( main_logger, "Ignoring TestCase at line " << xmlObject->Row() ); 137 | ++tc_ignored_count; 138 | continue; 139 | } 140 | 141 | ++tc_kept_count; 142 | const char *attr_qid = query->Attribute( "id" ); 143 | if ( ! attr_qid ) 144 | { 145 | ostringstream oss; 146 | oss << "Malformed scenario: VIRATQuery without 'id' attribute at " << fn << " line " << query->Row() 147 | << "\n'" << *query << "'"; 148 | 149 | throw runtime_error( oss.str() ); 150 | } 151 | const char* attr_activity_list = answer->Attribute( "actType" ); 152 | if ( ! attr_activity_list ) 153 | { 154 | ostringstream oss; 155 | oss << "Malformed scenario: AnswerKey without 'actType' attribute at line " << answer->Row(); 156 | throw runtime_error( oss.str() ); 157 | } 158 | string activity_list_str( attr_activity_list ); 159 | 160 | vector< string > parsed_activities; 161 | { 162 | boost::char_separator delim( "," ); 163 | boost::tokenizer< boost::char_separator< char > > act_tokens( activity_list_str, delim ); 164 | parsed_activities.assign( act_tokens.begin(), act_tokens.end() ); 165 | } 166 | 167 | vector< size_t > activity_list; 168 | for (size_t i=0; i >::iterator, bool > r = 174 | qid2activity_map.insert( make_pair( string( attr_qid ), activity_list) ); 175 | if ( ! r.second ) 176 | { 177 | LOG_WARN( main_logger, "Duplicate query id " << attr_qid << " in scenario?" ); 178 | } 179 | } // ...test case clause 180 | } // ... for all XML objects 181 | 182 | if (tc_kept_count + tc_ignored_count > 0) 183 | { 184 | LOG_INFO( main_logger, "Loading VIRAT scenario: kept " << tc_kept_count << " test cases; ignored " << tc_ignored_count ); 185 | } 186 | return true; 187 | } 188 | 189 | } // ...virat_scenario_utilities 190 | 191 | } // ...kwant 192 | } // ...kwiver 193 | -------------------------------------------------------------------------------- /scoring_framework/virat_scenario_utilities.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2013-2016 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | // Small collection of static utilities for use with VIRAT scenario files. 8 | 9 | #ifndef INCL_VIRAT_SCENARIO_UTILITIES_H 10 | #define INCL_VIRAT_SCENARIO_UTILITIES_H 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | namespace kwiver { 18 | namespace kwant { 19 | 20 | namespace virat_scenario_utilities 21 | { 22 | 23 | // See if the file contains a virat scenario with a quick 24 | // parse of the tokens, rather than a full-on tinyXML load 25 | 26 | SCORE_CORE_EXPORT bool fn_is_virat_scenario( const std::string& fn ); 27 | 28 | 29 | // Assuming that fn is a virat scenario, parse it and return two 30 | // pieces of information: 31 | // - the query ID to activity map (for scoring) 32 | // - the groundtruth filename / base timestamp map (for loading and aligning 33 | // groundtruth tracks 34 | 35 | SCORE_CORE_EXPORT bool process( const std::string& fn, 36 | std::map< std::string, std::vector< size_t > >& qid2activity_map, 37 | timestamp_utilities::timestamp_generator_map_type& xgtf_map, 38 | double fps ); 39 | 40 | } // virat_scenario_utilities 41 | 42 | } // ...kwant 43 | } // ...kwiver 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /utilities/blank_line_filter.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2012-2014 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #ifndef _VIDTK_BLANK_LINE_FILTER_H_ 8 | #define _VIDTK_BLANK_LINE_FILTER_H_ 9 | 10 | #include 11 | 12 | #include // EOF, WOULD_BLOCK 13 | #include // multichar_input_filter 14 | #include // get 15 | 16 | namespace vidtk { 17 | 18 | 19 | 20 | // ---------------------------------------------------------------- 21 | /** Blank line filter. 22 | * 23 | * This filter absorbs blank lines, including the trailing newline. 24 | * 25 | * Add this filter to boost::filtering_streams to remove blank lines 26 | * from a io stream. 27 | * 28 | * Example: 29 | \code 30 | #include 31 | 32 | std::ifstream reg_file( opt.c_str() ); 33 | 34 | // Build boost filter to remove comments and blank lines. 35 | // Order is important. 36 | boost::iostreams::filtering_istream in_stream; 37 | in_stream.push (vidtk::blank_line_filter()); 38 | in_stream.push (vidtk::shell_comments_filter()); 39 | in_stream.push (reg_file); 40 | 41 | // read file until eof 42 | std::string line; 43 | while ( std::getline(in_stream, line) ) 44 | { 45 | // process 'line' 46 | } // end while 47 | \endcode 48 | */ 49 | class blank_line_filter 50 | : public boost::iostreams::input_filter 51 | { 52 | public: 53 | blank_line_filter() 54 | : state_(ST_INIT) 55 | { } 56 | 57 | 58 | 59 | // ---------------------------------------------------------------- 60 | /* Skip blank lines. 61 | * 62 | * This filter absorbs blank lines. This could be a simpler 63 | * implementation if we were to make assumptions about leading 64 | * whitespace. 65 | * 66 | * This method is called from the boost::streams framework and should 67 | * not be called directly unless you know what you are doing. 68 | */ 69 | template 70 | int get(Source& src) 71 | { 72 | int c; 73 | while (true) 74 | { 75 | switch (this->state_) 76 | { 77 | 78 | case ST_INIT: 79 | { 80 | if ((c = boost::iostreams::get(src)) == EOF || c == boost::iostreams::WOULD_BLOCK) 81 | { 82 | return c; 83 | } 84 | 85 | // looking for non-whitespace and found EOL, 86 | // drop line and start again. 87 | if (c == '\n') 88 | { 89 | line_buffer_.clear(); 90 | break; // get next character 91 | } 92 | 93 | // Add character to buffer 94 | line_buffer_.append(1, c); 95 | 96 | if (! isspace(c)) 97 | { 98 | // We have non-whitespace character, flush collected 99 | // characters downstream and pass remaining characters. 100 | buffer_ptr_ = line_buffer_.begin(); 101 | state_ = ST_PASS_BUFFER; // switch to passing state 102 | } 103 | break; 104 | } 105 | 106 | case ST_PASS_BUFFER: 107 | { 108 | // passing buffer contents down stream 109 | if (buffer_ptr_ == line_buffer_.end()) 110 | { 111 | line_buffer_.clear(); 112 | state_ = ST_PASS; // switch to passing state 113 | break; // get next character 114 | } 115 | 116 | // return buffered character 117 | int b = *buffer_ptr_; 118 | buffer_ptr_++; 119 | return b; 120 | } 121 | 122 | // Pass characters until newline is seen. 123 | // pass newline too. 124 | case ST_PASS: 125 | { 126 | if ((c = boost::iostreams::get(src)) == EOF || c == boost::iostreams::WOULD_BLOCK) 127 | { 128 | return c; 129 | } 130 | 131 | if (c == '\n') 132 | { 133 | // EOL resets to buffering state 134 | state_ = ST_INIT; 135 | } 136 | return c; 137 | } 138 | 139 | } // end switch 140 | } // end while 141 | 142 | return c; 143 | } 144 | 145 | 146 | template 147 | void close(Source&) { state_ = ST_INIT; } 148 | 149 | private: 150 | 151 | enum { ST_INIT, ST_PASS_BUFFER, ST_PASS} state_; 152 | 153 | 154 | std::string line_buffer_; 155 | // Used for flushing buffered lines 156 | std::string::iterator buffer_ptr_; 157 | 158 | }; 159 | 160 | } // end namespace 161 | 162 | #endif /* _VIDTK_BLANK_LINE_FILTER_H_ */ 163 | -------------------------------------------------------------------------------- /utilities/shell_comments_filter.h: -------------------------------------------------------------------------------- 1 | /*ckwg +5 2 | * Copyright 2012-2014 by Kitware, Inc. All Rights Reserved. Please refer to 3 | * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel, 4 | * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065. 5 | */ 6 | 7 | #ifndef _VIDTK_SHELL_COMMENTS_FILTER_H_ 8 | #define _VIDTK_SHELL_COMMENTS_FILTER_H_ 9 | 10 | #include // EOF, WOULD_BLOCK 11 | #include // multichar_input_filter 12 | #include // get 13 | 14 | 15 | namespace vidtk 16 | { 17 | 18 | // ---------------------------------------------------------------- 19 | /** Filter to remove shell style comments. 20 | * 21 | * This filter removes all characters between the comment character 22 | * (default '#') up to, but not including, the \n from the stream. 23 | * 24 | * Add this filter to boost::filtering_streams to remove comments from 25 | * a stream. This can be combined with other filters in the 26 | * filtering_stream. 27 | * 28 | * Example: 29 | \code 30 | #include 31 | 32 | std::ifstream reg_file( opt.c_str() ); 33 | 34 | // Build boost filter to remove comments and blank lines. 35 | // Order is important. 36 | boost::iostreams::filtering_istream in_stream; 37 | in_stream.push (vidtk::blank_line_filter()); 38 | in_stream.push (vidtk::shell_comments_filter()); 39 | in_stream.push (reg_file); 40 | 41 | // read file until eof 42 | std::string line; 43 | while ( std::getline(in_stream, line) ) 44 | { 45 | // process 'line' 46 | } // end while 47 | \endcode 48 | */ 49 | class shell_comments_filter 50 | : public boost::iostreams::input_filter 51 | { 52 | public: 53 | // ---------------------------------------------------------------- 54 | /** Constructor. 55 | * 56 | * Create a new shell comment filter. The optional parameter 57 | * specifies the character to use to start the comment. 58 | * @param[in] comment_char - character to start comment. Default to '#'. 59 | */ 60 | 61 | explicit shell_comments_filter(char comment_char = '#') 62 | : skip_(false), comment_char_(comment_char) 63 | { } 64 | 65 | 66 | // ---------------------------------------------------------------- 67 | /* Filter input stream. 68 | * 69 | * This method reads one or more characters and produces only one 70 | * character. 71 | * 72 | * This method is called from the boost::streams framework and should 73 | * not be called directly unless you know what you are doing. 74 | */ 75 | template 76 | int get(Source& src) 77 | { 78 | int c; 79 | while (true) 80 | { 81 | if ((c = boost::iostreams::get(src)) == EOF || c == boost::iostreams::WOULD_BLOCK) 82 | { 83 | break; 84 | } 85 | 86 | skip_ = (c == comment_char_) ? 87 | true : c == '\n' ? 88 | false : skip_; 89 | 90 | if (!skip_) 91 | { 92 | break; 93 | } 94 | } 95 | return c; 96 | } 97 | 98 | 99 | template 100 | void close(Source&) { skip_ = false; } 101 | 102 | 103 | private: 104 | bool skip_; 105 | 106 | /// Comment character to use 107 | char comment_char_; 108 | 109 | }; 110 | 111 | } // end namespace 112 | 113 | 114 | #endif /* _VIDTK_SHELL_COMMENTS_FILTER_H_ */ 115 | --------------------------------------------------------------------------------