changeset 1186:7117a31555c1

Etienne Beauchamp s work on optimization with Nomad software
author Nicolas Saunier <nicolas.saunier@polymtl.ca>
date Tue, 21 Jun 2022 17:06:06 -0400
parents aa88acf06876
children 25c85a7ecf09
files scripts/nomad/README.md scripts/nomad/initial-parameters.txt scripts/nomad/nomad-parameters.txt scripts/nomad/optimize-with-nomad.py scripts/nomad/site-parameters-optimization.py
diffstat 5 files changed, 214 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/nomad/README.md	Tue Jun 21 17:06:06 2022 -0400
@@ -0,0 +1,38 @@
+# Optimization tool
+
+----
+## usage
+1. Make sure your data is structured like so:
+```
+    Data                        # Folder containing all the data
+    └── <Intersection name>     # Intersection (e.g. montcalm-chartwell)
+        ├── <tracking configuration file>.cfg
+        ├── <Date 1>
+        │   ├── <Other subdirectories>    # There can be as many subdirectories as you want
+        │       ├── <Video 1>
+        │       ├── <Ground truth database 1>_gt.sqlite
+        │       ├── homography.txt
+        │       ├── image.pg
+        │       ├── mask.png
+        │       └── point-correspondences.txt
+        ├── <Date 2>
+        └── <Date 3>
+             .
+             .
+             .
+```
+
+> Please note : You should have at least one ground truth database (with a name ending with "_gt.sqlite") for each date of the intersection.
+
+2. Select an intersection or several intersections for which you wish to find optimal tracking parameters, for instance:
+
+        $ python3 optimize-with-nomad.py -t /media/disk2/etienne/Data/montcalm-chartwell/ /media/disk2/etienne/Data/montcalm-victorin/ --optimize-grouping-only
+
+> Please note : You cannot find the optimal parameters only for the grouping algorithm if there is not a database already created. You should first execute the tracking algorithm with basic parameters.
+
+3. Once the optimal parameters are found, tracking may be launched:
+
+        $ cd $HOME/Data/montcalm-chartwell
+        $ feature-based-tracking tracking-visible.cfg --tf --video-filename 2019-11-27/Visible/2019-11-27T12\:20-05\:00.MP4 --database-filename 2019-11-27/Visible/2019-11-27T12\:20-05\:00.sqlite --homography-filename 2019-11-27/Visible/homography.txt --mask-filename 2019-11-27/Visible/mask.png --feature-quality 0.1 --min-feature-distanceklt 3.54964337411 --window-size 6 --min-tracking-error 0.01 --min-feature-time 15
+        $ feature-based-tracking tracking-visible.cfg --gf --video-filename 2019-11-27/Visible/2019-11-27T12\:20-05\:00.MP4 --database-filename 2019-11-27/Visible/2019-11-27T12\:20-05\:00.sqlite --homography-filename 2019-11-27/Visible/homography.txt --mask-filename 2019-11-27/Visible/mask.png --mm-connection-distance 2 --mm-segmentation-distance 1.9 --min-nfeatures-group 4
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/nomad/initial-parameters.txt	Tue Jun 21 17:06:06 2022 -0400
@@ -0,0 +1,1 @@
+0.1 3.54964337411 6 0.1833289751 15 2.5 0.5 3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/nomad/nomad-parameters.txt	Tue Jun 21 17:06:06 2022 -0400
@@ -0,0 +1,29 @@
+DIMENSION	8						# number of variables
+
+BB_EXE		"$python3 site-parameters-optimization.py"      # 'site-parameters-optimization.py' is the blackbox program
+BB_OUTPUT_TYPE	OBJ					        # object will be minimized
+
+X0		initial-parameters.txt                          # starting point
+
+LOWER_BOUND	( 0 0  1  0.01 2   0.5 0.1 1  )			# all variables' lower bounds
+UPPER_BOUND	( 1 10 10 0.3  100 100 100 15 )			# all variables' upper bounds
+
+MAX_BB_EVAL	500						# the algorithm terminates when
+								# n black-box evaluations have
+								# been made
+							  
+# TMP_DIR	/tmp						# indicates a directory where
+								# temporary files are put
+								# (increases performance by ~100%
+								# if you're working on a network
+								# account and if TMP_DIR is on a
+								# local disk)
+
+DISPLAY_DEGREE 2 
+
+# DISPLAY_ALL_EVAL yes
+
+DISPLAY_STATS BBE ( SOL ) OBJ					# Display the number of evaluation (BBE),
+                                                                # the current solution ( SOL ) and the objective
+
+# STATS_FILE test.txt BBE ( SOL ) OBJ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/nomad/optimize-with-nomad.py	Tue Jun 21 17:06:06 2022 -0400
@@ -0,0 +1,29 @@
+import os
+import argparse
+
+parser = argparse.ArgumentParser(description='The program is used to select the type of tracking to run with '
+                                             'tracking-mota.py with NOMAD',
+                                 epilog='''NOMAD - A blackbox optimization software: 
+                                 C. Audet, S. Le Digabel, C. Tribes and V. Rochon Montplaisir. The NOMAD project. 
+                                 Software available at https://www.gerad.ca/nomad.
+                                 
+                                 S. Le Digabel. Algorithm 909: NOMAD: Nonlinear Optimization with the MADS algorithm. 
+                                 ACM Transactions on Mathematical Software, 37(4):44:1–44:15, 2011.''',
+                                 formatter_class=argparse.RawDescriptionHelpFormatter)
+
+parser.add_argument('-t', dest='intersections', nargs = '*', type = str, help='name of the intersection for which '
+                                                    'the optimization is meant to be ran',
+                    required=True)
+parser.add_argument('--optimize-grouping-only', dest='optimizeGroupingOnly',
+                    help='optimize only the grouping parameters and not the ones associated with feature tracking',
+                    action='store_true')
+
+args = parser.parse_args()
+
+with open('arguments.txt', 'w') as f:
+    f.write(str(args.intersections))
+    f.write("\n")
+    f.write(str(args.optimizeGroupingOnly))
+
+os.system('nomad nomad-parameters.txt initial-parameters.txt')
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/nomad/site-parameters-optimization.py	Tue Jun 21 17:06:06 2022 -0400
@@ -0,0 +1,117 @@
+#! /usr/bin/env python3
+import os
+import sys
+import glob
+from trafficintelligence import storage, moving
+import subprocess
+import numpy as np
+
+
+def loadParametersStartProcess(filename):
+    # load initial parameters from x.txt
+    f = open(filename, 'r+')
+    l = f.readline()
+    x = [s for s in l.strip().split(" ")]
+    f.close()
+    
+    # create para-value list
+    para = paraValueList(x)
+    
+    # run process including trackingfeature, groupfeature, load groundtruth, compute mota
+    print(process(para, intersections, optimizeGroupingOnly))
+
+def paraValueList(x):
+    #create para-value list
+    #list of the 8 parameters and their values
+    pn = 8
+    p = pn*[None]
+    p[0] = '--feature-quality'             #]0.-0.4]
+    p[1] = '--min-feature-distanceklt'     #]0.-6]
+    p[2] = '--window-size'                 #[1-10]integer
+    p[3] = '--min-tracking-error'          #[0.01-0.3]
+    p[4] = '--min-feature-time'            #[2-100]integer
+    p[5] = '--mm-connection-distance'      #[0.5-100]
+    p[6] = '--mm-segmentation-distance'    #[1-100] ~mm-connection-distance / 2.5
+    p[7] = '--min-nfeatures-group'         #[2-4]
+    
+    para = []
+    for n in range(pn):
+        para = para + [p[n],x[n]]
+    
+    return para
+
+def process(para, intersections, optimizeGroupingOnly):
+    Mota = []
+    gtDatabaseaAbsPaths = []
+    configFileAbsPaths = []
+
+    cwd = os.getcwd()
+    # move to the location of the intersection
+    for intersectionPath in intersections:
+        intersectionAbsPath = os.path.abspath(intersectionPath)
+        os.chdir(intersectionAbsPath)
+        # iterate through all the subdirectories to find ground truth sqlite files
+        gtDatabaseaAbsPaths.extend([os.path.abspath(intersectionAbsPath + '/' + file) for file in glob.glob('**/*_gt.sqlite', recursive=True)])
+        configFileAbsPaths.append(os.path.abspath(intersectionAbsPath + '/' + glob.glob('*.cfg', recursive=True)[0]))
+        os.chdir(cwd)
+    for gtDatabaseAbsPath, configFileAbsPath in zip(gtDatabaseaAbsPaths, configFileAbsPaths):
+        gtDatabaseBasename = gtDatabaseAbsPath[:-10]
+        videoFilename = gtDatabaseBasename + ".MP4"
+        databaseFilename = gtDatabaseBasename + ".sqlite"
+        gtDatabaseDirname = os.path.dirname(gtDatabaseAbsPath)
+        homographyFilename = gtDatabaseDirname + "/homography.txt"
+        maskFilename = gtDatabaseDirname + "/mask.png"
+        # Skip feature tracking if the user specified to optimize only grouping parameters
+        if not optimizeGroupingOnly:
+            # Track features
+            trackingFeature(para, configFileAbsPath, videoFilename, databaseFilename, homographyFilename, maskFilename)
+        # Group features
+        groupFeature(para, configFileAbsPath, videoFilename, databaseFilename, homographyFilename, maskFilename)
+        #load trajectory
+        objects = storage.loadTrajectoriesFromSqlite(databaseFilename, 'object')
+        #load ground truth
+        annotations = storage.loadTrajectoriesFromSqlite(gtDatabaseAbsPath, 'object')
+        # Appending negative mota because nomad minimizes the output
+        Mota.append(-computeMota(annotations, objects, Mota))
+    
+    # Change to the previous directory
+    os.chdir(cwd)
+
+    return np.mean(Mota)
+
+def trackingFeature(para, config, video, db, homo, mask):
+    # remove previous tracking
+    if os.path.exists(db):
+        os.remove(db)
+    # trackingfeature command parameters
+    tf = ['feature-based-tracking', config, '--tf', '--video-filename', video, '--database-filename', db, '--homography-filename', homo, '--mask-filename', mask]
+    # run in command line and print directly
+    subprocess.check_output(tf + para[0:10])
+
+def groupFeature(para, config, video, db, homo, mask):
+    #remove previous grouping
+    storage.deleteFromSqlite(db, 'object')
+    #groupfeature command parameters
+    gf = ['feature-based-tracking', config, '--gf', '--video-filename', video, '--database-filename', db, '--homography-filename', homo, '--mask-filename', mask]
+    #run in command line and print directly
+    subprocess.check_output(gf + para[8:16])  # ['--min-feature-time', 'x', '--mm-connection-distance', 'x', '--mm-segmentation-distance', 'x', '--min-nfeatures-group', 'x']
+
+def computeMota(annotations, objects, Mota):
+    matchingDistance = 500
+    firstInstant = 0
+    lastInstant = 50000
+    return moving.computeClearMOT(annotations, objects, matchingDistance, firstInstant, lastInstant)[1]
+
+
+if __name__ == "__main__":
+    # Load args that were given with select-arguments.py
+    with open('arguments.txt', 'r') as f:
+        args = f.read().split('\n')
+        intersections = args[0]
+        optimizeGroupingOnly = args[1]
+        # Convert string representation of list into list
+        intersections = eval(intersections)
+
+    loadParametersStartProcess(sys.argv[1])
+    sys.exit(0)
+