├── .gitignore ├── README.md ├── compile.m ├── demo.m ├── get_opts.m ├── setup.m └── src ├── L1-tracklets ├── createTracklets.m ├── estimateVelocities.m ├── getAppearanceSubMatrix.m ├── getValidDetections.m ├── motionAffinity.m ├── smoothTracklets.m ├── trackletsVisualizePart1.m ├── trackletsVisualizePart2.m ├── trackletsVisualizePart3.m └── trackletsVisualizePart4.m ├── L2-trajectories ├── createTrajectories.m ├── fillTrajectories.m ├── findTrackletsInWindow.m ├── findTrajectoriesInWindow.m ├── getAppearanceMatrix.m ├── getSpaceTimeAffinity.m ├── getTrackletFeatures.m ├── mergeResults.m ├── mergeTracklets.m ├── postProcess.m ├── recomputeTrajectories.m ├── removeShortTracks.m ├── solveInGroups.m ├── trackletsToTrajectories.m ├── trajectoriesToTop.m ├── trajectoriesVisualizePart1.m ├── trajectoriesVisualizePart2.m └── trajectoriesVisualizePart3.m ├── L3-identities ├── getSpaceTimeAffinityL3.m ├── getTrajectoryFeatures.m ├── linkIdentities.m ├── loadL2trajectories.m ├── solveInGroupsIdentities.m └── trajectoriesToIdentities.m ├── compute_L0_features.m ├── compute_L1_tracklets.m ├── compute_L2_trajectories.m ├── compute_L3_identities.m ├── correlation_clustering ├── BIPCC │ ├── BIPCC.m │ ├── createBIP.m │ ├── graphPartitioning.m │ └── labelsFromAdjacency.m └── external │ ├── AL-ICM │ ├── AL_ICM.m │ ├── LICENSE.txt │ ├── README.txt │ ├── VERSION.txt │ └── pure_potts_icm_iter_mex.cpp │ └── KL │ ├── KernighanLin.m │ ├── LICENSE.txt │ ├── Multicut_KL_MEX.cpp │ └── andres │ ├── graph │ ├── adjacency.hxx │ ├── complete-graph.hxx │ ├── detail │ │ └── graph.hxx │ ├── graph.hxx │ ├── multicut │ │ └── kernighan-lin.hxx │ ├── subgraph.hxx │ └── visitor.hxx │ └── random-access-set.hxx ├── duke ├── DukeFrameReader.m ├── DukeVideoReader.m ├── downloadDukeMTMC.m ├── getROIs.m ├── global2local.m ├── image2world.m ├── local2global.m ├── prepareMOTChallengeSubmission.m └── world2map.m ├── evaluate.m ├── external ├── DukeMTMC-reID_evaluation │ ├── CITATION_DukeMTMC-reID.txt │ ├── CITATION_DukeMTMC.txt │ ├── Data_Distribution.jpg │ ├── DukeMTMC-reID_mosaic.jpg │ ├── LICENSE_DukeMTMC-reID.txt │ ├── LICENSE_DukeMTMC.txt │ ├── README.md │ ├── State-of-the-art │ │ └── README.md │ ├── col_sum.m │ ├── compute_AP.m │ ├── duke_rank.jpg │ ├── evaluation_res_duke_fast.m │ ├── file.png │ ├── generate_train_list_caffe.m │ ├── sqdist.m │ └── train_list.txt ├── combinator │ ├── combinator.m │ ├── cumsumall.cpp │ ├── cumsumall.m │ └── license.txt ├── distinguishable_colors │ ├── distinguishable_colors.m │ └── license.txt ├── export-fig │ ├── .gitignore │ ├── ImageSelection.class │ ├── ImageSelection.java │ ├── LICENSE │ ├── README.md │ ├── append_pdfs.m │ ├── copyfig.m │ ├── crop_borders.m │ ├── eps2pdf.m │ ├── export_fig.m │ ├── fix_lines.m │ ├── ghostscript.m │ ├── im2gif.m │ ├── isolate_axes.m │ ├── pdf2eps.m │ ├── pdftops.m │ ├── print2array.m │ ├── print2eps.m │ ├── read_write_entire_textfile.m │ ├── user_string.m │ └── using_hg2.m ├── motchallenge-devkit │ ├── README.md │ ├── evaluateTracking.m │ ├── seqmaps │ │ ├── DukeMTMCT-test-easy.txt │ │ ├── DukeMTMCT-test-hard.txt │ │ ├── DukeMTMCT-trainval-mini.txt │ │ └── DukeMTMCT-trainval.txt │ └── utils │ │ ├── CLEAR_MOT_HUN.m │ │ ├── IDmeasures.m │ │ ├── MinCostMatching.cpp │ │ ├── clearMOTMex.cpp │ │ ├── costBlockMex.cpp │ │ ├── costFunction.m │ │ ├── evaluateBenchmark.m │ │ ├── evaluateMultiCam.m │ │ ├── parseSequences2.m │ │ ├── printMetrics.m │ │ └── printMetricsExt.m └── nestedSortStruct │ ├── license.txt │ ├── nestedSortStruct.m │ ├── nestedSortStruct2.m │ ├── sortStruct.m │ └── sortStruct2.m ├── triplet-reid ├── LICENSE ├── README.md ├── aggregators.py ├── common.py ├── data │ ├── duke_query.csv │ ├── duke_test.csv │ ├── duke_train.csv │ ├── duke_train_small.csv │ ├── duke_trainvalmini.csv │ ├── duke_val_small.csv │ ├── market1501_query.csv │ ├── market1501_test.csv │ └── market1501_train.csv ├── duke_utils.py ├── embed.m ├── embed.py ├── embed_detections.m ├── embed_detections.py ├── evaluate.py ├── excluders │ ├── diagonal.py │ └── market1501.py ├── heads │ ├── __init__.py │ ├── direct.py │ ├── direct_normalize.py │ ├── fc1024.py │ └── fc1024_normalize.py ├── lbtoolbox.py ├── loss.py ├── nets │ ├── README.md │ ├── __init__.py │ ├── mobilenet_v1.py │ ├── mobilenet_v1_1_224.py │ ├── resnet_utils.py │ ├── resnet_v1.py │ ├── resnet_v1_101.py │ └── resnet_v1_50.py ├── train.py ├── train_duke.m ├── train_duke.sh └── train_duke_hnm.m ├── util ├── create_experiment_dir.m ├── error_types.m ├── feetPosition.m ├── getBoundingBoxCenters.m ├── getSpatialGroupIDs.m ├── get_bb.m ├── identities2mat.m ├── intervalSearch.m ├── matching_errors.m ├── pose2bb.m ├── renderPoses.m └── scale_bb.m └── visualization ├── data ├── duke_easy_scores.txt ├── duke_hard_scores.txt └── map.jpg ├── render_results.m ├── render_state_of_the_art.m ├── render_trajectories_side.m ├── render_trajectories_top.m ├── show_detections.m └── view_distance_distribution.m /.gitignore: -------------------------------------------------------------------------------- 1 | experiments/ 2 | src/external/mexopencv/ 3 | *.mex* 4 | *.asv 5 | *.pyc -------------------------------------------------------------------------------- /compile.m: -------------------------------------------------------------------------------- 1 | cur_dir = pwd; 2 | 3 | % KL 4 | cd src/correlation_clustering/external/KL 5 | mex -O -largeArrayDims Multicut_KL_MEX.cpp 6 | cd(cur_dir) 7 | 8 | % AL-ICM 9 | cd src/correlation_clustering/external/AL-ICM 10 | mex -O -largeArrayDims pure_potts_icm_iter_mex.cpp 11 | cd(cur_dir) 12 | 13 | % Combinator 14 | cd src/external/combinator 15 | mex -O -largeArrayDims cumsumall.cpp 16 | cd(cur_dir) 17 | 18 | cd src/external/motchallenge-devkit 19 | mex utils/MinCostMatching.cpp -outdir utils CXXFLAGS="$CXXFLAGS --std=c++11" 20 | mex utils/clearMOTMex.cpp -outdir utils CXXFLAGS="$CXXFLAGS --std=c++11" 21 | mex utils/costBlockMex.cpp -outdir utils COMPFLAGS="/openmp $COMPFLAGS" CXXFLAGS="$CXXFLAGS --std=c++11" 22 | cd(cur_dir) 23 | -------------------------------------------------------------------------------- /demo.m: -------------------------------------------------------------------------------- 1 | %% Options 2 | opts = get_opts(); 3 | create_experiment_dir(opts); 4 | 5 | %% Setup Gurobi 6 | if ~exist('setup_done','var') 7 | setup; 8 | setup_done = true; 9 | end 10 | 11 | %% Run Tracker 12 | 13 | % opts.visualize = true; 14 | opts.sequence = 2; % trainval-mini 15 | 16 | % Tracklets 17 | opts.optimization = 'KL'; 18 | compute_L1_tracklets(opts); 19 | 20 | % Single-camera trajectories 21 | opts.optimization = 'BIPCC'; 22 | opts.trajectories.appearance_groups = 1; 23 | compute_L2_trajectories(opts); 24 | opts.eval_dir = 'L2-trajectories'; 25 | evaluate(opts); 26 | 27 | % Multi-camera identities 28 | opts.identities.appearance_groups = 0; 29 | compute_L3_identities(opts); 30 | opts.eval_dir = 'L3-identities'; 31 | evaluate(opts); 32 | 33 | -------------------------------------------------------------------------------- /get_opts.m: -------------------------------------------------------------------------------- 1 | function opts = get_opts() 2 | 3 | addpath(genpath('src')) 4 | 5 | opts = []; 6 | opts.dataset_path = 'F:/DukeMTMC/'; 7 | opts.gurobi_path = 'C:/gurobi800/win64/matlab'; 8 | opts.experiment_root = 'experiments'; 9 | opts.experiment_name = 'demo'; 10 | opts.python3 = 'python3'; 11 | 12 | opts.reader = DukeVideoReader(opts.dataset_path); 13 | 14 | % General settings 15 | opts.eval_dir = 'L3-identities'; 16 | opts.visualize = false; 17 | opts.image_width = 1920; 18 | opts.image_height = 1080; 19 | opts.current_camera = -1; 20 | opts.world = 0; 21 | opts.ROIs = getROIs(); 22 | opts.minimum_trajectory_length = 100; 23 | opts.optimization = 'BIPCC'; 24 | opts.use_groupping = 1; 25 | opts.num_cam = 8; 26 | opts.sequence = 2; 27 | opts.sequence_names = {'trainval', 'trainval_mini', 'test_easy', 'test_hard'}; 28 | opts.sequence_intervals = {47720:227540, 127720:187540, 263504:356648, 227541:263503}; 29 | opts.start_frames = [5543, 3607, 27244, 31182, 1, 22402, 18968, 46766]; 30 | opts.render_threshold = 0.05; 31 | opts.load_tracklets = 1; 32 | opts.load_trajectories = 1; 33 | 34 | % Tracklets 35 | tracklets = []; 36 | tracklets.window_width = 50; 37 | tracklets.min_length = 5; 38 | tracklets.alpha = 1; 39 | tracklets.beta = 0.02; 40 | tracklets.cluster_coeff = 0.75; 41 | tracklets.nearest_neighbors = 8; 42 | tracklets.speed_limit = 20; 43 | tracklets.threshold = 8; 44 | 45 | % Trajectories 46 | trajectories = []; 47 | trajectories.appearance_groups = 0; % determined automatically when zero 48 | trajectories.alpha = 1; 49 | trajectories.beta = 0.01; 50 | trajectories.window_width = 300; 51 | trajectories.overlap = 150; 52 | trajectories.speed_limit = 30; 53 | trajectories.indifference_time = 100; 54 | trajectories.threshold = 8; 55 | 56 | % Identities 57 | identities = []; 58 | identities.window_width = 5000; 59 | identities.appearance_groups = 0; % determined automatically when zero 60 | identities.alpha = 1; 61 | identities.beta = 0.01; 62 | identities.overlap = 150; 63 | identities.speed_limit = 30; 64 | identities.indifference_time = 150; 65 | identities.threshold = 8; 66 | identities.extract_images = true; 67 | 68 | % CNN model 69 | net = []; 70 | net.train_set = 'data/duke_train.csv'; 71 | net.image_root = 'F:/DukeMTMC/DukeMTMC-reID'; 72 | net.model_name = 'resnet_v1_50'; 73 | net.initial_checkpoint = 'resnet_v1_50.ckpt'; 74 | net.experiment_root = 'experiments/demo_weighted_triplet'; 75 | net.embedding_dim = 128; 76 | net.batch_p = 18; 77 | net.batch_k = 4; 78 | net.pre_crop_height = 288; 79 | net.pre_crop_width = 144; 80 | net.input_width = 128; 81 | net.input_height = 256; 82 | net.margin = 'soft'; 83 | net.metric = 'euclidean'; 84 | net.loss = 'weighted_triplet'; 85 | net.learning_rate = 0.0003; 86 | net.train_iterations = 25000; 87 | net.decay_start_iteration = 15000; 88 | net.gpu_device = 0; 89 | net.augment = true; 90 | net.resume = false; 91 | net.checkpoint_frequency = 1000; 92 | net.hard_pool_size = 0; 93 | 94 | opts.tracklets = tracklets; 95 | opts.trajectories = trajectories; 96 | opts.identities = identities; 97 | opts.net = net; 98 | 99 | 100 | -------------------------------------------------------------------------------- /setup.m: -------------------------------------------------------------------------------- 1 | % Checks for a valid installation of the Gurobi optimizer. 2 | % Without Gurobi you can still run the tracker with the AL-ICM or KL optimizer 3 | try 4 | % Change this path accordingly 5 | run(fullfile(opts.gurobi_path, 'gurobi_setup.m')); 6 | catch 7 | fprintf('\nWARNING!\n\nGurobi optimizer not found.\nYou can still run the tracker with AL-ICM or KL optimization.\n'); 8 | end -------------------------------------------------------------------------------- /src/L1-tracklets/estimateVelocities.m: -------------------------------------------------------------------------------- 1 | function estimatedVelocities = estimateVelocities(originalDetections, startFrame, endFrame, nearestNeighbors, speedLimit) 2 | % This function estimates the velocity of a detection by calculating the component-wise 3 | % median of velocities required to reach a specified number of nearest neighbors. 4 | % Neighbors that exceed a specified speed limit are not considered. 5 | 6 | % Find detections in search range 7 | searchRangeMask = intervalSearch(originalDetections(:,1), startFrame - nearestNeighbors, endFrame+ nearestNeighbors); 8 | searchRangeCenters = getBoundingBoxCenters(originalDetections(searchRangeMask, 3:6)); 9 | searchRangeFrames = originalDetections(searchRangeMask, 1); 10 | detectionIndices = intervalSearch(searchRangeFrames, startFrame, endFrame); 11 | 12 | % Compute all pairwise distances 13 | pairDistance = pdist2(searchRangeCenters,searchRangeCenters); 14 | numDetections = length(detectionIndices); 15 | estimatedVelocities = zeros(numDetections,2); 16 | 17 | % Estimate the velocity of each detection 18 | for i = 1:numDetections 19 | 20 | currentDetectionIndex = detectionIndices(i); 21 | 22 | velocities = []; 23 | currentFrame = searchRangeFrames(currentDetectionIndex); 24 | 25 | % For each time instant in a small time neighborhood find the nearest detection in space 26 | for frame = currentFrame-nearestNeighbors:currentFrame+nearestNeighbors 27 | 28 | % Skip original frame 29 | if abs(currentFrame-frame) <= 0 30 | continue; 31 | end 32 | 33 | detectionsAtThisTimeInstant = searchRangeFrames == frame; 34 | 35 | % Skip if no detections in the current frame 36 | if sum(detectionsAtThisTimeInstant) == 0 37 | continue; 38 | end 39 | 40 | distancesAtThisTimeInstant = pairDistance(currentDetectionIndex,:); 41 | distancesAtThisTimeInstant(detectionsAtThisTimeInstant==0) = inf; 42 | 43 | % Find detection closest to the current detection 44 | [~, targetDetectionIndex] = min(distancesAtThisTimeInstant); 45 | estimatedVelocity = searchRangeCenters(targetDetectionIndex,:) - searchRangeCenters(currentDetectionIndex,:); 46 | estimatedVelocity = estimatedVelocity / (searchRangeFrames(targetDetectionIndex) - searchRangeFrames(currentDetectionIndex)); 47 | 48 | % Check if speed limit is violated 49 | estimatedSpeed = norm(estimatedVelocity); 50 | if estimatedSpeed > speedLimit 51 | continue; 52 | end 53 | 54 | % Update velocity estimates 55 | velocities = [velocities; estimatedVelocity]; 56 | 57 | end 58 | 59 | if isempty(velocities) 60 | velocities = [0 0]; 61 | end 62 | 63 | % Estimate the velocity 64 | estimatedVelocities(i,1) = mean(velocities(:,1)); 65 | estimatedVelocities(i,2) = mean(velocities(:,2)); 66 | 67 | end 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/L1-tracklets/getAppearanceSubMatrix.m: -------------------------------------------------------------------------------- 1 | function [ correlation ] = getAppearanceSubMatrix(observations, featureVectors, threshold ) 2 | 3 | features = cell2mat(featureVectors.appearance(observations)); 4 | dist = pdist2(features, features); 5 | correlation = (threshold - dist)/ threshold; 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/L1-tracklets/getValidDetections.m: -------------------------------------------------------------------------------- 1 | function [valid, detections_in_interval] = getValidDetections(detections_in_interval, detections_conf, num_visible, opts, iCam) 2 | 3 | % Flags valid OpenPose detections 4 | valid = true(size(detections_in_interval,1),1); 5 | for k = 1:size(detections_in_interval,1) 6 | pose = detections_in_interval(k,3:end); 7 | bb = pose2bb(pose, opts.render_threshold); 8 | [newbb, newpose] = scale_bb(bb, pose,1.25); 9 | feet = feetPosition(newbb); 10 | detections_in_interval(k,[3 4 5 6]) = newbb; 11 | 12 | % Drop small and large detections 13 | if newbb(3) < 20 || newbb(4) < 20 || newbb(4) > 450 14 | valid(k) = 0; 15 | continue; 16 | end 17 | 18 | % Check detection confidence 19 | if num_visible(k) < 5 || detections_conf(k) < 4 20 | valid(k) = 0; 21 | continue; 22 | end 23 | 24 | % Filter feet outside of ROI 25 | if ~inpolygon(feet(:,1),feet(:,2),opts.ROIs{iCam}(:,1),opts.ROIs{iCam}(:,2)) 26 | valid(k) = 0; 27 | continue; 28 | end 29 | 30 | end 31 | 32 | -------------------------------------------------------------------------------- /src/L1-tracklets/motionAffinity.m: -------------------------------------------------------------------------------- 1 | function [motionScores, impossibilityMatrix] = motionAffinity(detectionCenters,detectionFrames,estimatedVelocity, speedLimit, beta) 2 | % This function computes the motion affinities given a set of detections. 3 | % A simple motion prediction is performed from a source detection to 4 | % a target detection to compute the prediction error. 5 | 6 | numDetections = size(detectionCenters,1); 7 | impossibilityMatrix = zeros(length(detectionFrames)); 8 | 9 | frameDifference = pdist2(detectionFrames, detectionFrames); 10 | velocityX = repmat(estimatedVelocity(:,1), 1, numDetections ); 11 | velocityY = repmat(estimatedVelocity(:,2), 1, numDetections ); 12 | centerX = repmat(detectionCenters(:,1), 1, numDetections ); 13 | centerY = repmat(detectionCenters(:,2), 1, numDetections ); 14 | 15 | errorXForward = centerX + velocityX.*frameDifference - centerX'; 16 | errorYForward = centerY + velocityY.*frameDifference - centerY'; 17 | 18 | errorXBackward = centerX' + velocityX' .* -frameDifference' - centerX; 19 | errorYBackward = centerY' + velocityY' .* -frameDifference' - centerY; 20 | 21 | errorForward = sqrt( errorXForward.^2 + errorYForward.^2); 22 | errorBackward = sqrt( errorXBackward.^2 + errorYBackward.^2); 23 | 24 | % Only upper triangular part is valid 25 | predictionError = min(errorForward, errorBackward); 26 | predictionError = triu(predictionError) + triu(predictionError)'; 27 | 28 | % Check if speed limit is violated 29 | xDiff = centerX - centerX'; 30 | yDiff = centerY - centerY'; 31 | distanceMatrix = sqrt(xDiff.^2 + yDiff.^2); 32 | 33 | maxRequiredSpeedMatrix = distanceMatrix ./ abs(frameDifference); 34 | predictionError(maxRequiredSpeedMatrix > speedLimit) = inf; 35 | impossibilityMatrix(maxRequiredSpeedMatrix > speedLimit) = 1; 36 | 37 | motionScores = 1 - beta*predictionError; 38 | 39 | -------------------------------------------------------------------------------- /src/L1-tracklets/smoothTracklets.m: -------------------------------------------------------------------------------- 1 | function smoothedTracklets = smoothTracklets( tracklets, segmentStart, segmentInterval, featuresAppearance, minTrackletLength, currentInterval ) 2 | % This function smooths given tracklets by fitting a low degree polynomial 3 | % in their spatial location 4 | 5 | trackletIDs = unique(tracklets(:,2)); 6 | numTracklets = length(trackletIDs); 7 | smoothedTracklets = struct([]); 8 | 9 | for i = 1:numTracklets 10 | 11 | mask = tracklets(:,2)==trackletIDs(i); 12 | detections = tracklets(mask,:); 13 | 14 | % Reject tracklets of short length 15 | start = min(detections(:,1)); 16 | finish = max(detections(:,1)); 17 | 18 | if (size(detections,1) < minTrackletLength) || (finish - start < minTrackletLength) 19 | continue; 20 | end 21 | 22 | intervalLength = finish-start + 1; 23 | 24 | datapoints = linspace(start, finish, intervalLength); 25 | frames = detections(:,1); 26 | 27 | currentTracklet = zeros(intervalLength,size(tracklets,2)); 28 | currentTracklet(:,2) = ones(intervalLength,1) .* trackletIDs(i); 29 | currentTracklet(:,1) = [start : finish]; 30 | 31 | % Fit left, top, right, bottom, xworld, yworld 32 | for k = 3:size(tracklets,2) 33 | 34 | points = detections(:,k); 35 | p = polyfit(frames,points,1); 36 | newpoints = polyval(p, datapoints); 37 | 38 | currentTracklet(:,k) = newpoints'; 39 | end 40 | 41 | 42 | 43 | % Compute appearance features 44 | medianFeature = median(cell2mat(featuresAppearance(mask))); 45 | centers = getBoundingBoxCenters(currentTracklet(:,[3:6])); 46 | centerPoint = median(centers); % assumes more then one detection per tracklet 47 | centerPointWorld = 1;% median(currentTracklet(:,[7,8])); 48 | 49 | % Add to tracklet list 50 | smoothedTracklets(end+1).feature = medianFeature; 51 | smoothedTracklets(end).center = centerPoint; 52 | smoothedTracklets(end).centerWorld = centerPointWorld; 53 | smoothedTracklets(end).data = currentTracklet; 54 | smoothedTracklets(end).features = featuresAppearance(mask); 55 | smoothedTracklets(end).realdata = detections; 56 | smoothedTracklets(end).mask = mask; 57 | smoothedTracklets(end).startFrame = start; 58 | smoothedTracklets(end).endFrame = finish; 59 | smoothedTracklets(end).interval = currentInterval; 60 | smoothedTracklets(end).segmentStart = segmentStart; 61 | smoothedTracklets(end).segmentInterval = segmentInterval; 62 | smoothedTracklets(end).segmentEnd = segmentStart + segmentInterval - 1; 63 | 64 | assert(~isempty(currentTracklet)); 65 | end 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/L1-tracklets/trackletsVisualizePart1.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % SHOW DETECTIONS IN WINDOW 3 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4 | 5 | h = figure(2); 6 | clf('reset'); 7 | imshow(opts.reader.getFrame(opts.current_camera,startFrame)); 8 | pause(1); 9 | hold on; 10 | scatter(detectionCenters(:,1),detectionCenters(:,2)); 11 | 12 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -------------------------------------------------------------------------------- /src/L1-tracklets/trackletsVisualizePart2.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % SHOW SPATIAL GROUPING AND CORRELATIONS 3 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4 | 5 | rows = find(spatialGroupIDs == spatialGroupID); 6 | 7 | figure(2); 8 | 9 | % draw bounding box 10 | minx = min(detectionCentersImage(rows,1)); 11 | maxx = max(detectionCentersImage(rows,1)); 12 | miny = min(detectionCentersImage(rows,2)); 13 | maxy = max(detectionCentersImage(rows,2)); 14 | 15 | x = minx - 40; 16 | y = miny - 40; 17 | w = maxx - minx + 80; 18 | h = maxy - miny + 80; 19 | 20 | rectangle('Position',[x,y,w,h],'EdgeColor','green','LineWidth',2); 21 | 22 | labels = originalDetections(spatialGroupObservations,1); 23 | 24 | for kk = min(labels):min(labels) 25 | 26 | rrows = rows; 27 | 28 | if isempty(rrows) 29 | continue; 30 | end 31 | 32 | pairs = combnk([1:length(rrows)],2); 33 | 34 | pairs_i = pairs(:,1); 35 | pairs_j = pairs(:,2); 36 | 37 | points1 = detectionCentersImage(rrows(pairs_i),:); 38 | points2 = detectionCentersImage(rrows(pairs_j),:); 39 | 40 | points1top = detectionCenters(rrows(pairs_i),:); 41 | points2top = detectionCenters(rrows(pairs_j),:); 42 | 43 | myColorMap = NegativeEnhancingColormap(128, [-1 1], ... 44 | [0 0 1], [1 0 0], 1); 45 | 46 | if size(pairs,1) >2 47 | for p = 1 : length(pairs) 48 | 49 | pts = [ points1(p,:); points2(p,:) ]; 50 | ptstop = [ points1top(p,:); points2top(p,:) ]; 51 | correlation = correlationMatrix(pairs_i(p),pairs_j(p)); 52 | colorindex = int32( 1 + (1 + correlation) * 63 ); 53 | 54 | 55 | if correlation<-1 56 | linecolor = [0 0 0]; 57 | else 58 | linecolor = myColorMap(colorindex,:); 59 | end 60 | 61 | line(pts(:,1),pts(:,2),'color',linecolor); 62 | hold on; 63 | end 64 | end 65 | 66 | pause(0.5); 67 | end 68 | 69 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -------------------------------------------------------------------------------- /src/L1-tracklets/trackletsVisualizePart3.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % SHOW CLUSTERED DETECTIONS 3 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4 | ids = unique(identities); 5 | 6 | for kkk = 1:length(ids); 7 | 8 | id = ids(kkk); 9 | 10 | mask = identities == id; 11 | mask2 = ismember(currentDetectionsIDX,spatialGroupObservations.*mask); 12 | rows = find(currentDetectionsIDX .* mask2); 13 | 14 | figure(2); 15 | scatter(detectionCenters(rows,1), detectionCenters(rows,2), 'fill'); 16 | hold on; 17 | 18 | end 19 | 20 | pause(0.5); 21 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -------------------------------------------------------------------------------- /src/L1-tracklets/trackletsVisualizePart4.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % SHOW GENERATED TRACKLETS 3 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4 | 5 | figure(3); 6 | clf('reset') 7 | imshow(opts.reader.getFrame(opts.current_camera,startFrame)); 8 | hold on; 9 | 10 | for k = 1:length(ids) 11 | if isempty(smoothedTracklets), break; end 12 | 13 | data = smoothedTracklets(k).data; 14 | centers = getBoundingBoxCenters(data(:, 3 : 6)); 15 | 16 | scatter(centers(:,1),centers(:,2),'filled'); 17 | hold on; 18 | end 19 | hold off; 20 | drawnow; 21 | 22 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -------------------------------------------------------------------------------- /src/L2-trajectories/createTrajectories.m: -------------------------------------------------------------------------------- 1 | function outputTrajectories = createTrajectories( opts, inputTrajectories, startTime, endTime) 2 | % CREATETRAJECTORIES partitions a set of tracklets into trajectories. 3 | % The third stage uses appearance grouping to reduce problem complexity; 4 | % the fourth stage solves the graph partitioning problem for each 5 | % appearance group. 6 | 7 | 8 | % find current, old, and future tracklets 9 | currentTrajectoriesInd = findTrajectoriesInWindow(inputTrajectories, startTime, endTime); 10 | currentTrajectories = inputTrajectories(currentTrajectoriesInd); 11 | 12 | % safety check 13 | if length(currentTrajectories) <= 1 14 | outputTrajectories = inputTrajectories; 15 | return; 16 | end 17 | 18 | % select tracklets that will be selected in association. For previously 19 | % computed trajectories we select only the last three tracklets. 20 | inAssociation = []; tracklets = []; trackletLabels = []; 21 | for i = 1 : length(currentTrajectories) 22 | for k = 1 : length(currentTrajectories(i).tracklets) 23 | tracklets = [tracklets; currentTrajectories(i).tracklets(k)]; %#ok 24 | trackletLabels = [trackletLabels; i]; %#ok 25 | 26 | inAssociation(length(trackletLabels)) = false; %#ok 27 | if k >= length(currentTrajectories(i).tracklets) - 5 28 | inAssociation(length(trackletLabels)) = true; %#ok 29 | end 30 | 31 | end 32 | end 33 | inAssociation = logical(inAssociation); 34 | 35 | % show all tracklets 36 | if opts.visualize, trajectoriesVisualizePart1; end 37 | 38 | % solve the graph partitioning problem for each appearance group 39 | result = solveInGroups(opts, tracklets(inAssociation), trackletLabels(inAssociation)); 40 | 41 | % merge back solution. Tracklets that were associated are now merged back 42 | % with the rest of the tracklets that were sharing the same trajectory 43 | labels = trackletLabels; labels(inAssociation) = result.labels; 44 | count = 1; 45 | for i = 1 : length(inAssociation) 46 | if inAssociation(i) > 0 47 | labels(trackletLabels == trackletLabels(i)) = result.labels(count); 48 | count = count + 1; 49 | end 50 | end 51 | 52 | % merge co-identified tracklets to extended tracklets 53 | newTrajectories = trackletsToTrajectories(tracklets, labels); 54 | smoothTrajectories = recomputeTrajectories(newTrajectories); 55 | 56 | outputTrajectories = inputTrajectories; 57 | outputTrajectories(currentTrajectoriesInd) = []; 58 | outputTrajectories = [outputTrajectories; smoothTrajectories']; 59 | 60 | % show merged tracklets in window 61 | if opts.visualize, trajectoriesVisualizePart3; end 62 | 63 | end 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/L2-trajectories/fillTrajectories.m: -------------------------------------------------------------------------------- 1 | function [ detectionsUpdated ] = fillTrajectories( detections ) 2 | % This function adds points by interpolation to the resulting trajectories, 3 | % so that trajectories are complete and results are less influenced by 4 | % false negative detections 5 | 6 | detections = sortrows(detections,[1 3 4 5 6]); 7 | 8 | 9 | detectionsUpdated = detections; 10 | 11 | personIDs = unique(detections(:,2)); 12 | 13 | count = 0; 14 | 15 | for i = 1 : length(personIDs) 16 | 17 | personID = personIDs( i ); 18 | 19 | relevantDetections = detections( detections(:,2) == personID, : ); 20 | 21 | startFrame = min( relevantDetections(:,1)); 22 | endFrame = max( relevantDetections(:,1) ); 23 | 24 | missingFrames = setdiff( [startFrame:endFrame]', relevantDetections(:,1) ); 25 | 26 | if isempty(missingFrames) 27 | 28 | continue; 29 | 30 | end 31 | 32 | frameDiff = diff(missingFrames') > 1; 33 | 34 | startInd = [1, frameDiff]; 35 | endInd = [frameDiff, 1]; 36 | 37 | startInd = find(startInd); 38 | endInd = find(endInd); 39 | 40 | 41 | 42 | for k = 1:length(startInd) 43 | 44 | interpolatedDetections = zeros( missingFrames(endInd(k)) - missingFrames(startInd(k)) + 1 , size(detections,2) ); 45 | 46 | interpolatedDetections(:,2) = personID; 47 | interpolatedDetections(:,1) = [ missingFrames(startInd(k)):missingFrames(endInd(k)) ]'; 48 | 49 | preDetection = detections( (detections(:,2) == personID) .* detections(:,1) == missingFrames(startInd(k)) - 1, :); 50 | postDetection = detections( (detections(:,2) == personID) .* detections(:,1) == missingFrames(endInd(k)) + 1, :); 51 | 52 | 53 | for c = 3:size(detections,2) 54 | 55 | interpolatedDetections(:,c) = linspace(preDetection(c),postDetection(c),size(interpolatedDetections,1)); 56 | 57 | end 58 | 59 | detectionsUpdated = [ detectionsUpdated; interpolatedDetections ]; 60 | 61 | end 62 | 63 | count = count + length( missingFrames ); 64 | 65 | 66 | 67 | 68 | 69 | end 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/L2-trajectories/findTrackletsInWindow.m: -------------------------------------------------------------------------------- 1 | function trackletsInWindow = findTrackletsInWindow(trajectories, startTime, endTime) 2 | trackletsInWindow = []; 3 | 4 | if isempty(trajectories), return; end 5 | 6 | trackletStartFrame = cell2mat({trajectories.startFrame}); 7 | trackletEndFrame = cell2mat({trajectories.endFrame}); 8 | trackletsInWindow = find( (trackletEndFrame >= startTime) .* (trackletStartFrame <= endTime) ); 9 | 10 | -------------------------------------------------------------------------------- /src/L2-trajectories/findTrajectoriesInWindow.m: -------------------------------------------------------------------------------- 1 | function trajectoriesInWindow = findTrajectoriesInWindow(trajectories, startTime, endTime) 2 | trajectoriesInWindow = []; 3 | 4 | if isempty(trajectories), return; end 5 | 6 | trajectoryStartFrame = [trajectories.startFrame]; %cell2mat({trajectories.startFrame}); 7 | trajectoryEndFrame = [trajectories.endFrame]; % cell2mat({trajectories.endFrame}); 8 | trajectoriesInWindow = find( (trajectoryEndFrame >= startTime) .* (trajectoryStartFrame <= endTime) ); 9 | 10 | -------------------------------------------------------------------------------- /src/L2-trajectories/getAppearanceMatrix.m: -------------------------------------------------------------------------------- 1 | function [ appearanceMatrix ] = getAppearanceMatrix(featureVectors, threshold ) 2 | 3 | % Computes the appearance affinity matrix 4 | 5 | features = double(cell2mat(featureVectors')); 6 | dist = pdist2(features, features); 7 | appearanceMatrix = (threshold - dist)/ threshold; 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/L2-trajectories/getSpaceTimeAffinity.m: -------------------------------------------------------------------------------- 1 | function [stAffinity, impossibilityMatrix, indiffMatrix] = getSpaceTimeAffinity(tracklets, beta1, speedLimit, indifferenceLimit) 2 | 3 | numTracklets = length(tracklets); 4 | 5 | [~, ~, startpoint, endpoint, intervals, ~, velocity] = getTrackletFeatures(tracklets); 6 | 7 | centerFrame = round(mean(intervals,2)); 8 | frameDifference = pdist2(centerFrame, centerFrame, @(frame1, frame2) frame1 - frame2); 9 | overlapping = pdist2(intervals,intervals, @overlapTest); 10 | centers = 0.5 * (endpoint + startpoint); 11 | centersDistance = pdist2(centers,centers); 12 | v = (frameDifference > 0) | overlapping; 13 | merging = (centersDistance < 5) & overlapping; 14 | 15 | velocityX = repmat(velocity(:, 1), 1, numTracklets); 16 | velocityY = repmat(velocity(:, 2), 1, numTracklets); 17 | 18 | startX = repmat(centers(:, 1), 1, numTracklets); 19 | startY = repmat(centers(:, 2), 1, numTracklets); 20 | endX = repmat(centers(:, 1), 1, numTracklets); 21 | endY = repmat(centers(:, 2), 1, numTracklets); 22 | 23 | errorXForward = endX + velocityX .* frameDifference - startX'; 24 | errorYForward = endY + velocityY .* frameDifference - startY'; 25 | 26 | errorXBackward = startX' + velocityX' .* -frameDifference - endX; 27 | errorYBackward = startY' + velocityY' .* -frameDifference - endY; 28 | 29 | errorForward = sqrt(errorXForward.^2 + errorYForward.^2); 30 | errorBackward = sqrt(errorXBackward.^2 + errorYBackward.^2); 31 | 32 | % check if speed limit is violated 33 | xDiff = endX - startX'; 34 | yDiff = endY - startY'; 35 | distanceMatrix = sqrt(xDiff.^2 + yDiff.^2); 36 | maxSpeedMatrix = distanceMatrix./abs(frameDifference); 37 | 38 | violators = maxSpeedMatrix > speedLimit; 39 | violators(~v) = 0; 40 | violators = violators + violators'; 41 | 42 | % build impossibility matrix 43 | impossibilityMatrix = zeros(numTracklets); 44 | impossibilityMatrix(violators == 1 & merging ~=1) = 1; 45 | impossibilityMatrix(overlapping == 1 & merging ~=1) = 1; 46 | 47 | % this is a symmetric matrix, although tracklets are oriented in time 48 | errorMatrix = min(errorForward, errorBackward); 49 | errorMatrix = errorMatrix .* v; 50 | errorMatrix(~v) = 0; 51 | errorMatrix = errorMatrix + errorMatrix'; 52 | errorMatrix(violators == 1) = Inf; 53 | 54 | % compute indifference matrix 55 | timeDifference = frameDifference .* (frameDifference > 0); 56 | timeDifference = timeDifference + timeDifference'; 57 | indiffMatrix = 1 - sigmf(timeDifference,[0.1 indifferenceLimit/2]); 58 | 59 | % compute space-time affinities 60 | stAffinity = 1 - beta1*errorMatrix; 61 | stAffinity = max(0, stAffinity); 62 | 63 | end 64 | 65 | function overlap = overlapTest(interval1, interval2) 66 | 67 | duration1 = interval1(2) - interval1(1); 68 | duration2 = interval2(:,2) - interval2(:, 1); 69 | 70 | i1 = repmat(interval1,size(interval2,1), 1); 71 | unionMin = min([i1, interval2], [], 2); 72 | unionMax = max([i1, interval2], [], 2); 73 | 74 | overlap = double(duration1 + duration2 - unionMax + unionMin >= 0); 75 | 76 | end 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/L2-trajectories/getTrackletFeatures.m: -------------------------------------------------------------------------------- 1 | function [ centersWorld, centersView, startpoint, endpoint, intervals, duration, velocity ] = getTrackletFeatures( tracklets ) 2 | 3 | numTracklets = length(tracklets); 4 | 5 | % bounding box centers for each tracklet 6 | 7 | centersWorld = cell( numTracklets, 1 ); 8 | centersView = cell( numTracklets, 1 ); 9 | 10 | for i = 1 : numTracklets 11 | 12 | detections = cell2mat({tracklets(i).data}); 13 | 14 | % 2d points 15 | bb = detections( :, [3,4,5,6,1] ); 16 | x = 0.5*(bb(:,1) + bb(:,3)); 17 | y = 0.5*(bb(:,2) + bb(:,4)); 18 | t = bb(:,5); 19 | centersView{i} = [x,y,t]; 20 | 21 | % 3d points 22 | % worldcoords = detections(:, [7,8,2] ); 23 | % x = worldcoords(:,1); 24 | % y = worldcoords(:,2); 25 | % t = worldcoords(:,3); 26 | % centersWorld{i} = [x,y,t]; 27 | centersWorld{i} = centersView{i}; 28 | 29 | end 30 | 31 | % calculate velocity, direction, for each tracklet 32 | 33 | velocity = zeros(numTracklets,2); 34 | duration = zeros(numTracklets,1); 35 | intervals = zeros(numTracklets,2); 36 | startpoint = zeros(numTracklets,2); 37 | endpoint = zeros(numTracklets,2); 38 | 39 | 40 | for ind = 1:numTracklets 41 | 42 | 43 | intervals(ind,:) = [centersWorld{ind}(1,3), centersWorld{ind}(end,3)]; 44 | startpoint(ind,:) = [centersWorld{ind}(1,1), centersWorld{ind}(1,2)]; 45 | endpoint(ind,:) = [centersWorld{ind}(end,1), centersWorld{ind}(end,2)]; 46 | 47 | duration(ind) = centersWorld{ind}(end,3)-centersWorld{ind}(1,3); 48 | direction = [endpoint(ind,:) - startpoint(ind,:)]; 49 | velocity(ind,:) = direction./duration(ind); 50 | 51 | 52 | end 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/L2-trajectories/mergeResults.m: -------------------------------------------------------------------------------- 1 | function [ mergedResult ] = mergeResults( result1, result2 ) 2 | % Merges the independent solutions for the two results 3 | 4 | maximumLabel = max(result1.labels); 5 | 6 | if isempty(maximumLabel) 7 | maximumLabel = 0; 8 | end 9 | 10 | mergedResult.labels = [result1.labels; maximumLabel + result2.labels]; 11 | mergedResult.observations = [result1.observations;result2.observations]; 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/L2-trajectories/mergeTracklets.m: -------------------------------------------------------------------------------- 1 | function [ mergedTracklets ] = mergeTracklets( tracklets, labels ) 2 | % This function merges co-identified tracklets 3 | 4 | uniqueLabels = unique(labels); 5 | 6 | for i = 1:length(uniqueLabels) 7 | 8 | currentLabel = uniqueLabels(i); 9 | 10 | trackletIndicesToMerge = find(labels == currentLabel); 11 | 12 | newTracklet = []; 13 | newTracklet.startFrame = inf; 14 | newTracklet.endFrame = -1; 15 | newTracklet.ids = []; 16 | newTracklet.data = []; 17 | newTracklet.realdata = []; 18 | newTracklet.feature = []; 19 | newTracklet.features = []; 20 | newTracklet.mask = []; 21 | newTracklet.id = 0; 22 | 23 | for k = 1:length(trackletIndicesToMerge) 24 | 25 | id = trackletIndicesToMerge(k); 26 | 27 | newTracklet.data = [newTracklet.data; tracklets(id).data]; 28 | newTracklet.data = sortrows(newTracklet.data,2); 29 | 30 | 31 | newTracklet.realdata = [newTracklet.realdata; tracklets(id).realdata]; 32 | newTracklet.features = [newTracklet.features; tracklets(id).features]; 33 | newTracklet.startFrame = min(newTracklet.startFrame, tracklets(id).startFrame); 34 | newTracklet.endFrame = max(newTracklet.endFrame, tracklets(id).endFrame); 35 | newTracklet.ids = [newTracklet.ids; tracklets(id).ids]; 36 | newTracklet.interval = tracklets(id).interval; 37 | newTracklet.center = tracklets(id).center; 38 | newTracklet.centerWorld = tracklets(id).centerWorld; 39 | 40 | end 41 | 42 | % Re-compute the appearance descriptor 43 | medianFeature = median(cell2mat(newTracklet.features)); 44 | newTracklet.feature = medianFeature; 45 | 46 | mergedTracklets(i) = newTracklet; 47 | 48 | end 49 | 50 | mergedTracklets = mergedTracklets'; 51 | -------------------------------------------------------------------------------- /src/L2-trajectories/postProcess.m: -------------------------------------------------------------------------------- 1 | function [trackerOutputFinal] = postProcess(dataset, extendedTracklets, trackerOutputFinal) 2 | if isempty(extendedTracklets), return; end 3 | 4 | lastID = 0; 5 | if ~isempty(trackerOutputFinal), lastID = max(trackerOutputFinal(:, 1)); end 6 | 7 | % collect tracker output 8 | trackerOutputRaw = []; 9 | for i = 1 : length(extendedTracklets) 10 | currentTrackletData = extendedTracklets(i).data; 11 | currentTrackletData(:, 1) = lastID + 1; 12 | lastID = lastID + 1; 13 | trackerOutputRaw = [trackerOutputRaw; currentTrackletData]; 14 | end 15 | 16 | % interpolate to recover missing observations 17 | trackerOutputFilled = fillTrajectories(trackerOutputRaw); 18 | 19 | % remove very short trajectories 20 | trackerOutputRemoved = removeShortTracks(trackerOutputFilled, dataset.minimumTrajectoryDuration); 21 | %[~, ~, ic] = unique(trackerOutputRemoved(:,1)); 22 | %trackerOutputRemoved(:,1) = ic; 23 | %trackerOutput = sortrows(trackerOutputRemoved,[2 1]); 24 | 25 | trackerOutputFinal = sortrows(vertcat(trackerOutputFinal, trackerOutputRemoved), [2 1]); 26 | -------------------------------------------------------------------------------- /src/L2-trajectories/recomputeTrajectories.m: -------------------------------------------------------------------------------- 1 | function newTrajectories = recomputeTrajectories( trajectories ) 2 | %RECOMPUTETRAJECTORIES Summary of this function goes here 3 | % Detailed explanation goes here 4 | 5 | % newTrajectories = trajectories; 6 | 7 | segmentLength = 50; 8 | 9 | for i = 1:length(trajectories) 10 | 11 | segmentStart = trajectories(i).segmentStart; 12 | segmentEnd = trajectories(i).segmentEnd; 13 | 14 | numSegments = (segmentEnd + 1 - segmentStart) / segmentLength; 15 | 16 | alldata = {trajectories(i).tracklets(:).data}; 17 | alldata = cell2mat(alldata'); 18 | alldata = sortrows(alldata,2); 19 | [~, uniqueRows] = unique(alldata(:,1)); 20 | 21 | alldata = alldata(uniqueRows,:); 22 | dataFrames = alldata(:,1); 23 | 24 | frames = segmentStart:segmentEnd; 25 | interestingFrames = round([min(dataFrames), frames(1) + segmentLength/2:segmentLength:frames(end), max(dataFrames)]); 26 | 27 | keyData = alldata(ismember(dataFrames,interestingFrames),:); 28 | 29 | % for k = size(keyData,1)-1:-1:1 30 | % 31 | % while keyData(k,2) == keyData(k+1,2) 32 | % keyData(k+1,:) = []; 33 | % end 34 | % 35 | % end 36 | 37 | keyData(:,2) = -1; 38 | newData = fillTrajectories(keyData); 39 | 40 | newTrajectory = trajectories(i); 41 | sampleTracklet = trajectories(i).tracklets(1); 42 | newTrajectory.tracklets = []; 43 | 44 | 45 | for k = 1:numSegments 46 | 47 | tracklet = sampleTracklet; 48 | tracklet.segmentStart = segmentStart + (k-1)*segmentLength; 49 | tracklet.segmentEnd = tracklet.segmentStart + segmentLength - 1; 50 | 51 | trackletFrames = tracklet.segmentStart:tracklet.segmentEnd; 52 | 53 | 54 | rows = ismember(newData(:,1), trackletFrames); 55 | 56 | tracklet.data = newData(rows,:); 57 | 58 | tracklet.startFrame = min(tracklet.data(:,1)); 59 | tracklet.endFrame = max(tracklet.data(:,1)); 60 | % if isempty(tracklet.data) 61 | % tracklet.startFrame = min(tracklet.realdata(:,1)); 62 | % tracklet.endFrame = max(tracklet.realdata(:,1)); 63 | % end 64 | newTrajectory.startFrame = min(newTrajectory.startFrame, tracklet.startFrame); 65 | newTrajectory.endFrame = max(newTrajectory.endFrame, tracklet.endFrame); 66 | 67 | newTrajectory.tracklets = [newTrajectory.tracklets; tracklet]; 68 | 69 | end 70 | 71 | newTrajectories(i) = newTrajectory; 72 | 73 | 74 | end 75 | 76 | -------------------------------------------------------------------------------- /src/L2-trajectories/removeShortTracks.m: -------------------------------------------------------------------------------- 1 | function [ detectionsUpdated ] = removeShortTracks( detections, cutoffLength ) 2 | %This function removes short tracks that have not been associated with any 3 | %trajectory. Those are likely to be false positives. 4 | 5 | detectionsUpdated = detections; 6 | 7 | detections = sortrows(detections, [2, 1]); 8 | 9 | personIDs = unique(detections(:,2)); 10 | lengths = hist(detections(:,2), personIDs)'; 11 | 12 | [~,~, removedIDs] = find( personIDs .* (lengths <= cutoffLength)); 13 | 14 | detectionsUpdated(ismember(detectionsUpdated(:,2),removedIDs),:) = []; 15 | 16 | -------------------------------------------------------------------------------- /src/L2-trajectories/solveInGroups.m: -------------------------------------------------------------------------------- 1 | function result = solveInGroups(opts, tracklets, labels) 2 | 3 | global trajectorySolverTime; 4 | 5 | params = opts.trajectories; 6 | if length(tracklets) < params.appearance_groups 7 | params.appearanceGroups = 1; 8 | end 9 | 10 | featureVectors = {tracklets.feature}; 11 | % adaptive number of appearance groups 12 | if params.appearance_groups == 0 13 | % Increase number of groups until no group is too large to solve 14 | while true 15 | params.appearance_groups = params.appearance_groups + 1; 16 | appearanceGroups = kmeans( cell2mat(featureVectors'), params.appearance_groups, 'emptyaction', 'singleton', 'Replicates', 10); 17 | uid = unique(appearanceGroups); 18 | freq = [histc(appearanceGroups(:),uid)]; 19 | largestGroupSize = max(freq); 20 | % The BIP solver might run out of memory for large graphs 21 | if largestGroupSize <= 150 22 | break 23 | end 24 | end 25 | else 26 | % fixed number of appearance groups 27 | appearanceGroups = kmeans( cell2mat(featureVectors'), params.appearance_groups, 'emptyaction', 'singleton', 'Replicates', 10); 28 | end 29 | % solve separately for each appearance group 30 | allGroups = unique(appearanceGroups); 31 | 32 | result_appearance = cell(1, length(allGroups)); 33 | for i = 1 : length(allGroups) 34 | 35 | fprintf('merging tracklets in appearance group %d\n',i); 36 | group = allGroups(i); 37 | indices = find(appearanceGroups == group); 38 | sameLabels = pdist2(labels(indices), labels(indices)) == 0; 39 | 40 | % compute appearance and spacetime scores 41 | appearanceAffinity = getAppearanceMatrix(featureVectors(indices), params.threshold); 42 | [spacetimeAffinity, impossibilityMatrix, indifferenceMatrix] = getSpaceTimeAffinity(tracklets(indices), params.beta, params.speed_limit, params.indifference_time); 43 | 44 | % compute the correlation matrix 45 | correlationMatrix = appearanceAffinity + spacetimeAffinity - 1; 46 | correlationMatrix = correlationMatrix .* indifferenceMatrix; 47 | 48 | correlationMatrix(impossibilityMatrix == 1) = -inf; 49 | correlationMatrix(sameLabels) = 1; 50 | 51 | % show appearance group tracklets 52 | if opts.visualize, trajectoriesVisualizePart2; end 53 | 54 | % solve the optimization problem 55 | solutionTime = tic; 56 | if strcmp(opts.optimization,'AL-ICM') 57 | result_appearance{i}.labels = AL_ICM(sparse(correlationMatrix)); 58 | elseif strcmp(opts.optimization,'KL') 59 | result_appearance{i}.labels = KernighanLin(correlationMatrix); 60 | elseif strcmp(opts.optimization,'BIPCC') 61 | initialSolution = KernighanLin(correlationMatrix); 62 | result_appearance{i}.labels = BIPCC(correlationMatrix, initialSolution); 63 | end 64 | 65 | trajectorySolutionTime = toc(solutionTime); 66 | trajectorySolverTime = trajectorySolverTime + trajectorySolutionTime; 67 | 68 | result_appearance{i}.observations = indices; 69 | end 70 | 71 | 72 | % collect independent solutions from each appearance group 73 | result.labels = []; 74 | result.observations = []; 75 | 76 | for i = 1:numel(unique(appearanceGroups)) 77 | result = mergeResults(result, result_appearance{i}); 78 | end 79 | 80 | [~,id] = sort(result.observations); 81 | result.observations = result.observations(id); 82 | result.labels = result.labels(id); 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/L2-trajectories/trackletsToTrajectories.m: -------------------------------------------------------------------------------- 1 | function trajectories = trackletsToTrajectories( tracklets, labels ) 2 | 3 | trajectories = []; 4 | 5 | uniqueLabels = unique(labels); 6 | 7 | for i = 1:length(uniqueLabels) 8 | 9 | trackletIndices = find(labels == uniqueLabels(i)); 10 | 11 | trajectory = struct('tracklets',[],'startFrame',inf,'endFrame',-inf,'segmentStart',inf,'segmentEnd',-inf); 12 | 13 | for k = 1:length(trackletIndices) 14 | ind = trackletIndices(k); 15 | trajectory.tracklets = [trajectory.tracklets; tracklets(ind)]; 16 | trajectory.startFrame = min(trajectory.startFrame, tracklets(ind).startFrame); 17 | trajectory.endFrame = max(trajectory.startFrame, tracklets(ind).endFrame); 18 | trajectory.segmentStart = min(trajectory.segmentStart, tracklets(ind).segmentStart); 19 | trajectory.segmentEnd = max(trajectory.segmentEnd, tracklets(ind).segmentEnd); 20 | trajectory.feature = tracklets(ind).feature; 21 | end 22 | 23 | trajectories = [trajectories; trajectory]; 24 | end 25 | 26 | -------------------------------------------------------------------------------- /src/L2-trajectories/trajectoriesToTop.m: -------------------------------------------------------------------------------- 1 | function data = trajectoriesToTop( trajectories ) 2 | %TRAJECTORIESTOTOP Summary of this function goes here 3 | % Detailed explanation goes here 4 | 5 | data = zeros(0,8); 6 | 7 | for i = 1:length(trajectories) 8 | 9 | traj = trajectories(i); 10 | 11 | for k = 1:length(traj.tracklets) 12 | 13 | newdata = traj.tracklets(k).data; 14 | newdata(:,2) = i; 15 | data = [data; newdata]; 16 | 17 | 18 | end 19 | 20 | end 21 | 22 | -------------------------------------------------------------------------------- /src/L2-trajectories/trajectoriesVisualizePart1.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % VISUALIZE 1: SHOW ALL TRACKLETS 3 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4 | 5 | % backgroundImage = imfuse(readFrame(dataset, max(1,startTime)),readFrame(dataset,min(endTime,dataset.endingFrame+syncTimeAcrossCameras(dataset.camera))),'blend','Scaling','joint'); 6 | startImg = opts.reader.getFrame(opts.current_camera, max(1,startTime)); 7 | endImg = opts.reader.getFrame(opts.current_camera, max(1,endTime)); 8 | backgroundImage = imfuse(startImg, endImg,'blend','Scaling','joint'); 9 | 10 | imshow(backgroundImage); 11 | 12 | hold on; 13 | 14 | trCount = 0; 15 | for k = 1 : length(currentTrajectories) 16 | 17 | for i = 1:length(currentTrajectories(k).tracklets) 18 | trCount = trCount +1; 19 | detections = currentTrajectories(k).tracklets(i).data; 20 | trackletCentersView = getBoundingBoxCenters(detections(:,[3:6])); 21 | scatter(trackletCentersView(:,1),trackletCentersView(:,2),'filled'); 22 | total = size(trackletCentersView,1); 23 | text(trackletCentersView(round(total/2),1),trackletCentersView(round(total/2),2)+0.01,sprintf('(%d,%d),I:%d,F:%d)',k,i,trCount,min(detections(:,1)))); 24 | hold on; 25 | 26 | end 27 | 28 | end 29 | 30 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 31 | 32 | -------------------------------------------------------------------------------- /src/L2-trajectories/trajectoriesVisualizePart2.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % DRAW RELATIONSHIPS WITHIN EACH APPEARANCE GROUP 3 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4 | 5 | for kk = 1 : max(appearanceGroups) 6 | 7 | relevantTracklets = indices; 8 | trackletCentersViewTmp = []; 9 | 10 | for iii = 1 : length(relevantTracklets) 11 | data = cell2mat({tracklets(relevantTracklets(iii)).data}); 12 | centerIndex = 1 + ceil(size(data,1)/2); 13 | 14 | trackletCentersViewTmp = [trackletCentersViewTmp; getBoundingBoxCenters(data(centerIndex, 3 : 6))]; %#ok 15 | end 16 | 17 | pairsLocal = combnk(1:length(relevantTracklets),2); 18 | pairsWindow = combnk(relevantTracklets,2); 19 | 20 | pairs_i = pairsLocal(:,1); 21 | pairs_j = pairsLocal(:,2); 22 | 23 | points1 = trackletCentersViewTmp(pairs_i,:); 24 | points2 = trackletCentersViewTmp(pairs_j,:); 25 | 26 | myColorMap = NegativeEnhancingColormap(128, [-1 1], [0 0 1], [1 0 0], 1); 27 | 28 | if size(pairsLocal, 1) >2 29 | 30 | for p = 1 : length(pairsLocal) 31 | 32 | pts = [ points1(p,:); points2(p,:) ]; 33 | correlation = correlationMatrix(pairsLocal(p,1),pairsLocal(p,2)); 34 | 35 | linecolor = [0 1 0]; 36 | 37 | if correlation == -inf || correlation == 10000 || correlation == 0 38 | continue; 39 | end 40 | 41 | if correlation == 10000 || correlation > 1 42 | linecolor = [0 1 0]; 43 | end 44 | 45 | if correlation >= -10 && correlation <=10 46 | colorindex = int32( 1 + (1 + correlation) * 63 ); 47 | colorindex = max(colorindex,1); 48 | colorindex = min(colorindex, 128); 49 | linecolor = myColorMap(colorindex,:); 50 | end 51 | 52 | line(pts(:,1),pts(:,2),'color',linecolor); 53 | hold on; 54 | 55 | end 56 | 57 | pause(0.1); 58 | 59 | end 60 | 61 | end 62 | 63 | hold off; 64 | 65 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -------------------------------------------------------------------------------- /src/L2-trajectories/trajectoriesVisualizePart3.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % VISUALIZE 3: SHOW ALL MERGED TRACKLETS IN WINDOWS 3 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4 | 5 | figure, imshow(opts.reader.getFrame(opts.current_camera,endTime)); 6 | hold on; 7 | 8 | currentTrajectories = smoothTrajectories; 9 | numTrajectories = length(currentTrajectories); 10 | 11 | colors = distinguishable_colors(numTrajectories); 12 | for k = 1:numTrajectories 13 | 14 | 15 | for i = 1 : length(currentTrajectories(k).tracklets) 16 | 17 | detections = currentTrajectories(k).tracklets(i).data; 18 | trackletCentersView = getBoundingBoxCenters(detections(:, 3:6)); 19 | 20 | plot(trackletCentersView(:,1),trackletCentersView(:,2),'LineWidth',4,'Color',colors(k,:)); 21 | hold on; 22 | 23 | end 24 | 25 | end 26 | 27 | hold off; 28 | drawnow; 29 | 30 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 31 | -------------------------------------------------------------------------------- /src/L3-identities/getSpaceTimeAffinityL3.m: -------------------------------------------------------------------------------- 1 | function [motionMatrix, impossibilityMatrix, indifferenceMatrix] = getSpaceTimeAffinityMC(trajectories) 2 | 3 | numTrajectories = length(trajectories); 4 | 5 | % constants 6 | 7 | 8 | % parameters 9 | par_indifference = 6000; 10 | speedLimit = 2.5; 11 | frameRate = 60000/1001; 12 | 13 | %% create binary feasibility matrix based on speed and direction 14 | feasibilityMatrix = zeros(numTrajectories); 15 | for idx1 = 1 : numTrajectories-1 16 | for idx2 = idx1+1 : numTrajectories 17 | 18 | % temporal ordering is required here (A observed before B) 19 | if trajectories(idx1).data(1, 9) < trajectories(idx2).data(1, 9) 20 | A = trajectories(idx1); 21 | B = trajectories(idx2); 22 | else 23 | A = trajectories(idx2); 24 | B = trajectories(idx1); 25 | end 26 | if ~isempty(intersect(A.data(:,9),B.data(:,9))) 27 | continue; 28 | end 29 | 30 | % compute required number of frames 31 | distance = sqrt(sum((A.data(end, [7 8]) - B.data(1, [7 8])).^2)); 32 | frames_betw = abs(B.data(1, 9) - A.data(end, 9)); 33 | min_number_of_required_frames = distance / speedLimit * frameRate; 34 | 35 | % compute directional information 36 | L1 = sqrt(sum((A.data(end, [7 8]) - B.data( 1, [7 8])).^2)); 37 | L2 = sqrt(sum((A.data(end, [7 8]) - B.data(end, [7 8])).^2)); 38 | L3 = sqrt(sum((A.data( 1, [7 8]) - B.data( 1, [7 8])).^2)); 39 | 40 | if frames_betw > min_number_of_required_frames && L1 < L2 && L1 < L3 && ~isequal(A.camera, B.camera) 41 | feasibilityMatrix(idx1,idx2) = 1; % feasible association 42 | end 43 | 44 | end 45 | end 46 | feasibilityMatrix = feasibilityMatrix + feasibilityMatrix'; 47 | impossibilityMatrix = ~feasibilityMatrix; 48 | 49 | %% motion information 50 | motionMatrix = zeros(numTrajectories); 51 | for idx1 = 1 : numTrajectories-1 52 | for idx2 = idx1+1 : numTrajectories 53 | 54 | % temporal ordering is required here 55 | if trajectories(idx1).data(1, 9) < trajectories(idx2).data(1, 9) 56 | A = trajectories(idx1); 57 | B = trajectories(idx2); 58 | else 59 | A = trajectories(idx2); 60 | B = trajectories(idx1); 61 | end 62 | frame_difference = abs(B.data(1, 9) - A.data(end, 9)); % it could happen to be negative in overlapping camera settings 63 | space_difference = sqrt(sum((B.data(1, [7 8]) - A.data(end, [7 8])).^2, 2)); 64 | needed_speed = space_difference / frame_difference; %mpf 65 | speedA = sqrt(sum(diff(A.data(:, [7 8])).^2, 2)); 66 | speedA = mean(speedA(max([2, end-9]):end)); 67 | speedB = sqrt(sum(diff(B.data(:, [7 8])).^2, 2)); 68 | speedB = mean(speedB(1:min([numel(speedB)-1, 10]))); 69 | motionMatrix(idx1, idx2) = sigmf(min([abs(speedA-needed_speed), abs(speedB-needed_speed)]), [5 0])-0.53; 70 | 71 | end 72 | end 73 | motionMatrix = motionMatrix + motionMatrix'; 74 | 75 | %% indifference matrix 76 | indifferenceMatrix = zeros(numTrajectories); 77 | frame_difference = zeros(numTrajectories); 78 | for idx1 = 1 : numTrajectories-1 79 | for idx2 = idx1+1 : numTrajectories 80 | 81 | % temporal ordering is required here 82 | if trajectories(idx1).data(1, 9) < trajectories(idx2).data(1, 9) 83 | A = trajectories(idx1); 84 | B = trajectories(idx2); 85 | else 86 | A = trajectories(idx2); 87 | B = trajectories(idx1); 88 | end 89 | frame_difference(idx1, idx2) = max(0, B.data(1, 9) - A.data(end, 9)); % it could happen to be negative in overlapping camera settings 90 | indifferenceMatrix(idx1,idx2) = sigmf(frame_difference(idx1,idx2), [0.001 par_indifference/2]); 91 | 92 | end 93 | end 94 | indifferenceMatrix = indifferenceMatrix + indifferenceMatrix'; 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /src/L3-identities/getTrajectoryFeatures.m: -------------------------------------------------------------------------------- 1 | function trajectories = getTrajectoryFeatures(opts, trajectories) 2 | 3 | % Gather bounding boxes for this trajectory 4 | detections = []; 5 | csvfile = sprintf('%s/%s/L3-identities/temp_images_%s/L2images.csv',opts.experiment_root, opts.experiment_name, opts.sequence_names{opts.sequence}); 6 | 7 | 8 | if opts.identities.extract_images 9 | temp_dir = sprintf('%s/%s/L3-identities/temp_images_%s',opts.experiment_root, opts.experiment_name, opts.sequence_names{opts.sequence}); 10 | status = rmdir(temp_dir,'s'); 11 | mkdir(temp_dir); 12 | fid = fopen(csvfile,'w'); 13 | end 14 | 15 | for i = 1:length(trajectories) 16 | 17 | 18 | % For every trajectory, extract one image every k frames 19 | inds = round(linspace(1,size(trajectories(i).trajectories(1).data,1),10)); 20 | 21 | if opts.identities.extract_images 22 | fprintf('Extracting imgages for trajectory %d/%d\n', i, length(trajectories)); 23 | mkdir(sprintf('%s/%s/L3-identities/temp_images_%s/%05d/',opts.experiment_root, opts.experiment_name, opts.sequence_names{opts.sequence}, i)); 24 | end 25 | 26 | for k=1:length(inds) 27 | bb = trajectories(i).trajectories(1).data(inds(k),3:6); 28 | frame = trajectories(i).trajectories(1).data(inds(k),1); 29 | camera = trajectories(i).trajectories(1).camera; 30 | if opts.identities.extract_images 31 | img = opts.reader.getFrame(camera, frame); 32 | snapshot = get_bb(img,bb); 33 | snapshot = imresize(snapshot,[opts.net.input_height, opts.net.input_width]); 34 | % trajectories(i).trajectories(1).snapshot{k} = snapshot; 35 | filename = sprintf('%s/%s/L3-identities/temp_images_%s/%05d/img_%d_%d.jpg',opts.experiment_root, opts.experiment_name, opts.sequence_names{opts.sequence} , i,i,k); 36 | imwrite(snapshot, filename); 37 | fprintf(fid,'%05d,%s\n',k,filename); 38 | end 39 | detections = [detections; camera, frame, bb, i, k]; 40 | end 41 | end 42 | if opts.identities.extract_images 43 | fclose(fid); 44 | end 45 | 46 | % Compute features 47 | % features = embed_detections(opts, detections); 48 | net = opts.net; 49 | cur_dir = pwd; 50 | cd src/triplet-reid 51 | featuresfile = sprintf('%s/%s/L3-identities/L2features_%s.h5',opts.experiment_root, opts.experiment_name, opts.sequence_names{opts.sequence}); 52 | 53 | command = strcat(opts.python3, ' embed.py' , ... 54 | sprintf(' --experiment_root %s', net.experiment_root), ... 55 | sprintf(' --image_root %s', fullfile(cur_dir)), ... 56 | sprintf(' --filename L2features_%s.h5', opts.sequence_names{opts.sequence}), ... 57 | sprintf(' --dataset ../../%s', csvfile)); 58 | fprintf(command); 59 | system(command); 60 | cd(cur_dir); 61 | movefile(sprintf('src/triplet-reid/%s/L2features_%s.h5',opts.net.experiment_root, opts.sequence_names{opts.sequence}),featuresfile); 62 | 63 | features = h5read(featuresfile, '/emb'); 64 | features = features'; 65 | 66 | % Assign features to trajectories 67 | ids = unique(detections(:,7)); 68 | for i = 1:length(ids) 69 | id = ids(i); 70 | trajectory_features = features(detections(:,7)==id,:); 71 | % Trajectory feature is the average of all features (if more than one image) 72 | trajectories(id).trajectories(1).feature = mean(trajectory_features,1); 73 | end 74 | 75 | -------------------------------------------------------------------------------- /src/L3-identities/linkIdentities.m: -------------------------------------------------------------------------------- 1 | function outputIdentities = linkIdentities( opts, inputIdentities, startTime, endTime) 2 | 3 | 4 | % find current, old, and future tracklets 5 | currentIdentitiesInd = findTrajectoriesInWindow(inputIdentities, startTime, endTime); 6 | currentIdentities = inputIdentities(currentIdentitiesInd); 7 | 8 | % safety check 9 | if length(currentIdentities) <= 1 10 | outputIdentities = inputIdentities; 11 | return; 12 | end 13 | 14 | % select tracklets that will be selected in association. For previously 15 | % computed trajectories we select only the last three tracklets. 16 | inAssociation = []; trajectories = []; trajectoryLabels = []; 17 | for i = 1 : length(currentIdentities) 18 | for k = 1 : length(currentIdentities(i).trajectories) 19 | trajectories = [trajectories; currentIdentities(i).trajectories(k)]; %#ok 20 | trajectoryLabels = [trajectoryLabels; i]; %#ok 21 | 22 | inAssociation(length(trajectoryLabels)) = false; %#ok 23 | if k >= length(currentIdentities(i).trajectories) - 5 24 | inAssociation(length(trajectoryLabels)) = true; %#ok 25 | end 26 | 27 | end 28 | end 29 | inAssociation = logical(inAssociation); 30 | 31 | % show all tracklets 32 | % if VISUALIZE, trajectoriesVisualizePart1; end 33 | 34 | % solve the graph partitioning problem for each appearance group 35 | result = solveInGroupsIdentities(opts, trajectories(inAssociation), trajectoryLabels(inAssociation)); 36 | 37 | % merge back solution. Tracklets that were associated are now merged back 38 | % with the rest of the tracklets that were sharing the same trajectory 39 | labels = trajectoryLabels; 40 | labels(inAssociation) = result.labels; 41 | count = 1; 42 | for i = 1 : length(inAssociation) 43 | if inAssociation(i) > 0 44 | labels(trajectoryLabels == trajectoryLabels(i)) = result.labels(count); 45 | count = count + 1; 46 | end 47 | end 48 | 49 | % Merge 50 | mergedIdentities = []; 51 | uniqueLabels = unique(labels); 52 | for i = 1:length(uniqueLabels) 53 | label = uniqueLabels(i); 54 | inds = find(labels == label); 55 | identity = []; 56 | identity.startFrame = Inf; 57 | identity.endFrame = -Inf; 58 | for k = 1:length(inds) 59 | identity.trajectories(k) = trajectories(inds(k)); 60 | identity.startFrame = min(identity.startFrame, identity.trajectories(k).startFrame); 61 | identity.endFrame = max(identity.endFrame, identity.trajectories(k).endFrame); 62 | end 63 | identity.trajectories = sortStruct(identity.trajectories,'startFrame'); 64 | 65 | 66 | mergedIdentities = [mergedIdentities; identity]; 67 | 68 | data = []; 69 | for k = 1:length(identity.trajectories) 70 | data = [data; identity.trajectories(k).data]; 71 | end 72 | frames = unique(data(:,9)); 73 | assert(length(frames) == size(data,1), 'Found duplicate ID/Frame pairs'); 74 | 75 | end 76 | 77 | % merge co-identified trajectories 78 | outputIdentities = inputIdentities; 79 | outputIdentities(currentIdentitiesInd) = []; 80 | outputIdentities = [mergedIdentities', outputIdentities]; 81 | 82 | -------------------------------------------------------------------------------- /src/L3-identities/loadL2trajectories.m: -------------------------------------------------------------------------------- 1 | function trajectories = loadL2trajectories(opts) 2 | % Loads single camera trajectories that have been written to disk 3 | trajectories = []; 4 | count = 1; 5 | for iCam = 1:opts.num_cam 6 | tracker_output = dlmread(fullfile(opts.experiment_root, opts.experiment_name, 'L2-trajectories', sprintf('cam%d_%s.txt',iCam, opts.sequence_names{opts.sequence}))); 7 | 8 | ids = unique(tracker_output(:,2)); 9 | 10 | for idx = 1:length(ids) 11 | id = ids(idx); 12 | trajectory = []; 13 | trajectory.data = tracker_output(tracker_output(:,2)==id,:); 14 | feet_world = image2world(feetPosition(trajectory.data(:,[3:6])), iCam); 15 | trajectory.data(:,[7,8]) = feet_world; 16 | trajectory.mcid = count; 17 | trajectory.scid = id; 18 | trajectory.camera = iCam; 19 | trajectory.startFrame = min(trajectory.data(:,1)); 20 | trajectory.endFrame = max(trajectory.data(:,1)); 21 | trajectories(count).trajectories = trajectory; 22 | count = count + 1; 23 | 24 | end 25 | end 26 | 27 | -------------------------------------------------------------------------------- /src/L3-identities/solveInGroupsIdentities.m: -------------------------------------------------------------------------------- 1 | function result = solveInGroupsIdentities(opts, trajectories, labels, VISUALIZE) 2 | 3 | global identitySolverTime; 4 | 5 | params = opts.identities; 6 | if length(trajectories) < params.appearance_groups 7 | params.appearanceGroups = 1; 8 | end 9 | 10 | % adaptive number of appearance groups 11 | if params.appearance_groups == 0 12 | params.appearance_groups = 1 + floor(length(trajectories)/120); 13 | end 14 | 15 | % fixed number of appearance groups 16 | featureVectors_tmp = double(cell2mat({trajectories.feature}')); 17 | uniqueLabels = unique(labels); 18 | for k = 1:length(uniqueLabels) 19 | label = uniqueLabels(k); 20 | inds = find(labels == label); 21 | meanVector = mean(featureVectors_tmp(inds,:),1); 22 | featureVectors_tmp(inds,:) = repmat(meanVector, length(inds),1); 23 | end 24 | 25 | featureVectors = {trajectories.feature}; 26 | 27 | for k = 1:length(featureVectors) 28 | featureVectors{k} = featureVectors_tmp(k,:); 29 | end 30 | 31 | appearanceGroups = kmeans( cell2mat(featureVectors'), params.appearance_groups, 'emptyaction', 'singleton', 'Replicates', 10); 32 | 33 | % solve separately for each appearance group 34 | allGroups = unique(appearanceGroups); 35 | 36 | % figure; 37 | % for k = 1:length(trajectories) 38 | % subplot(1,length(trajectories),k); 39 | % imshow(trajectories(k).snapshot); 40 | % end 41 | 42 | 43 | group_results = cell(1, length(allGroups)); 44 | for i = 1 : length(allGroups) 45 | 46 | fprintf('merging trajectories in appearance group %d\n',i); 47 | group = allGroups(i); 48 | indices = find(appearanceGroups == group); 49 | sameLabels = pdist2(labels(indices), labels(indices)) == 0; 50 | 51 | % compute appearance and spacetime scores 52 | appearanceCorrelation = getAppearanceMatrix(featureVectors(indices), params.threshold); 53 | [spacetimeAffinity, impossibilityMatrix, indifferenceMatrix] = getSpaceTimeAffinityL3(trajectories(indices)); 54 | correlationMatrix = ... 55 | 1 * appearanceCorrelation + ... 56 | 5 * (spacetimeAffinity).*(1-indifferenceMatrix)-0.1; 57 | correlationMatrix = ... 58 | 1 * appearanceCorrelation + ... 59 | (spacetimeAffinity).*(1-indifferenceMatrix); 60 | 61 | 62 | correlationMatrix(impossibilityMatrix) = -Inf; 63 | 64 | 65 | correlationMatrix(sameLabels) = 100; 66 | 67 | % show appearance group tracklets 68 | % if VISUALIZE, trajectoriesVisualizePart2; end 69 | 70 | % solve the optimization problem 71 | solutionTime = tic; 72 | 73 | if strcmp(opts.optimization,'AL-ICM') 74 | group_results{i}.labels = AL_ICM(sparse(correlationMatrix)); 75 | elseif strcmp(opts.optimization,'KL') 76 | group_results{i}.labels = KernighanLin(correlationMatrix); 77 | elseif strcmp(opts.optimization,'BIPCC') 78 | initialSolution = KernighanLin(correlationMatrix); 79 | group_results{i}.labels = BIPCC(correlationMatrix,initialSolution); 80 | end 81 | 82 | identitySolutionTime = toc(solutionTime); 83 | identitySolverTime = identitySolverTime + identitySolutionTime; 84 | 85 | group_results{i}.observations = indices; 86 | end 87 | 88 | 89 | % collect independent solutions from each appearance group 90 | result.labels = []; 91 | result.observations = []; 92 | 93 | for i = 1:numel(unique(appearanceGroups)) 94 | result = mergeResults(result, group_results{i}); 95 | end 96 | 97 | [~,id] = sort(result.observations); 98 | result.observations = result.observations(id); 99 | result.labels = result.labels(id); 100 | 101 | 102 | -------------------------------------------------------------------------------- /src/L3-identities/trajectoriesToIdentities.m: -------------------------------------------------------------------------------- 1 | function identities = trajectoriesToIdentities( trajectories, labels ) 2 | 3 | identities = []; 4 | 5 | uniqueLabels = unique(labels); 6 | 7 | for i = 1:length(uniqueLabels) 8 | 9 | trajectoryIndices = find(labels == uniqueLabels(i)); 10 | 11 | trajectory = struct('trajectories',[],'startFrame',inf,'endFrame',-inf); 12 | 13 | for k = 1:length(trajectoryIndices) 14 | ind = trajectoryIndices(k); 15 | trajectory.trajectories = [trajectory.trajectories; trajectories(ind)]; 16 | trajectory.startFrame = min(trajectory.startFrame, trajectories(ind).startFrame); 17 | trajectory.endFrame = max(trajectory.startFrame, trajectories(ind).endFrame); 18 | % trajectory.segmentStart = min(trajectory.segmentStart, trajectories(ind).segmentStart); 19 | % trajectory.segmentEnd = max(trajectory.segmentEnd, trajectories(ind).segmentEnd); 20 | % trajectory.feature = trajectories(ind).feature; 21 | end 22 | 23 | identities = [identities; trajectory]; 24 | end 25 | 26 | -------------------------------------------------------------------------------- /src/compute_L0_features.m: -------------------------------------------------------------------------------- 1 | function compute_L0_features(opts) 2 | % Computes features for the input poses 3 | 4 | for iCam = 1:opts.num_cam 5 | 6 | % Load poses 7 | load(fullfile(opts.dataset_path, 'detections','openpose', sprintf('camera%d.mat',iCam))); 8 | poses = detections; 9 | 10 | % Convert poses to detections 11 | detections = zeros(size(poses,1),6); 12 | for k = 1:size(poses,1) 13 | pose = poses(k,3:end); 14 | bb = pose2bb(pose, opts.render_threshold); 15 | [newbb, newpose] = scale_bb(bb,pose,1.25); 16 | detections(k,:) = [iCam, poses(k,2), newbb]; 17 | end 18 | 19 | % Compute feature embeddings 20 | features = embed_detections(opts,detections); 21 | 22 | % Save features 23 | h5write(sprintf('%s/%s/L0-features/features%d.h5',opts.experiment_root,opts.experiment_name,iCam),'/emb', features); 24 | end 25 | 26 | -------------------------------------------------------------------------------- /src/compute_L1_tracklets.m: -------------------------------------------------------------------------------- 1 | function compute_L1_tracklets(opts) 2 | % Computes tracklets for all cameras 3 | 4 | for iCam = 1:8 5 | 6 | opts.current_camera = iCam; 7 | 8 | sequence_window = opts.sequence_intervals{opts.sequence}; 9 | start_frame = global2local(opts.start_frames(opts.current_camera), sequence_window(1)); 10 | end_frame = global2local(opts.start_frames(opts.current_camera), sequence_window(end)); 11 | 12 | % Load OpenPose detections for current camera 13 | load(fullfile(opts.dataset_path, 'detections','OpenPose', sprintf('camera%d.mat',iCam))); 14 | 15 | % Load features for all detections 16 | features = h5read(sprintf('%s/%s/L0-features/features%d.h5',opts.experiment_root,opts.experiment_name,iCam),'/emb'); 17 | features = double(features'); 18 | all_dets = detections; 19 | appearance = cell(size(all_dets,1),1); 20 | frames = cell(size(all_dets,1),1); 21 | for k = 1:length(frames) 22 | appearance{k} = features(k,:); 23 | frames{k} = detections(k,2); 24 | end 25 | 26 | % Compute tracklets for every 1-second interval 27 | tracklets = struct([]); 28 | 29 | for window_start_frame = start_frame : opts.tracklets.window_width : end_frame 30 | fprintf('%d/%d\n', window_start_frame, end_frame); 31 | 32 | % Retrieve detections in current window 33 | window_end_frame = window_start_frame + opts.tracklets.window_width - 1; 34 | window_frames = window_start_frame : window_end_frame; 35 | window_inds = find(ismember(all_dets(:,2),window_frames)); 36 | detections_in_window = all_dets(window_inds,:); 37 | detections_conf = sum(detections_in_window(:,5:3:end),2); 38 | num_visible = sum(detections_in_window(:,5:3:end)> opts.render_threshold, 2); 39 | 40 | % Use only valid detections 41 | [valid, detections_in_window] = getValidDetections(detections_in_window, detections_conf, num_visible, opts, iCam); 42 | detections_in_window = detections_in_window(valid,:); 43 | detections_in_window(:,7:end) = []; 44 | detections_in_window(:,[1 2]) = detections_in_window(:,[2 1]); 45 | filteredDetections = detections_in_window; 46 | filteredFeatures = []; 47 | filteredFeatures.appearance = appearance(window_inds(valid)); 48 | 49 | % Compute tracklets in current window 50 | % Then add them to the list of all tracklets 51 | tracklets = createTracklets(opts, filteredDetections, filteredFeatures, window_start_frame, window_end_frame, tracklets); 52 | end 53 | 54 | % Save tracklets 55 | save(sprintf('%s/%s/L1-tracklets/tracklets%d_%s.mat', ... 56 | opts.experiment_root, ... 57 | opts.experiment_name, ... 58 | iCam, ... 59 | opts.sequence_names{opts.sequence}), ... 60 | 'tracklets'); 61 | 62 | % Clean up 63 | clear all_dets appearance detections features frames 64 | 65 | end 66 | -------------------------------------------------------------------------------- /src/compute_L2_trajectories.m: -------------------------------------------------------------------------------- 1 | function compute_L2_trajectories(opts) 2 | % Computes single-camera trajectories from tracklets 3 | for iCam = 1:8 4 | 5 | % Initialize 6 | load(fullfile(opts.experiment_root, opts.experiment_name, 'L1-tracklets', sprintf('tracklets%d_%s.mat',iCam,opts.sequence_names{opts.sequence}))); 7 | trajectoriesFromTracklets = trackletsToTrajectories(tracklets,1:length(tracklets)); 8 | 9 | opts.current_camera = iCam; 10 | sequence_interval = opts.sequence_intervals{opts.sequence}; 11 | startFrame = global2local(opts.start_frames(opts.current_camera), sequence_interval(1) - opts.trajectories.window_width); 12 | endFrame = global2local(opts.start_frames(opts.current_camera), sequence_interval(1) + opts.trajectories.window_width); 13 | 14 | trajectories = trajectoriesFromTracklets; 15 | 16 | while startFrame <= global2local(opts.start_frames(opts.current_camera), sequence_interval(end)) 17 | % Display loop state 18 | clc; fprintf('Cam: %d - Window %d...%d\n', iCam, startFrame, endFrame); 19 | 20 | % Compute trajectories in current time window 21 | trajectories = createTrajectories( opts, trajectories, startFrame, endFrame); 22 | 23 | % Update loop range 24 | startFrame = endFrame - opts.trajectories.overlap; 25 | endFrame = startFrame + opts.trajectories.window_width; 26 | end 27 | 28 | % Convert trajectories 29 | trackerOutputRaw = trajectoriesToTop(trajectories); 30 | % Interpolate missing detections 31 | trackerOutputFilled = fillTrajectories(trackerOutputRaw); 32 | % Remove spurius tracks 33 | trackerOutputRemoved = removeShortTracks(trackerOutputFilled, opts.minimum_trajectory_length); 34 | % Make identities 1-indexed 35 | [~, ~, ic] = unique(trackerOutputRemoved(:,2)); 36 | trackerOutputRemoved(:,2) = ic; 37 | trackerOutput = sortrows(trackerOutputRemoved,[2 1]); 38 | 39 | %% Save trajectories 40 | fprintf('Saving results\n'); 41 | fileOutput = trackerOutput(:, [1:6]); 42 | dlmwrite(sprintf('%s/%s/L2-trajectories/cam%d_%s.txt', ... 43 | opts.experiment_root, ... 44 | opts.experiment_name, ... 45 | iCam, ... 46 | opts.sequence_names{opts.sequence}), ... 47 | fileOutput, 'delimiter', ' ', 'precision', 6); 48 | 49 | 50 | end 51 | end -------------------------------------------------------------------------------- /src/compute_L3_identities.m: -------------------------------------------------------------------------------- 1 | function compute_L3_identities(opts) 2 | % Computes multi-camera trajectories from single-camera trajectories 3 | 4 | trajectories = loadL2trajectories(opts); 5 | trajectories = getTrajectoryFeatures(opts, trajectories); 6 | filename = sprintf('%s/%s/L3-identities/L2trajectories_%s.mat',opts.experiment_root, opts.experiment_name, opts.sequence_names{opts.sequence}); 7 | save(filename,'trajectories'); 8 | % load(filename); 9 | identities = trajectories; 10 | 11 | for k = 1:length(identities) 12 | identities(k).trajectories(1).data(:,end+1) = local2global(opts.start_frames(identities(k).trajectories(1).camera) ,identities(k).trajectories(1).data(:,1)); 13 | identities(k).trajectories(1).startFrame = identities(k).trajectories(1).data(1,9); 14 | identities(k).startFrame = identities(k).trajectories(1).startFrame; 15 | identities(k).trajectories(1).endFrame = identities(k).trajectories(1).data(end,9); 16 | identities(k).endFrame = identities(k).trajectories(1).endFrame; 17 | end 18 | identities = sortStruct(identities,'startFrame'); 19 | 20 | global_interval = opts.sequence_intervals{opts.sequence}; 21 | startFrame = global_interval(1); 22 | endFrame = global_interval(1) + opts.identities.window_width - 1; 23 | 24 | while startFrame <= global_interval(end) 25 | clc; fprintf('Window %d...%d\n', startFrame, endFrame); 26 | 27 | identities = linkIdentities(opts, identities, startFrame, endFrame); 28 | 29 | % advance sliding temporal window 30 | startFrame = endFrame - opts.identities.window_width/2; 31 | endFrame = startFrame + opts.identities.window_width; 32 | end 33 | %% 34 | fprintf('Saving results\n'); 35 | trackerOutputL3 = identities2mat(identities); 36 | for iCam = 1:opts.num_cam 37 | cam_data = trackerOutputL3(trackerOutputL3(:,1) == iCam,2:end); 38 | dlmwrite(sprintf('%s/%s/L3-identities/cam%d_%s.txt', ... 39 | opts.experiment_root, ... 40 | opts.experiment_name, ... 41 | iCam, ... 42 | opts.sequence_names{opts.sequence}), ... 43 | cam_data, 'delimiter', ' ', 'precision', 6); 44 | end -------------------------------------------------------------------------------- /src/correlation_clustering/BIPCC/BIPCC.m: -------------------------------------------------------------------------------- 1 | function labels = BIPCC( correlationMatrix, initialGuess ) 2 | % Solves a graph partitioning problem as a Binary Integer Program 3 | % 4 | % Input 5 | % correlationMatrix 6 | % initialGuess - initial guess label vector (optional) 7 | % 8 | % Output 9 | % labels - optimal label vector 10 | % 11 | % Ergys Ristani 12 | % Duke University 13 | 14 | [f, A, b] = createBIP(correlationMatrix); 15 | 16 | [X, ~] = graphPartitioning(f, A, b, correlationMatrix, initialGuess); 17 | 18 | % Create a label vector from the BIP solution 19 | 20 | labels = labelsFromAdjacency(X, correlationMatrix); 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/correlation_clustering/BIPCC/createBIP.m: -------------------------------------------------------------------------------- 1 | function [ f, A, b ] = createBIP( W ) 2 | % Creates a Binary Integer Program for a given correlation matrix 3 | % 4 | % Input 5 | % W - correlation matrix 6 | % 7 | % Output 8 | % f - objective function 9 | % A, b - constraints of type Ax <= b 10 | % 11 | % Ergys Ristani 12 | % Duke University 13 | 14 | fprintf('Num vars: %d\n\n', size(W,1)); 15 | 16 | constraintsTime = tic; 17 | 18 | N = size(W,1); 19 | 20 | % f is the objective function f = sum{W} w_uv * x_uv 21 | f = zeros(N*(N-1)/2,1); 22 | 23 | % pairs - a list of all edges (u,v), v>u 24 | pairs = zeros(N,N-1); 25 | 26 | pos = 1; 27 | for i = 1:N-1 28 | for j = i + 1:N 29 | pairs(i, j) = pos; 30 | f(pos) = W(i,j); 31 | pos = pos + 1; 32 | end 33 | end 34 | 35 | % Constraint matrix x_uv + x_ut - x_vt <= 1 represented by 36 | % Ax<=b 37 | 38 | % Computie all possible triples between observations 39 | 40 | if N>=3 41 | 42 | triples = combinator(N,3,'c','no'); % Equivalent to combnk(1:N,3) but faster 43 | 44 | % Translate triples to observation unique IDs 45 | % Observation (i,j) has pairs(i,j) as unique ID 46 | 47 | idx = zeros(size(triples)); 48 | 49 | idx(:,1) = pairs(sub2ind(size(pairs), triples(:,1),triples(:,2))); 50 | idx(:,2) = pairs(sub2ind(size(pairs), triples(:,1),triples(:,3))); 51 | idx(:,3) = pairs(sub2ind(size(pairs), triples(:,2),triples(:,3))); 52 | 53 | % Express x_uv + x_ut - x_vt <= 1 as three constraints by permutation 54 | idx_x3 = kron(idx,[1 1 1]'); 55 | permutations = idx_x3; 56 | permutations(2:3:end,:)=circshift(idx_x3(2:3:end,:)',1)'; 57 | permutations(3:3:end,:)=circshift(idx_x3(3:3:end,:)',2)'; 58 | 59 | idx = permutations'; 60 | 61 | rows = kron(1:(size(triples,1)*3),[1,1,1])'; 62 | cols = idx(:); 63 | values = kron(ones(size(triples,1)*3,1),[1 1 -1]'); 64 | 65 | % A - constraints matrix 66 | A = sparse(rows,cols,values); 67 | b = ones(size(A,1),1); 68 | 69 | % All columns in A belonging to -inf assignments are removed for 70 | % memory efficiency. This affects how the partial solution is built 71 | % in correlationClustering.m 72 | infIndices = pairs(W==-inf); 73 | infIndices(infIndices==0) = []; 74 | 75 | A(:,infIndices) = []; 76 | f(infIndices) = []; 77 | 78 | elseif N == 2 79 | 80 | % No constraints 81 | A = sparse([0]); 82 | b = [0]; 83 | 84 | else 85 | 86 | % N is 0 or 1 87 | A = sparse([0]); 88 | b = [0]; 89 | f = W; 90 | end 91 | 92 | fprintf('Assembling BIP. '); toc(constraintsTime); 93 | fprintf('Number of constraints: %d\n\n', size(A,1)); 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /src/correlation_clustering/BIPCC/graphPartitioning.m: -------------------------------------------------------------------------------- 1 | function [ X , objval ] = graphPartitioning( f, A, b, correlationMatrix, initialGuess ) 2 | % Finds the optimal partitioning of a correlation matrix 3 | % 4 | % Input: 5 | % f - the objective function to maximize 6 | % sum w_uv * x_uv 7 | % A, b - constraints of type Ax <= b 8 | % correlationMatrix 9 | % initialGuess (optinal) 10 | % 11 | % Output: 12 | % X - solution in the form of an upper triangular matrix in vector form 13 | % objval - value of the objective 14 | % 15 | % Ergys Ristani 16 | % Duke University 17 | 18 | partialSolution = f; 19 | partialSolution(f == -inf) = 0; 20 | partialSolution(f ~= -inf) = NaN; 21 | 22 | if ~isempty(initialGuess) 23 | N = length(initialGuess); 24 | pos = 1; 25 | for i = 1:N-1 26 | for j = i + 1:N 27 | if correlationMatrix(i,j) == -inf 28 | continue; 29 | end 30 | partialSolution(pos) = initialGuess(i) == initialGuess(j); 31 | pos = pos + 1; 32 | end 33 | end 34 | end 35 | 36 | % Gurobi solver parameters 37 | clear model; 38 | model.obj = f; 39 | model.A = sparse(A); 40 | model.sense = '<'; 41 | model.rhs = b; 42 | model.vtype = 'B'; % binary 43 | model.modelsense = 'max'; 44 | model.start = partialSolution; 45 | model.norelheuristic = 1; 46 | clear params; 47 | 48 | params = struct(); 49 | params.Presolve = 0; 50 | % params.CliqueCuts = 2; 51 | %params.outputflag = 0; % Silence gurobi 52 | % params.NodefileStart = 0.1; 53 | % params.TimeLimit = 10; 54 | 55 | result = gurobi(model, params); 56 | objval = result.objval; 57 | X = [result.x]; 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/correlation_clustering/BIPCC/labelsFromAdjacency.m: -------------------------------------------------------------------------------- 1 | function [ labels ] = labelsFromAdjacency( X, W ) 2 | % Transforms the Binary Integer Program solution vector to a label vector 3 | % 4 | % Input 5 | % X - solution vector representing an upper triangular matrix 6 | % W - correlation matrix 7 | % 8 | % Output 9 | % labels - label vector 10 | % 11 | % Ergys Ristani 12 | % Duke University 13 | 14 | labels = [1:size(W,1)]'; 15 | pos = 1; 16 | 17 | for i = 1:size(W,1) 18 | 19 | for j = i + 1:size(W,1) 20 | 21 | % Skip the -inf assignments that were previously removed from the 22 | % Binary Integer Program (createILP.m) for memory efficiency 23 | if W(i,j)== -inf 24 | continue; 25 | end 26 | 27 | if X(pos)==1 28 | labels(j) = labels(i); 29 | end 30 | 31 | pos = pos + 1; 32 | end 33 | end 34 | 35 | % Assign labels from 1 to n 36 | ids = unique(labels); 37 | tmplabels = labels; 38 | 39 | for i = 1:length(ids) 40 | labels(tmplabels==ids(i)) = i; 41 | end 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/correlation_clustering/external/AL-ICM/AL_ICM.m: -------------------------------------------------------------------------------- 1 | function l = AL_ICM(w, ig) 2 | % 3 | % adaptive-label ICM 4 | % 5 | % Usage: 6 | % l = AL_ICM(w, [ig]) 7 | % 8 | % Inputs: 9 | % w - sparse, symmetric affinity matrix containng BOTH positive and 10 | % negative entries (attraction and repultion). 11 | % ig- initial guess labeling (optional) 12 | % 13 | % Output: 14 | % l - labeling of the nodes into different clusters. 15 | % 16 | 17 | 18 | 19 | n = size(w,1); 20 | 21 | LIMIT = 100*ceil(reallog(n)); 22 | 23 | % ignore diagonal 24 | w = w - spdiags( spdiags(w,0), 0, n, n); 25 | 26 | % initial guess 27 | if nargin == 1 || isempty(ig) || numel(ig) ~= n 28 | l = ones(n, 1); 29 | else 30 | [~, ~, l] = unique(ig); 31 | end 32 | 33 | l = 1:n; 34 | 35 | for itr=1:LIMIT 36 | nl = pure_potts_icm_iter_mex(w, l); 37 | if isequal(l,nl) 38 | break; 39 | end 40 | l = nl; 41 | if mod(itr, 15) == 0 % removing empty labels make code run faster! 42 | [~, ~, l] = unique(l); 43 | l = pure_potts_icm_iter_mex(w, l); 44 | end 45 | end 46 | 47 | % if itr == LIMIT 48 | % warning('ML_ICM:itr','reached LIMIT'); 49 | % end 50 | 51 | % remove "empty" labels 52 | [~, ~, l] = unique(l); 53 | 54 | -------------------------------------------------------------------------------- /src/correlation_clustering/external/AL-ICM/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Large Scale Correlation Clustering Software 2 | ------------------------------------------- 3 | 4 | This license agreement concerns the software or data identified above, 5 | which may include source code, and any associated materials, text or speech files, 6 | associated media and "online" or electronic documentation and any updates provided (together, the "Software"). 7 | 8 | By installing, copying, or otherwise using this Software, 9 | you agree to be bound by the terms of this agreement. 10 | If you do not agree, do not install copy or use the Software. 11 | The Software is protected by copyright and other intellectual property laws and is licensed, not sold. 12 | 13 | SCOPE OF RIGHTS: 14 | You may use, copy, reproduce, and distribute this Software for any non-commercial purpose, 15 | subject to the restrictions in this agreement. 16 | Some purposes which can be non-commercial are teaching, academic research, public demonstrations and personal experimentation. 17 | You may also distribute this Software with books or other teaching materials, 18 | or publish the Software on websites, that are intended to teach the use of the Software for academic or other non-commercial purposes. 19 | 20 | You may not use or distribute this Software or any derivative works in any form for commercial purposes. 21 | Examples of commercial purposes would be running business operations, licensing, leasing, or selling the Software, 22 | distributing the Software for use with commercial products, 23 | using the Software in the creation or use of commercial products or any other activity which purpose is to procure a 24 | commercial gain to you or others. 25 | 26 | You may create derivative works of such portions of the Software and distribute the modified Software for non-commercial purposes, 27 | as provided herein. 28 | 29 | In return, we simply require that you agree: 30 | 1. That you will not remove any copyright or other notices from the Software. 31 | 2. That if you distribute the Software or any derivative works of the Software, 32 | you will distribute them under the same terms and conditions as in this license, 33 | and you will not grant other rights to the Software or derivative works that are different from 34 | those provided by this agreement. 35 | 3. That if you have created derivative works of the Software, and distribute such derivative works, 36 | you will cause the modified files to carry prominent notices so that recipients know that they are not receiving 37 | the original Software. Such notices must state: (i) that you have changed the Software; and (ii) the date of any changes. 38 | 4. THAT THE SOFTWARE COMES "AS IS", WITH NO WARRANTIES. THIS MEANS NO EXPRESS, IMPLIED OR STATUTORY WARRANTY, 39 | INCLUDING WITHOUT LIMITATION, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, 40 | ANY WARRANTY AGAINST INTERFERENCE WITH YOUR ENJOYMENT OF THE SOFTWARE OR ANY WARRANTY OF TITLE OR NON-INFRINGEMENT. 41 | THERE IS NO WARRANTY THAT THIS SOFTWARE WILL FULFILL ANY OF YOUR PARTICULAR PURPOSES OR NEEDS. 42 | ALSO, YOU MUST PASS THIS DISCLAIMER ON WHENEVER YOU DISTRIBUTE THE SOFTWARE OR DERIVATIVE WORKS. 43 | 5. THAT ANY CONTRIBUTOR TO THE SOFTWARE WILL BE LIABLE FOR ANY DAMAGES RELATED TO THE SOFTWARE OR THIS AGREEMENT, 44 | INCLUDING DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL OR INCIDENTAL DAMAGES, TO THE MAXIMUM EXTENT THE LAW PERMITS, 45 | NO MATTER WHAT LEGAL THEORY IT IS BASED ON. 46 | ALSO, YOU MUST PASS THIS LIMITATION OF LIABILITY ON WHENEVER YOU DISTRIBUTE THE SOFTWARE OR DERIVATIVE WORKS. 47 | 6. That we have no duty of reasonable care or lack of negligence, and we are not obligated to (and will not) 48 | provide technical support for the Software. 49 | 7. That if you breach this agrrement or if you sue anyone over patents that you think may apply to or read on the Software 50 | or anyone's use of the Software, this agrrement (and your license and rights obtained herein) terminate automatically. 51 | Upon any such termination, you shall destroy all of your copies of the Software immediately. 52 | Sections 3, 4, 5, and 6 of this agrrement shall survive any termination of this agrrement. 53 | 8. That all rights not expressly granted to you in this agrrement are reserved. 54 | -------------------------------------------------------------------------------- /src/correlation_clustering/external/AL-ICM/README.txt: -------------------------------------------------------------------------------- 1 | =============================================== 2 | Large Scale Correlation Clustering Optimization 3 | =============================================== 4 | 5 | 1. Included packages 6 | -------------------- 7 | This distribution includes the following package 8 | QPBO - see below 9 | 10 | 2. Main functions 11 | ----------------- 12 | This package contains the following functions 13 | a_expand.m - implementation fo the expand&explore algorithm (Alg. 1 in Bagon&Galun2011) 14 | ab_swap.m - implementation fo the swap&explore algorithm (Alg. 2 in Bagon&Galun2011) 15 | AL_ICM.m - implementation fo the adaptive label ICM (Sec. 5.2 in Bagon&Galun2011) 16 | 17 | 3. Installation 18 | --------------- 19 | a. Download the tarball from the website and extract it to a local folder. 20 | b. Make sure your mex compiler under Matlab is well setup. 21 | If it does not, type (in Matlab) 22 | >> mex -setup 23 | and chhose either the visual studio compiler (for PC), or the gcc compiler (of Linux) 24 | c. Change directory in Matlab to the local folder in which you extracted the tarball 25 | d. Run: 26 | >> mexall 27 | This will compile several mex files required by our algorithms. 28 | e. If no error appear - you are good to go... 29 | 30 | 4. Documentation 31 | ---------------- 32 | Use (in Matlab) 33 | >> doc 34 | To see usage instructions and documentation for the various functions. 35 | 36 | 5. Usage example 37 | ---------------- 38 | Here is a small usage example (you may use this example to test your installation) 39 | You may copy-paste these commands into Matlab: 40 | % 41 | [gt w] = MakeSynthAff(100, [1 3 5 2], 10, .5, .1); % creates a synthetic sparse affinity matrix 42 | plotWl(w,gt); % plot the matrix ordered according to the ground-truth labeling 43 | el = a_expand(w); % CC optimization using expand&explore 44 | plotWl(w,el); % visualize the result 45 | sl = ab_swap(w); % CC optimization using swap&explore 46 | plotWl(w,sl); % visualize the result 47 | il = AL_ICM(w); % CC optimization using adaptive-label ICM 48 | plotWl(w,il); % visualize the result 49 | [CCEnergy(w,gt) CCEnergy(w,el) CCEnergy(w,sl) CCEnergy(w,il)], % output CC objective values for the different partitions. 50 | 51 | 6. Proper reference 52 | ------------------- 53 | Using this software in any academic work you must cite the following works in any resulting publication: 54 | S. Bagon and M. Galun. "Large Scale Correlation Clustering Optimization", arXiv'2011 55 | C. Rother, V. Kolmogorov, V. Lempitsky, and M. Szummer. "Optimizing binary MRFs via extended roof duality", CVPR'2007 56 | 57 | 58 | A. Included package: 59 | QPBO: Vladimir Kolmogorov's implementation 60 | ------------------------------------------ 61 | source files from: 62 | http://pub.ist.ac.at/~vnk/software.html 63 | -------------------------------------------------------------------------------- /src/correlation_clustering/external/AL-ICM/VERSION.txt: -------------------------------------------------------------------------------- 1 | Version 1.0 2 | -------------------------------------------------------------------------------- /src/correlation_clustering/external/AL-ICM/pure_potts_icm_iter_mex.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.h" 2 | #include 3 | #include 4 | #include 5 | /* 6 | * Usage: 7 | * l = pure_potts_icm_iter_mex(W, l); 8 | * 9 | * Solve iteratively for 10 | * 11 | * 12 | * Use Gauss - Seidel iterations (i.e., use updated values in current iteration) 13 | * 14 | * W is sparse NxN real matrix 15 | * l is double Nx1 vector of labels 16 | * 17 | * 18 | * compile using: 19 | * >> mex -O -largeArrayDims pure_potts_icm_iter_mex.cpp 20 | */ 21 | 22 | #line __LINE__ "pure_potts_icm_iter_mex" 23 | 24 | #define STR(s) #s 25 | #define ERR_CODE(a,b) a ":" "line_" STR(b) 26 | 27 | 28 | // INPUTS 29 | enum { 30 | WIN = 0, 31 | LIN, 32 | NIN 33 | }; 34 | 35 | // OUTPUTS 36 | enum { 37 | LOUT = 0, 38 | NOUT 39 | }; 40 | template 41 | inline 42 | T max(const T& a, const T& b) 43 | { 44 | return (a>b)?a:b; 45 | } 46 | 47 | template 48 | inline 49 | mwIndex AdjustCol(const typename std::vector& buffer) 50 | { 51 | // what is the maximal entry? 52 | mwIndex mi = buffer.size()+1; 53 | T mx = 0; 54 | for (mwIndex k = 0 ; k < buffer.size() ; k++ ) { 55 | if ( buffer[k] > mx ) { 56 | mx = buffer[k]; 57 | mi = k+1; // 1-based matlab index/label 58 | } 59 | } 60 | return mi; 61 | } 62 | 63 | void 64 | mexFunction( 65 | int nout, 66 | mxArray* pout[], 67 | int nin, 68 | const mxArray* pin[]) 69 | { 70 | if ( nin != NIN ) 71 | mexErrMsgIdAndTxt(ERR_CODE(__FILE__, __LINE__),"must have %d inputs", NIN); 72 | if (nout==0) 73 | return; 74 | if (nout != NOUT ) 75 | mexErrMsgIdAndTxt(ERR_CODE(__FILE__, __LINE__),"must have exactly %d output", NOUT); 76 | 77 | if ( mxIsComplex(pin[WIN]) || !mxIsSparse(pin[WIN]) ) 78 | mexErrMsgIdAndTxt(ERR_CODE(__FILE__, __LINE__),"W must be real sparse matrix"); 79 | 80 | if ( mxIsComplex(pin[LIN]) || mxIsSparse(pin[LIN]) || mxGetClassID(pin[LIN]) != mxDOUBLE_CLASS ) 81 | mexErrMsgIdAndTxt(ERR_CODE(__FILE__, __LINE__),"l must be real full double vector"); 82 | 83 | 84 | if ( mxGetN(pin[WIN]) != mxGetM(pin[WIN]) || mxGetN(pin[WIN]) != mxGetNumberOfElements(pin[LIN]) || 85 | mxGetNumberOfDimensions(pin[WIN])!=2 ) 86 | mexErrMsgIdAndTxt(ERR_CODE(__FILE__, __LINE__),"matrix - vector dimensions mismatch"); 87 | 88 | // allocate output vector 89 | mwSize N = mxGetN(pin[WIN]); // n and m are equal - W is square 90 | 91 | 92 | pout[0] = mxCreateDoubleMatrix(N, 1, mxREAL); 93 | double* pl = mxGetPr(pout[0]); 94 | 95 | double* pr = mxGetPr(pin[LIN]); 96 | memcpy(pl, pr, N*sizeof(double)); 97 | 98 | /* how many labels in initial guess? */ 99 | mwIndex nl=0; 100 | for ( mwIndex ii(0); ii < N ; ii++ ) 101 | nl = max(nl, pl[ii]); 102 | 103 | 104 | // mexPrintf("init has %d labels\n", nl); 105 | 106 | 107 | /* computation starts */ 108 | pr = mxGetPr(pin[WIN]); 109 | mwIndex* pir = mxGetIr(pin[WIN]); 110 | mwIndex* pjc = mxGetJc(pin[WIN]); 111 | 112 | // compute each column of u' and then make a decision about it 113 | // before moving on to the next columns 114 | for (mwSize col=0; col< N; col++) { 115 | 116 | std::vector buffer(nl,0); // start a vector of zeros the size of number of labels 117 | 118 | 119 | 120 | // perform sparse multiplication 121 | for (mwIndex ri = pjc[col] ; // starting row index 122 | ri < pjc[col+1] ; // stopping row index 123 | ri++) { 124 | if ( col != pir[ri] ) { 125 | // only off-diagonal elements are participating 126 | // mexPrintf("\t col=%d, row=%d, l[row]=%.0f\n", col, pir[ri], pl[ pir[ ri ] ]-1 ); 127 | buffer[ pl[ pir[ri] ]-1 ] += pr[ri]; 128 | } 129 | // pir[ri] -> current row 130 | // col -> current col 131 | // pr[ri] -> W[pir[ri], col] 132 | 133 | } 134 | 135 | 136 | // mexPrintf("col %d: Buffer has %d elements, buffer[0]=%.2f\n", col, buffer.size(), buffer[0]); 137 | 138 | pl[col] = AdjustCol(buffer); // make a decision for this pixel 139 | if ( pl[col] > nl ) 140 | nl = pl[col]; 141 | 142 | } 143 | } 144 | 145 | -------------------------------------------------------------------------------- /src/correlation_clustering/external/KL/KernighanLin.m: -------------------------------------------------------------------------------- 1 | function labels = KernighanLin(correlationMatrix) 2 | 3 | if size(correlationMatrix,1) == 1 4 | labels = [1]; 5 | return; 6 | end 7 | upper_tri = triu(ones(size(correlationMatrix))); 8 | [sourceNode, targetNode] = find(upper_tri); 9 | values = correlationMatrix(sub2ind(size(correlationMatrix),sourceNode,targetNode)); 10 | infidx = find(values == -Inf); 11 | 12 | % [sourceNode,targetNode,values] = find(problemGraph.sparse_correlationMat_Upper); 13 | rmIdx = bsxfun(@eq,sourceNode,targetNode); 14 | % rmIdx(infidx) = 1; 15 | sourceNode(rmIdx) = []; 16 | targetNode(rmIdx) = []; 17 | values(rmIdx) = []; 18 | sourceNode = sourceNode -1; 19 | targetNode = targetNode-1; 20 | 21 | nNodes = numel(diag(upper_tri)); 22 | weightedGraph = [sourceNode,targetNode,values]; 23 | 24 | A = Multicut_KL_MEX(nNodes,size(weightedGraph,1),weightedGraph); 25 | %output is of the form ((u1,v1,u2,v2,...) for all edges {u_i,v_i} 26 | %index starting with 0. 27 | % Converting back to matlab format 28 | A_r = A(1:2:end)'; 29 | A_r(:,2) = A(2:2:end); 30 | A_r= A_r+ 1; 31 | result = A_r; 32 | 33 | 34 | G = graph; 35 | G = addnode(G,nNodes); 36 | G = addedge(G,result(:,1)',result(:,2)'); 37 | labels = conncomp(G)'; 38 | 39 | 40 | 41 | end -------------------------------------------------------------------------------- /src/correlation_clustering/external/KL/LICENSE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamvitJ/Duke-DeepCC/cfa233a00a819d409b5c7927e5fdad7719aaa87c/src/correlation_clustering/external/KL/LICENSE.txt -------------------------------------------------------------------------------- /src/correlation_clustering/external/KL/Multicut_KL_MEX.cpp: -------------------------------------------------------------------------------- 1 | #include "mex.h" 2 | #include 3 | #include "andres/graph/graph.hxx" 4 | #include "andres/graph/multicut/kernighan-lin.hxx" 5 | #include 6 | #include 7 | #include 8 | #include 9 | using namespace std; 10 | 11 | void mexFunction(int nlhs, mxArray* plhs[], 12 | int nrhs, const mxArray* prhs[]) { 13 | 14 | 15 | double* nNodesP = mxGetPr(prhs[0]); 16 | double* nEdgesP = mxGetPr(prhs[1]); 17 | 18 | int nNodes = nNodesP[0]; 19 | int nEdges = nEdgesP[0]; 20 | std::cout << nNodes << std::endl; 21 | std::cout << nEdges<< std::endl; 22 | 23 | double* nLinks= mxGetPr(prhs[2]); 24 | 25 | andres::graph::Graph<> graph; 26 | graph.insertVertices(nNodes); 27 | std::vector weights(nEdges); 28 | 29 | for (int iEdge=0;iEdge<=nEdges-1;iEdge++){ 30 | graph.insertEdge(nLinks[iEdge],nLinks[iEdge+nEdges]); 31 | weights[iEdge] = nLinks[iEdge+2*nEdges]; 32 | } 33 | 34 | std::vector edge_labels(graph.numberOfEdges()); 35 | andres::graph::multicut::kernighanLin(graph, weights, edge_labels,edge_labels); 36 | //andres::graph::multicut::ilp(graph, weights, edge_labels, edge_labels); 37 | 38 | std::vector > entryList; 39 | 40 | 41 | 42 | for (int i=0; i graphRes = graph.findEdge(i,j); 45 | if (graphRes.first){ 46 | bool label= edge_labels[graphRes.second]; 47 | if (label == 0){ 48 | entryList.push_back(std::make_pair(i,j)); 49 | // resList[i][j]=1; 50 | } 51 | } 52 | } 53 | } 54 | plhs[0] = mxCreateDoubleMatrix(1,entryList.size()*2,mxREAL); 55 | 56 | int linIdx = 0; 57 | double* resP = mxGetPr(plhs[0]); 58 | for(const pair &entry : entryList){ 59 | 60 | resP[linIdx] = entry.first; 61 | resP[linIdx+1] = entry.second; 62 | linIdx = linIdx+2; 63 | 64 | } 65 | // 66 | // for (int i = 0; i< entryList.size()*2; i++){ 67 | // for (int j = 0; j< nNodes; j++){ 68 | // entryList. 69 | // resP[nNodes*i+j]= resList[i][j; 70 | // } 71 | // } 72 | } 73 | -------------------------------------------------------------------------------- /src/correlation_clustering/external/KL/andres/graph/adjacency.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef ANDRES_GRAPH_ADJACENCY_HXX 3 | #define ANDRES_GRAPH_ADJACENCY_HXX 4 | 5 | namespace andres { 6 | namespace graph { 7 | 8 | /// The adjacency of a vertex consists of a vertex and a connecting edge. 9 | template 10 | class Adjacency { 11 | public: 12 | typedef T Value; 13 | 14 | Adjacency(const Value = T(), const Value = T()); 15 | Value vertex() const; 16 | Value& vertex(); 17 | Value edge() const; 18 | Value& edge(); 19 | bool operator<(const Adjacency&) const; 20 | bool operator<=(const Adjacency&) const; 21 | bool operator>(const Adjacency&) const; 22 | bool operator>=(const Adjacency&) const; 23 | bool operator==(const Adjacency&) const; 24 | bool operator!=(const Adjacency&) const; 25 | 26 | private: 27 | Value vertex_; 28 | Value edge_; 29 | }; 30 | 31 | /// Construct an adjacency. 32 | /// 33 | /// \param vertex Vertex. 34 | /// \param edge Edge. 35 | /// 36 | template 37 | inline 38 | Adjacency::Adjacency( 39 | const Value vertex, 40 | const Value edge 41 | ) 42 | : vertex_(vertex), 43 | edge_(edge) 44 | {} 45 | 46 | /// Access the vertex. 47 | /// 48 | template 49 | inline typename Adjacency::Value 50 | Adjacency::vertex() const { 51 | return vertex_; 52 | } 53 | 54 | /// Access the vertex. 55 | /// 56 | template 57 | inline typename Adjacency::Value& 58 | Adjacency::vertex() { 59 | return vertex_; 60 | } 61 | 62 | /// Access the edge. 63 | /// 64 | template 65 | inline typename Adjacency::Value 66 | Adjacency::edge() const { 67 | return edge_; 68 | } 69 | 70 | /// Access the edge. 71 | /// 72 | template 73 | inline typename Adjacency::Value& 74 | Adjacency::edge() { 75 | return edge_; 76 | } 77 | 78 | /// Adjacencies are ordered first wrt the vertex, then wrt the edge. 79 | /// 80 | template 81 | inline bool 82 | Adjacency::operator<( 83 | const Adjacency& in 84 | ) const { 85 | if(vertex_ < in.vertex_) { 86 | return true; 87 | } 88 | else if(vertex_ == in.vertex_) { 89 | return edge_ < in.edge_; 90 | } 91 | else { 92 | return false; 93 | } 94 | } 95 | 96 | /// Adjacencies are ordered first wrt the vertex, then wrt the edge. 97 | /// 98 | template 99 | inline bool 100 | Adjacency::operator<=( 101 | const Adjacency& in 102 | ) const { 103 | if(vertex_ < in.vertex_) { 104 | return true; 105 | } 106 | else if(vertex_ == in.vertex_) { 107 | return edge_ <= in.edge_; 108 | } 109 | else { 110 | return false; 111 | } 112 | } 113 | 114 | /// Adjacencies are ordered first wrt the vertex, then wrt the edge. 115 | /// 116 | template 117 | inline bool 118 | Adjacency::operator>( 119 | const Adjacency& in 120 | ) const { 121 | if(vertex_ > in.vertex_) { 122 | return true; 123 | } 124 | else if(vertex_ == in.vertex_) { 125 | return edge_ > in.edge_; 126 | } 127 | else { 128 | return false; 129 | } 130 | } 131 | 132 | /// Adjacencies are ordered first wrt the vertex, then wrt the edge. 133 | /// 134 | template 135 | inline bool 136 | Adjacency::operator>=( 137 | const Adjacency& in 138 | ) const { 139 | if(vertex_ > in.vertex_) { 140 | return true; 141 | } 142 | else if(vertex_ == in.vertex_) { 143 | return edge_ >= in.edge_; 144 | } 145 | else { 146 | return false; 147 | } 148 | } 149 | 150 | /// Adjacencies are equal if both the vertex and the edge are equal. 151 | /// 152 | template 153 | inline bool 154 | Adjacency::operator==( 155 | const Adjacency& in 156 | ) const { 157 | return vertex_ == in.vertex_ && edge_ == in.edge_; 158 | } 159 | 160 | /// Adjacencies are unequal if either the vertex or the edge are unqual. 161 | /// 162 | template 163 | inline bool 164 | Adjacency::operator!=( 165 | const Adjacency& in 166 | ) const { 167 | return !(*this == in); 168 | } 169 | 170 | } // namespace graph 171 | } // namespace andres 172 | 173 | #endif // #ifndef ANDRES_GRAPH_ADJACENCY_HXX 174 | -------------------------------------------------------------------------------- /src/correlation_clustering/external/KL/andres/graph/subgraph.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef ANDRES_GRAPH_SUBGRAPH_HXX 3 | #define ANDRES_GRAPH_SUBGRAPH_HXX 4 | 5 | namespace andres { 6 | namespace graph { 7 | 8 | /// An entire graph. 9 | template 10 | struct DefaultSubgraphMask { 11 | typedef T Value; 12 | 13 | bool vertex(const Value v) const 14 | { return true; } 15 | bool edge(const Value e) const 16 | { return true; } 17 | }; 18 | 19 | } // namespace graph 20 | } // namespace andres 21 | 22 | #endif // #ifndef ANDRES_GRAPH_SUBGRAPH_HXX 23 | -------------------------------------------------------------------------------- /src/correlation_clustering/external/KL/andres/graph/visitor.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef ANDRES_GRAPH_VISITOR_HXX 3 | #define ANDRES_GRAPH_VISITOR_HXX 4 | 5 | #include 6 | #include 7 | 8 | namespace andres { 9 | namespace graph { 10 | 11 | /// Visitors can be used to follow the indices of vertices and edges. 12 | /// 13 | /// These indices change due to the insetion and removal of vertices and edges. 14 | /// 15 | template 16 | struct IdleGraphVisitor { 17 | typedef S size_type; 18 | 19 | IdleGraphVisitor() {} 20 | void insertVertex(const size_type a) const {} 21 | void insertVertices(const size_type a, const size_type n) const {} 22 | void eraseVertex(const size_type a) const {} 23 | void relabelVertex(const size_type a, const size_type b) const {} 24 | void insertEdge(const size_type a) const {} 25 | void eraseEdge(const size_type a) const {} 26 | void relabelEdge(const size_type a, const size_type b) const {} 27 | }; 28 | 29 | /// Visitors can be used to follow the indices of vertices and edges. 30 | /// 31 | /// These indices change due to the insetion and removal of vertices and edges. 32 | /// 33 | template 34 | struct VerboseGraphVisitor { 35 | typedef S size_type; 36 | 37 | VerboseGraphVisitor() {} 38 | void insertVertex(const size_type a) const 39 | { std::cout << "inserting vertex " << a << std::endl; } 40 | void insertVertices(const size_type a, const size_type n) const 41 | { std::cout << "inserting " << n << " vertices, starting from index " << a << std::endl; } 42 | void eraseVertex(const size_type a) const 43 | { std::cout << "removing vertex " << a << std::endl; } 44 | void relabelVertex(const size_type a, const size_type b) const 45 | { std::cout << "relabeling vertex " << a << ". new label is " << b << std::endl; } 46 | void insertEdge(const size_type a) const 47 | { std::cout << "inserting edge " << a << std::endl; } 48 | void eraseEdge(const size_type a) const 49 | { std::cout << "removing edge " << a << std::endl; } 50 | void relabelEdge(const size_type a, const size_type b) const 51 | { std::cout << "relabeling edge " << a << ". new label is " << b << std::endl; } 52 | }; 53 | 54 | } // namespace graph 55 | } // namespace andres 56 | 57 | #endif // #ifndef ANDRES_GRAPH_VISITOR_HXX 58 | -------------------------------------------------------------------------------- /src/duke/DukeFrameReader.m: -------------------------------------------------------------------------------- 1 | classdef DukeFrameReader < handle 2 | % Assumes all frames are extracted to DukeMTMC/frames/ 3 | % Example use 4 | % 5 | % reader = DukeFrameReader('g:/dukemtmc/'); 6 | % camera = 2; 7 | % frame = 360720; 8 | % figure, imshow(reader.getFrame(camera, frame)); 9 | 10 | properties 11 | NumCameras = 8; 12 | NumFrames = [359580, 360720, 355380, 374850, 366390, 344400, 337680, 353220]; 13 | DatasetPath = ''; 14 | end 15 | methods 16 | function obj = DukeFrameReader(datasetPath) 17 | obj.DatasetPath = datasetPath; 18 | end 19 | 20 | function img = getFrame(obj, iCam, iFrame) 21 | % DukeMTMC frames are 1-indexed 22 | assert(iFrame > 0 && iFrame <= obj.NumFrames(iCam),'Frame out of range'); 23 | img = imread(sprintf('%sframes/camera%d/%d.jpg',obj.DatasetPath, iCam, iFrame)); 24 | end 25 | 26 | end 27 | end 28 | 29 | -------------------------------------------------------------------------------- /src/duke/DukeVideoReader.m: -------------------------------------------------------------------------------- 1 | classdef DukeVideoReader < handle 2 | % Example use 3 | % 4 | % reader = DukeVideoReader('F:/datasets/DukeMTMC/'); 5 | % camera = 2; 6 | % frame = 360720; 7 | % figure, imshow(reader.getFrame(camera, frame)); 8 | 9 | properties 10 | NumCameras = 8; 11 | NumFrames = [359580, 360720, 355380, 374850, 366390, 344400, 337680, 353220]; 12 | PartFrames = [38370,38370,38400,38670,38370,38400,38790,38370; ... 13 | 38370,38370,38370,38670,38370,38370,38640,38370;... 14 | 38370,38370,38370,38670,38370,38370,38460,38370;... 15 | 38370,38370,38370,38670,38370,38370,38610,38370;... 16 | 38370,38370,38370,38670,38370,38400,38760,38370;... 17 | 38370,38370,38370,38700,38370,38400,38760,38370;... 18 | 38370,38370,38370,38670,38370,38370,38790,38370;... 19 | 38370,38370,38370,38670,38370,38370,38490,38370;... 20 | 38370,38370,38370,38670,38370,37350,28380,38370;... 21 | 14250,15390,10020,26790,21060, 0, 0, 7890]; 22 | MaxPart = [9, 9, 9, 9, 9, 8, 8, 9]; 23 | DatasetPath = ''; 24 | CurrentCamera = 1; 25 | CurrentPart = 0; 26 | PrevCamera = 1; 27 | PrevFrame = 0; 28 | PrevPart = 0; 29 | Video = []; 30 | lastFrame = []; 31 | end 32 | methods 33 | function obj = DukeVideoReader(datasetPath) 34 | obj.DatasetPath = datasetPath; 35 | obj.Video = cv.VideoCapture(sprintf('%svideos/camera%d/%05d.MTS',obj.DatasetPath,obj.CurrentCamera, obj.CurrentPart), 'API','FFMPEG'); 36 | end 37 | 38 | function img = getFrame(obj, iCam, iFrame) 39 | % DukeMTMC frames are 1-indexed 40 | assert(iFrame > 0 && iFrame <= obj.NumFrames(iCam),'Frame out of range'); 41 | 42 | ksum = 0; 43 | for k = 0:obj.MaxPart(iCam) 44 | ksumprev = ksum; 45 | ksum = ksum + obj.PartFrames(k+1,iCam); 46 | if iFrame <= ksum 47 | currentFrame = iFrame - 1 - ksumprev; 48 | iPart = k; 49 | break; 50 | end 51 | end 52 | 53 | if iPart ~= obj.CurrentPart || iCam ~= obj.CurrentCamera 54 | obj.CurrentCamera = iCam; 55 | obj.CurrentPart = iPart; 56 | obj.PrevFrame = -1; 57 | obj.Video = cv.VideoCapture(sprintf('%svideos/camera%d/%05d.MTS',obj.DatasetPath,obj.CurrentCamera, obj.CurrentPart), 'API','FFMPEG'); 58 | end 59 | 60 | if currentFrame ~= obj.PrevFrame + 1 61 | obj.Video.PosFrames = currentFrame; 62 | 63 | if obj.Video.PosFrames ~= currentFrame 64 | back_frame = max(currentFrame - 31, 0); % Keyframes every 30 frames 65 | obj.Video.PosFrames = back_frame; 66 | while obj.Video.PosFrames < currentFrame 67 | obj.Video.read; 68 | back_frame = back_frame + 1; 69 | end 70 | end 71 | 72 | end 73 | assert(obj.Video.PosFrames == currentFrame) 74 | img = obj.Video.read; 75 | 76 | % Keep track of last read 77 | obj.PrevCamera = iCam; 78 | obj.PrevFrame = currentFrame; 79 | obj.PrevPart = iPart; 80 | end 81 | 82 | end 83 | end 84 | 85 | -------------------------------------------------------------------------------- /src/duke/getROIs.m: -------------------------------------------------------------------------------- 1 | function ROI = getROIs() 2 | 3 | ROI{1} = [214 553;1904 411;1897 1055;216 1051]; 4 | ROI{2} = [35 423;574 413;624 300;1075 284;1150 341;1153 396;1260 393;1610 492;1608 460;1614 446;1771 440;1777 485;1894 483;1894 1048;29 1051]; 5 | ROI{3} = [65 659;926 662;798 592;827 574;1201 573;1500 671;1888 673;1882 1047;68 1044]; 6 | ROI{4} = [1189 1043;1664 1043;1434 240;1267 240]; 7 | ROI{5} = [28 1054;1897 1054;1893 202;410 211;397 313;311 320;104 369;71 449;107 567;27 750]; 8 | ROI{6} = [154 646;957 626;1863 886;1866 1050;40 1059;56 775;103 682]; 9 | ROI{7} = [40 751;40 1049;1862 1050;1862 875;862 817;1004 730;667 710;445 779]; 10 | ROI{8} = [1867 637;1806 1066;53 1047;455 807;457 729;824 531]; -------------------------------------------------------------------------------- /src/duke/global2local.m: -------------------------------------------------------------------------------- 1 | function local_frame = global2local(start_frame, global_frame) 2 | 3 | local_frame = global_frame - start_frame + 1; 4 | 5 | end 6 | 7 | -------------------------------------------------------------------------------- /src/duke/local2global.m: -------------------------------------------------------------------------------- 1 | function global_frame = local2global(start_frame, local_frame) 2 | 3 | global_frame = local_frame + start_frame - 1; 4 | 5 | end 6 | 7 | -------------------------------------------------------------------------------- /src/duke/prepareMOTChallengeSubmission.m: -------------------------------------------------------------------------------- 1 | function prepareMOTChallengeSubmission(opts) 2 | % Prepare submission file duke.txt 3 | 4 | submission_data = []; 5 | for sequence = 3:4 6 | 7 | for iCam = 1:8 8 | filename = sprintf('%s/%s/L3-identities/cam%d_%s.txt', opts.experiment_root, opts.experiment_name, iCam, opts.sequence_names{sequence}); 9 | data = dlmread(filename); 10 | data(:,[1 2]) = data(:,[2 1]); 11 | data = [iCam*ones(size(data,1),1), data]; 12 | data = data(:,[1:7]); 13 | submission_data = [submission_data; data]; 14 | end 15 | end 16 | 17 | % Sanity check 18 | frameIdPairs = submission_data(:,1:3); 19 | [u,I,~] = unique(frameIdPairs, 'rows', 'first'); 20 | hasDuplicates = size(u,1) < size(frameIdPairs,1); 21 | if hasDuplicates 22 | ixDupRows = setdiff(1:size(frameIdPairs,1), I); 23 | dupFrameIdExample = frameIdPairs(ixDupRows(1),:); 24 | rows = find(ismember(frameIdPairs, dupFrameIdExample, 'rows')); 25 | 26 | errorMessage = sprintf('Invalid submission: Found duplicate ID/Frame pairs in result.\nInstance:\n'); 27 | errorMessage = [errorMessage, sprintf('%10.2f', submission_data(rows(1),:)), sprintf('\n')]; 28 | errorMessage = [errorMessage, sprintf('%10.2f', submission_data(rows(2),:)), sprintf('\n')]; 29 | assert(~hasDuplicates, errorMessage); 30 | end 31 | 32 | % Write duke.txt 33 | dlmwrite(sprintf('%s/%s/duke.txt',opts.experiment_root, opts.experiment_name), int32(submission_data), 'delimiter', ' ','precision',6); 34 | 35 | -------------------------------------------------------------------------------- /src/duke/world2map.m: -------------------------------------------------------------------------------- 1 | function [ C ] = world2map( W ) 2 | 3 | image_points = [307.4323 469.2366; 485.2483 708.9507]; 4 | world_points = [0 0; 24.955 32.85]; 5 | 6 | diff = image_points(2,:) - image_points(1,:); 7 | 8 | scale = diff ./ world_points(2,:); 9 | trans = image_points(1,:); 10 | 11 | C(:,1) = W(:,1)*scale(1) + trans(1); 12 | C(:,2) = W(:,2)*scale(2) + trans(2); 13 | 14 | end 15 | 16 | -------------------------------------------------------------------------------- /src/evaluate.m: -------------------------------------------------------------------------------- 1 | function [allMets, metsBenchmark, metsMultiCam] = evaluate(opts) 2 | 3 | evalNames = {'trainval', 'trainval-mini', 'test-easy', 'test-hard'}; 4 | seqMap = sprintf('DukeMTMCT-%s.txt', evalNames{opts.sequence}); 5 | eval_folder = [opts.experiment_root, filesep, opts.experiment_name, filesep, opts.eval_dir]; 6 | gt_folder = [opts.dataset_path, 'ground_truth']; 7 | [allMets, metsBenchmark, metsMultiCam] = evaluateTracking(seqMap, eval_folder, gt_folder, 'DukeMTMCT',opts.dataset_path); 8 | -------------------------------------------------------------------------------- /src/external/DukeMTMC-reID_evaluation/CITATION_DukeMTMC-reID.txt: -------------------------------------------------------------------------------- 1 | @inproceedings{zheng2017unlabeled, 2 | title={Unlabeled Samples Generated by GAN Improve the Person Re-identification Baseline in vitro}, 3 | author={Zheng, Zhedong and Zheng, Liang and Yang, Yi}, 4 | booktitle={Proceedings of the IEEE International Conference on Computer Vision}, 5 | year={2017} 6 | } 7 | -------------------------------------------------------------------------------- /src/external/DukeMTMC-reID_evaluation/CITATION_DukeMTMC.txt: -------------------------------------------------------------------------------- 1 | @inproceedings{ristani2016MTMC, 2 | title = {Performance Measures and a Data Set for Multi-Target, Multi-Camera Tracking}, 3 | author = {Ristani, Ergys and Solera, Francesco and Zou, Roger and Cucchiara, Rita and Tomasi, Carlo}, 4 | booktitle = {European Conference on Computer Vision workshop on Benchmarking Multi-Target Tracking}, 5 | year = {2016} 6 | } 7 | -------------------------------------------------------------------------------- /src/external/DukeMTMC-reID_evaluation/Data_Distribution.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamvitJ/Duke-DeepCC/cfa233a00a819d409b5c7927e5fdad7719aaa87c/src/external/DukeMTMC-reID_evaluation/Data_Distribution.jpg -------------------------------------------------------------------------------- /src/external/DukeMTMC-reID_evaluation/DukeMTMC-reID_mosaic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamvitJ/Duke-DeepCC/cfa233a00a819d409b5c7927e5fdad7719aaa87c/src/external/DukeMTMC-reID_evaluation/DukeMTMC-reID_mosaic.jpg -------------------------------------------------------------------------------- /src/external/DukeMTMC-reID_evaluation/LICENSE_DukeMTMC-reID.txt: -------------------------------------------------------------------------------- 1 | ATTRIBUTION PROTOCOL 2 | 3 | If you use the DukeMTMC-reID data or any data derived from it, please cite our work as follows: 4 | 5 | @article{zheng2017unlabeled, 6 | title={Unlabeled Samples Generated by GAN Improve the Person Re-identification Baseline in vitro}, 7 | author={Zheng, Zhedong and Zheng, Liang and Yang, Yi}, 8 | journal={arXiv preprint arXiv:1701.07717}, 9 | year={2017} 10 | } 11 | 12 | and include this license and attribution protocol within any derivative work. 13 | 14 | 15 | If you use the DukeMTMC data or any data derived from it, please cite the original work as follows: 16 | 17 | @inproceedings{ristani2016MTMC, 18 | title = {Performance Measures and a Data Set for Multi-Target, Multi-Camera Tracking}, 19 | author = {Ristani, Ergys and Solera, Francesco and Zou, Roger and Cucchiara, Rita and Tomasi, Carlo}, 20 | booktitle = {European Conference on Computer Vision workshop on Benchmarking Multi-Target Tracking}, 21 | year = {2016} 22 | } 23 | 24 | and include this license and attribution protocol within any derivative work. 25 | 26 | If you publish data derived from DukeMTMC, the original dataset creators should first be notified. 27 | Any future dataset derived from DukeMTMC should include "DukeMTMC" in the title, as well as link to the 28 | original project website (http://vision.cs.duke.edu/DukeMTMC/). 29 | 30 | The DukeMTMC dataset is made available under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) license. (https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt) 31 | 32 | You are free to: 33 | 34 | Share — copy and redistribute the material in any medium or format 35 | Adapt — remix, transform, and build upon the material 36 | 37 | Under the following terms: 38 | 39 | Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. 40 | 41 | NonCommercial — You may not use the material for commercial purposes. 42 | 43 | ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. 44 | 45 | No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. 46 | -------------------------------------------------------------------------------- /src/external/DukeMTMC-reID_evaluation/LICENSE_DukeMTMC.txt: -------------------------------------------------------------------------------- 1 | ATTRIBUTION PROTOCOL 2 | 3 | If you use the DukeMTMC data or any data derived from it, please cite the original work as follows: 4 | 5 | @inproceedings{ristani2016MTMC, 6 | title = {Performance Measures and a Data Set for Multi-Target, Multi-Camera Tracking}, 7 | author = {Ristani, Ergys and Solera, Francesco and Zou, Roger and Cucchiara, Rita and Tomasi, Carlo}, 8 | booktitle = {European Conference on Computer Vision workshop on Benchmarking Multi-Target Tracking}, 9 | year = {2016} 10 | } 11 | 12 | and include this license and attribution protocol within any derivative work. 13 | 14 | If you publish data derived from DukeMTMC, the original dataset creators should first be notified. 15 | Any future dataset derived from DukeMTMC should include "DukeMTMC" in the title, as well as link to the 16 | original project website (http://vision.cs.duke.edu/DukeMTMC/). 17 | 18 | The DukeMTMC dataset is made available under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) license. (https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt) 19 | 20 | You are free to: 21 | 22 | Share — copy and redistribute the material in any medium or format 23 | Adapt — remix, transform, and build upon the material 24 | 25 | Under the following terms: 26 | 27 | Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. 28 | 29 | NonCommercial — You may not use the material for commercial purposes. 30 | 31 | ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. 32 | 33 | No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. 34 | -------------------------------------------------------------------------------- /src/external/DukeMTMC-reID_evaluation/col_sum.m: -------------------------------------------------------------------------------- 1 | function s = col_sum(x) 2 | % COL_SUM Sum for each column. 3 | % A more readable alternative to sum(x,1). 4 | s = sum(x,1); 5 | -------------------------------------------------------------------------------- /src/external/DukeMTMC-reID_evaluation/compute_AP.m: -------------------------------------------------------------------------------- 1 | function [ap, cmc] = compute_AP(good_image, junk_image, index) 2 | 3 | cmc = zeros(length(index), 1); 4 | ngood = length(good_image); 5 | 6 | old_recall = 0; 7 | old_precision = 1.0; 8 | ap = 0; 9 | intersect_size = 0; 10 | j = 0; 11 | good_now = 0; 12 | njunk = 0; 13 | for n = 1:length(index) 14 | flag = 0; 15 | if ~isempty(find(good_image == index(n), 1)) 16 | cmc(n-njunk:end) = 1; 17 | flag = 1; % good image 18 | good_now = good_now+1; 19 | end 20 | if ~isempty(find(junk_image == index(n), 1)) 21 | njunk = njunk + 1; 22 | continue; % junk image 23 | end 24 | 25 | if flag == 1%good 26 | intersect_size = intersect_size + 1; 27 | end 28 | recall = intersect_size/ngood; 29 | precision = intersect_size/(j + 1); 30 | ap = ap + (recall - old_recall)*((old_precision+precision)/2); 31 | old_recall = recall; 32 | old_precision = precision; 33 | j = j+1; 34 | 35 | if good_now == ngood 36 | return; 37 | end 38 | end 39 | 40 | end 41 | 42 | -------------------------------------------------------------------------------- /src/external/DukeMTMC-reID_evaluation/duke_rank.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamvitJ/Duke-DeepCC/cfa233a00a819d409b5c7927e5fdad7719aaa87c/src/external/DukeMTMC-reID_evaluation/duke_rank.jpg -------------------------------------------------------------------------------- /src/external/DukeMTMC-reID_evaluation/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamvitJ/Duke-DeepCC/cfa233a00a819d409b5c7927e5fdad7719aaa87c/src/external/DukeMTMC-reID_evaluation/file.png -------------------------------------------------------------------------------- /src/external/DukeMTMC-reID_evaluation/generate_train_list_caffe.m: -------------------------------------------------------------------------------- 1 | p = dir('./bounding_box_train/*.jpg'); 2 | 3 | txt = fopen('train_list.txt','w'); 4 | person_id = 0; 5 | last_id = 1; 6 | for i = 1:numel(p) 7 | file_path = sprintf('./bounding_box_train/%s',p(i).name); 8 | 9 | tmp_id = str2num(p(i).name(1:4)); 10 | camera_id = str2num(p(i).name(7)); 11 | 12 | if(tmp_id ~= last_id) 13 | person_id = person_id + 1; 14 | last_id = tmp_id; 15 | end 16 | fprintf(txt,'%s %d\n',file_path,person_id); % caffe start from 0 17 | end -------------------------------------------------------------------------------- /src/external/DukeMTMC-reID_evaluation/sqdist.m: -------------------------------------------------------------------------------- 1 | function m = sqdist(p, q, A) 2 | % SQDIST Squared Euclidean or Mahalanobis distance. 3 | % SQDIST(p,q) returns m(i,j) = (p(:,i) - q(:,j))'*(p(:,i) - q(:,j)). 4 | % SQDIST(p,q,A) returns m(i,j) = (p(:,i) - q(:,j))'*A*(p(:,i) - q(:,j)). 5 | % The Lightspeed Matlab toolbox 6 | % Written by Tom Minka 7 | 8 | [d, pn] = size(p); 9 | [d, qn] = size(q); 10 | 11 | if pn == 0 || qn == 0 12 | m = zeros(pn,qn); 13 | return 14 | end 15 | 16 | if nargin == 2 17 | 18 | pmag = col_sum(p .* p); 19 | qmag = col_sum(q .* q); 20 | m = repmat(qmag, pn, 1) + repmat(pmag', 1, qn) - 2*p'*q; 21 | %m = ones(pn,1)*qmag + pmag'*ones(1,qn) - 2*p'*q; 22 | 23 | else 24 | 25 | Ap = A*p; 26 | Aq = A*q; 27 | pmag = col_sum(p .* Ap); 28 | qmag = col_sum(q .* Aq); 29 | m = repmat(qmag, pn, 1) + repmat(pmag', 1, qn) - 2*p'*Aq; 30 | 31 | end 32 | -------------------------------------------------------------------------------- /src/external/combinator/cumsumall.m: -------------------------------------------------------------------------------- 1 | %CUMSUMALL cumulative sum of integer elements 2 | % For vectors, CUMSUMALL(X) is a vector containing the cumulative sum of 3 | % the elements of X. For matrices, CUMSUM(X) is a matrix the same size 4 | % as X containing the cumulative sums over each column. 5 | % 6 | % Class suport: 7 | % int8, int16, int32 8 | % 9 | % Keep in mind that the usefullness of this MEX-File is limited because of 10 | % saturation for most problems outside of use with COMBINATOR. 11 | % 12 | % See also cumsum, CUMPROD, SUM, PROD. 13 | -------------------------------------------------------------------------------- /src/external/combinator/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009, Matt Fig 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /src/external/distinguishable_colors/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2011, Tim Holy 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /src/external/export-fig/.gitignore: -------------------------------------------------------------------------------- 1 | /.ignore 2 | *.txt 3 | *.asv 4 | *~ 5 | *.mex* 6 | -------------------------------------------------------------------------------- /src/external/export-fig/ImageSelection.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamvitJ/Duke-DeepCC/cfa233a00a819d409b5c7927e5fdad7719aaa87c/src/external/export-fig/ImageSelection.class -------------------------------------------------------------------------------- /src/external/export-fig/ImageSelection.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamvitJ/Duke-DeepCC/cfa233a00a819d409b5c7927e5fdad7719aaa87c/src/external/export-fig/ImageSelection.java -------------------------------------------------------------------------------- /src/external/export-fig/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Oliver J. Woodford, Yair M. Altman 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /src/external/export-fig/append_pdfs.m: -------------------------------------------------------------------------------- 1 | %APPEND_PDFS Appends/concatenates multiple PDF files 2 | % 3 | % Example: 4 | % append_pdfs(output, input1, input2, ...) 5 | % append_pdfs(output, input_list{:}) 6 | % append_pdfs test.pdf temp1.pdf temp2.pdf 7 | % 8 | % This function appends multiple PDF files to an existing PDF file, or 9 | % concatenates them into a PDF file if the output file doesn't yet exist. 10 | % 11 | % This function requires that you have ghostscript installed on your 12 | % system. Ghostscript can be downloaded from: http://www.ghostscript.com 13 | % 14 | % IN: 15 | % output - string of output file name (including the extension, .pdf). 16 | % If it exists it is appended to; if not, it is created. 17 | % input1 - string of an input file name (including the extension, .pdf). 18 | % All input files are appended in order. 19 | % input_list - cell array list of input file name strings. All input 20 | % files are appended in order. 21 | 22 | % Copyright: Oliver Woodford, 2011 23 | 24 | % Thanks to Reinhard Knoll for pointing out that appending multiple pdfs in 25 | % one go is much faster than appending them one at a time. 26 | 27 | % Thanks to Michael Teo for reporting the issue of a too long command line. 28 | % Issue resolved on 5/5/2011, by passing gs a command file. 29 | 30 | % Thanks to Martin Wittmann for pointing out the quality issue when 31 | % appending multiple bitmaps. 32 | % Issue resolved (to best of my ability) 1/6/2011, using the prepress 33 | % setting 34 | 35 | % 26/02/15: If temp dir is not writable, use the output folder for temp 36 | % files when appending (Javier Paredes); sanity check of inputs 37 | 38 | function append_pdfs(varargin) 39 | 40 | if nargin < 2, return; end % sanity check 41 | 42 | % Are we appending or creating a new file 43 | append = exist(varargin{1}, 'file') == 2; 44 | output = [tempname '.pdf']; 45 | try 46 | % Ensure that the temp dir is writable (Javier Paredes 26/2/15) 47 | fid = fopen(output,'w'); 48 | fwrite(fid,1); 49 | fclose(fid); 50 | delete(output); 51 | isTempDirOk = true; 52 | catch 53 | % Temp dir is not writable, so use the output folder 54 | [dummy,fname,fext] = fileparts(output); %#ok 55 | fpath = fileparts(varargin{1}); 56 | output = fullfile(fpath,[fname fext]); 57 | isTempDirOk = false; 58 | end 59 | if ~append 60 | output = varargin{1}; 61 | varargin = varargin(2:end); 62 | end 63 | % Create the command file 64 | if isTempDirOk 65 | cmdfile = [tempname '.txt']; 66 | else 67 | cmdfile = fullfile(fpath,[fname '.txt']); 68 | end 69 | fh = fopen(cmdfile, 'w'); 70 | fprintf(fh, '-q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress -sOutputFile="%s" -f', output); 71 | fprintf(fh, ' "%s"', varargin{:}); 72 | fclose(fh); 73 | % Call ghostscript 74 | ghostscript(['@"' cmdfile '"']); 75 | % Delete the command file 76 | delete(cmdfile); 77 | % Rename the file if needed 78 | if append 79 | movefile(output, varargin{1}); 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /src/external/export-fig/copyfig.m: -------------------------------------------------------------------------------- 1 | function fh = copyfig(fh) 2 | %COPYFIG Create a copy of a figure, without changing the figure 3 | % 4 | % Examples: 5 | % fh_new = copyfig(fh_old) 6 | % 7 | % This function will create a copy of a figure, but not change the figure, 8 | % as copyobj sometimes does, e.g. by changing legends. 9 | % 10 | % IN: 11 | % fh_old - The handle of the figure to be copied. Default: gcf. 12 | % 13 | % OUT: 14 | % fh_new - The handle of the created figure. 15 | 16 | % Copyright (C) Oliver Woodford 2012 17 | 18 | % 26/02/15: If temp dir is not writable, use the dest folder for temp 19 | % destination files (Javier Paredes) 20 | % 15/04/15: Suppress warnings during copyobj (Dun Kirk comment on FEX page 2013-10-02) 21 | 22 | % Set the default 23 | if nargin == 0 24 | fh = gcf; 25 | end 26 | % Is there a legend? 27 | if isempty(findall(fh, 'Type', 'axes', 'Tag', 'legend')) 28 | % Safe to copy using copyobj 29 | oldWarn = warning('off'); %#ok %Suppress warnings during copyobj (Dun Kirk comment on FEX page 2013-10-02) 30 | fh = copyobj(fh, 0); 31 | warning(oldWarn); 32 | else 33 | % copyobj will change the figure, so save and then load it instead 34 | tmp_nam = [tempname '.fig']; 35 | try 36 | % Ensure that the temp dir is writable (Javier Paredes 26/2/15) 37 | fid = fopen(tmp_nam,'w'); 38 | fwrite(fid,1); 39 | fclose(fid); 40 | delete(tmp_nam); % cleanup 41 | catch 42 | % Temp dir is not writable, so use the current folder 43 | [dummy,fname,fext] = fileparts(tmp_nam); %#ok 44 | fpath = pwd; 45 | tmp_nam = fullfile(fpath,[fname fext]); 46 | end 47 | hgsave(fh, tmp_nam); 48 | fh = hgload(tmp_nam); 49 | delete(tmp_nam); 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /src/external/export-fig/export_fig.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamvitJ/Duke-DeepCC/cfa233a00a819d409b5c7927e5fdad7719aaa87c/src/external/export-fig/export_fig.m -------------------------------------------------------------------------------- /src/external/export-fig/pdf2eps.m: -------------------------------------------------------------------------------- 1 | %PDF2EPS Convert a pdf file to eps format using pdftops 2 | % 3 | % Examples: 4 | % pdf2eps source dest 5 | % 6 | % This function converts a pdf file to eps format. 7 | % 8 | % This function requires that you have pdftops, from the Xpdf suite of 9 | % functions, installed on your system. This can be downloaded from: 10 | % http://www.foolabs.com/xpdf 11 | % 12 | %IN: 13 | % source - filename of the source pdf file to convert. The filename is 14 | % assumed to already have the extension ".pdf". 15 | % dest - filename of the destination eps file. The filename is assumed to 16 | % already have the extension ".eps". 17 | 18 | % Copyright (C) Oliver Woodford 2009-2010 19 | 20 | % Thanks to Aldebaro Klautau for reporting a bug when saving to 21 | % non-existant directories. 22 | 23 | function pdf2eps(source, dest) 24 | % Construct the options string for pdftops 25 | options = ['-q -paper match -eps -level2 "' source '" "' dest '"']; 26 | % Convert to eps using pdftops 27 | [status, message] = pdftops(options); 28 | % Check for error 29 | if status 30 | % Report error 31 | if isempty(message) 32 | error('Unable to generate eps. Check destination directory is writable.'); 33 | else 34 | error(message); 35 | end 36 | end 37 | % Fix the DSC error created by pdftops 38 | fid = fopen(dest, 'r+'); 39 | if fid == -1 40 | % Cannot open the file 41 | return 42 | end 43 | fgetl(fid); % Get the first line 44 | str = fgetl(fid); % Get the second line 45 | if strcmp(str(1:min(13, end)), '% Produced by') 46 | fseek(fid, -numel(str)-1, 'cof'); 47 | fwrite(fid, '%'); % Turn ' ' into '%' 48 | end 49 | fclose(fid); 50 | end 51 | 52 | -------------------------------------------------------------------------------- /src/external/export-fig/print2eps.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamvitJ/Duke-DeepCC/cfa233a00a819d409b5c7927e5fdad7719aaa87c/src/external/export-fig/print2eps.m -------------------------------------------------------------------------------- /src/external/export-fig/read_write_entire_textfile.m: -------------------------------------------------------------------------------- 1 | %READ_WRITE_ENTIRE_TEXTFILE Read or write a whole text file to/from memory 2 | % 3 | % Read or write an entire text file to/from memory, without leaving the 4 | % file open if an error occurs. 5 | % 6 | % Reading: 7 | % fstrm = read_write_entire_textfile(fname) 8 | % Writing: 9 | % read_write_entire_textfile(fname, fstrm) 10 | % 11 | %IN: 12 | % fname - Pathname of text file to be read in. 13 | % fstrm - String to be written to the file, including carriage returns. 14 | % 15 | %OUT: 16 | % fstrm - String read from the file. If an fstrm input is given the 17 | % output is the same as that input. 18 | 19 | function fstrm = read_write_entire_textfile(fname, fstrm) 20 | modes = {'rt', 'wt'}; 21 | writing = nargin > 1; 22 | fh = fopen(fname, modes{1+writing}); 23 | if fh == -1 24 | error('Unable to open file %s.', fname); 25 | end 26 | try 27 | if writing 28 | fwrite(fh, fstrm, 'char*1'); 29 | else 30 | fstrm = fread(fh, '*char')'; 31 | end 32 | catch ex 33 | fclose(fh); 34 | rethrow(ex); 35 | end 36 | fclose(fh); 37 | end 38 | -------------------------------------------------------------------------------- /src/external/export-fig/user_string.m: -------------------------------------------------------------------------------- 1 | function string = user_string(string_name, string) 2 | %USER_STRING Get/set a user specific string 3 | % 4 | % Examples: 5 | % string = user_string(string_name) 6 | % isSaved = user_string(string_name, new_string) 7 | % 8 | % Function to get and set a string in a system or user specific file. This 9 | % enables, for example, system specific paths to binaries to be saved. 10 | % 11 | % The specified string will be saved in a file named .txt, 12 | % either in a subfolder named .ignore under this file's folder, or in the 13 | % user's prefdir folder (in case this file's folder is non-writable). 14 | % 15 | % IN: 16 | % string_name - String containing the name of the string required, which 17 | % sets the filename storing the string: .txt 18 | % new_string - The new string to be saved in the .txt file 19 | % 20 | % OUT: 21 | % string - The currently saved string. Default: '' 22 | % isSaved - Boolean indicating whether the save was succesful 23 | 24 | % Copyright (C) Oliver Woodford 2011-2014, Yair Altman 2015- 25 | 26 | % This method of saving paths avoids changing .m files which might be in a 27 | % version control system. Instead it saves the user dependent paths in 28 | % separate files with a .txt extension, which need not be checked in to 29 | % the version control system. Thank you to Jonas Dorn for suggesting this 30 | % approach. 31 | 32 | % 10/01/2013 - Access files in text, not binary mode, as latter can cause 33 | % errors. Thanks to Christian for pointing this out. 34 | % 29/05/2015 - Save file in prefdir if current folder is non-writable (issue #74) 35 | 36 | if ~ischar(string_name) 37 | error('string_name must be a string.'); 38 | end 39 | % Create the full filename 40 | fname = [string_name '.txt']; 41 | dname = fullfile(fileparts(mfilename('fullpath')), '.ignore'); 42 | file_name = fullfile(dname, fname); 43 | if nargin > 1 44 | % Set string 45 | if ~ischar(string) 46 | error('new_string must be a string.'); 47 | end 48 | % Make sure the save directory exists 49 | %dname = fileparts(file_name); 50 | if ~exist(dname, 'dir') 51 | % Create the directory 52 | try 53 | if ~mkdir(dname) 54 | string = false; 55 | return 56 | end 57 | catch 58 | string = false; 59 | return 60 | end 61 | % Make it hidden 62 | try 63 | fileattrib(dname, '+h'); 64 | catch 65 | end 66 | end 67 | % Write the file 68 | fid = fopen(file_name, 'wt'); 69 | if fid == -1 70 | % file cannot be created/updated - use prefdir if file does not already exist 71 | % (if file exists but is simply not writable, don't create a duplicate in prefdir) 72 | if ~exist(file_name,'file') 73 | file_name = fullfile(prefdir, fname); 74 | fid = fopen(file_name, 'wt'); 75 | end 76 | if fid == -1 77 | string = false; 78 | return; 79 | end 80 | end 81 | try 82 | fprintf(fid, '%s', string); 83 | catch 84 | fclose(fid); 85 | string = false; 86 | return 87 | end 88 | fclose(fid); 89 | string = true; 90 | else 91 | % Get string 92 | fid = fopen(file_name, 'rt'); 93 | if fid == -1 94 | % file cannot be read, try to read the file in prefdir 95 | file_name = fullfile(prefdir, fname); 96 | fid = fopen(file_name, 'rt'); 97 | if fid == -1 98 | string = ''; 99 | return 100 | end 101 | end 102 | string = fgetl(fid); 103 | fclose(fid); 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /src/external/export-fig/using_hg2.m: -------------------------------------------------------------------------------- 1 | %USING_HG2 Determine if the HG2 graphics engine is used 2 | % 3 | % tf = using_hg2(fig) 4 | % 5 | %IN: 6 | % fig - handle to the figure in question. 7 | % 8 | %OUT: 9 | % tf - boolean indicating whether the HG2 graphics engine is being used 10 | % (true) or not (false). 11 | 12 | % 19/06/2015 - Suppress warning in R2015b; cache result for improved performance 13 | % 06/06/2016 - Fixed issue #156 (bad return value in R2016b) 14 | 15 | function tf = using_hg2(fig) 16 | persistent tf_cached 17 | if isempty(tf_cached) 18 | try 19 | if nargin < 1, fig = figure('visible','off'); end 20 | oldWarn = warning('off','MATLAB:graphicsversion:GraphicsVersionRemoval'); 21 | try 22 | % This generates a [supressed] warning in R2015b: 23 | tf = ~graphicsversion(fig, 'handlegraphics'); 24 | catch 25 | tf = ~verLessThan('matlab','8.4'); % =R2014b 26 | end 27 | warning(oldWarn); 28 | catch 29 | tf = false; 30 | end 31 | if nargin < 1, delete(fig); end 32 | tf_cached = tf; 33 | else 34 | tf = tf_cached; 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /src/external/motchallenge-devkit/seqmaps/DukeMTMCT-test-easy.txt: -------------------------------------------------------------------------------- 1 | name 2 | cam1_test_easy 3 | cam2_test_easy 4 | cam3_test_easy 5 | cam4_test_easy 6 | cam5_test_easy 7 | cam6_test_easy 8 | cam7_test_easy 9 | cam8_test_easy -------------------------------------------------------------------------------- /src/external/motchallenge-devkit/seqmaps/DukeMTMCT-test-hard.txt: -------------------------------------------------------------------------------- 1 | name 2 | cam1_test_hard 3 | cam2_test_hard 4 | cam3_test_hard 5 | cam4_test_hard 6 | cam5_test_hard 7 | cam6_test_hard 8 | cam7_test_hard 9 | cam8_test_hard -------------------------------------------------------------------------------- /src/external/motchallenge-devkit/seqmaps/DukeMTMCT-trainval-mini.txt: -------------------------------------------------------------------------------- 1 | name 2 | cam1_trainval_mini 3 | cam2_trainval_mini 4 | cam3_trainval_mini 5 | cam4_trainval_mini 6 | cam5_trainval_mini 7 | cam6_trainval_mini 8 | cam7_trainval_mini 9 | cam8_trainval_mini -------------------------------------------------------------------------------- /src/external/motchallenge-devkit/seqmaps/DukeMTMCT-trainval.txt: -------------------------------------------------------------------------------- 1 | name 2 | cam1_trainval 3 | cam2_trainval 4 | cam3_trainval 5 | cam4_trainval 6 | cam5_trainval 7 | cam6_trainval 8 | cam7_trainval 9 | cam8_trainval -------------------------------------------------------------------------------- /src/external/motchallenge-devkit/utils/IDmeasures.m: -------------------------------------------------------------------------------- 1 | % Tracking Performance Measures as described in the paper: 2 | % 3 | % Performance Measures and a Data Set for Multi-Target, Multi-Camera Tracking. 4 | % E. Ristani, F. Solera, R. S. Zou, R. Cucchiara and C. Tomasi. 5 | % ECCV 2016 Workshop on Benchmarking Multi-Target Tracking. 6 | % 7 | % Ergys Ristani 8 | % Duke University 2016 9 | 10 | function [measures] = IDmeasures( groundTruthMat, predictionMat, threshold, world ) 11 | % Input: 12 | % groundTruthMat - frame, ID, left, top, width, height, worldX, worldY 13 | % predictionMat - frame, ID, left, top, width, height, worldX, worldY 14 | % threshold - Ground plane distance (1m) or intersection_over_union 15 | % world - boolean paramenter determining if the evaluation is 16 | % done in the world ground plane or in the image plane 17 | 18 | % Convert input trajectories from .top format to cell arrays. Each cell has 19 | % data for one identity. 20 | idsPred = unique(predictionMat(:,2)); 21 | idsGT = unique(groundTruthMat(:,2)); 22 | ground_truth = cell(length(idsGT),1); 23 | prediction = cell(length(idsPred),1); 24 | for i = 1:length(idsGT) 25 | ground_truth{i} = groundTruthMat(groundTruthMat(:,2) == idsGT(i),:); 26 | end 27 | for i = 1:length(idsPred) 28 | prediction{i} = predictionMat(predictionMat(:,2) == idsPred(i),:); 29 | end 30 | 31 | % Initialize cost matrix blocks 32 | cost = zeros(length(prediction) + length(ground_truth)); 33 | cost( length(ground_truth) + 1:end, 1:length(prediction)) = inf; 34 | cost( 1:length(ground_truth), length(prediction) + 1 : end) = inf; 35 | 36 | fp = zeros(size(cost)); 37 | fn = zeros(size(cost)); 38 | 39 | % Compute cost block 40 | [costBlock, fpBlock, fnBlock] = costBlockMex(ground_truth, prediction, threshold, world); 41 | % for i = 1:length(ground_truth) 42 | % for j = 1:length(prediction) 43 | % [costBlock(i,j), fpBlock(i,j), fnBlock(i,j)] = costFunction(ground_truth{i}, prediction{j}, threshold, world); 44 | % end 45 | % end 46 | cost(1:size(costBlock,1),1:size(costBlock,2)) = costBlock; 47 | fp(1:size(costBlock,1),1:size(costBlock,2)) = fpBlock; 48 | fn(1:size(costBlock,1),1:size(costBlock,2)) = fnBlock; 49 | 50 | % Compute FP block 51 | for i = 1:length(prediction) 52 | cost(i+length(ground_truth),i) = size(prediction{i},1); 53 | fp(i+length(ground_truth),i) = size(prediction{i},1); 54 | end 55 | 56 | % Compute FN block 57 | for i = 1:length(ground_truth) 58 | cost(i,i+length(prediction)) = size(ground_truth{i},1); 59 | fn(i,i+length(prediction)) = size(ground_truth{i},1); 60 | 61 | end 62 | 63 | % Solve truth-to-result identity matching 64 | [optimalMatch, totalCost] = MinCostMatching(cost); 65 | for i = 1:size(optimalMatch,1) 66 | assignment(i) = find(optimalMatch(i,:)); 67 | end 68 | 69 | % For visualization 70 | % solutionMatrix = zeros(size(cost)); 71 | % for i = 1:length(assignment), solutionMatrix(i,assignment(i)) = 1; end 72 | 73 | numGT = sum(cellfun(@(x) size(x,1), ground_truth)); 74 | numPRED = sum(cellfun(@(x) size(x,1), prediction)); 75 | 76 | % Count assignment errors 77 | IDFP = 0; 78 | IDFN = 0; 79 | for i = 1:length(assignment) 80 | IDFP = IDFP + fp(i,assignment(i)); 81 | IDFN = IDFN + fn(i,assignment(i)); 82 | end 83 | 84 | IDTP = numGT - IDFN; 85 | % Sanity check 86 | assert(IDTP == numPRED - IDFP); 87 | 88 | IDPrecision = IDTP / (IDTP + IDFP); 89 | if numPRED == 0, IDPrecision = 0; end 90 | IDRecall = IDTP / (IDTP + IDFN); 91 | IDF1 = 2*IDTP/(numGT + numPRED); 92 | 93 | measures.IDP = IDPrecision * 100; 94 | measures.IDR = IDRecall * 100; 95 | measures.IDF1 = IDF1 * 100; 96 | measures.numGT = numGT; 97 | measures.numPRED = numPRED; 98 | measures.IDTP = IDTP; 99 | measures.IDFP = IDFP; 100 | measures.IDFN = IDFN; 101 | 102 | 103 | -------------------------------------------------------------------------------- /src/external/motchallenge-devkit/utils/MinCostMatching.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | // Min cost bipartite matching via shortest augmenting paths 7 | // 8 | // Code from https://github.com/jaehyunp/ 9 | // 10 | // This is an O(n^3) implementation of a shortest augmenting path 11 | // algorithm for finding min cost perfect matchings in dense 12 | // graphs. In practice, it solves 1000x1000 problems in around 1 13 | // second. 14 | // 15 | // cost[i][j] = cost for pairing left node i with right node j 16 | // Lmate[i] = index of right node that left node i pairs with 17 | // Rmate[j] = index of left node that right node j pairs with 18 | // 19 | // The values in cost[i][j] may be positive or negative. To perform 20 | // maximization, simply negate the cost[][] matrix. 21 | 22 | 23 | 24 | typedef vector VD; 25 | typedef vector VVD; 26 | typedef vector VI; 27 | 28 | double MinCostMatching(const VVD &cost, VI &Lmate, VI &Rmate) { 29 | int n = int(cost.size()); 30 | 31 | // construct dual feasible solution 32 | VD u(n); 33 | VD v(n); 34 | for (int i = 0; i < n; i++) { 35 | u[i] = cost[i][0]; 36 | for (int j = 1; j < n; j++) u[i] = min(u[i], cost[i][j]); 37 | } 38 | for (int j = 0; j < n; j++) { 39 | v[j] = cost[0][j] - u[0]; 40 | for (int i = 1; i < n; i++) v[j] = min(v[j], cost[i][j] - u[i]); 41 | } 42 | 43 | // construct primal solution satisfying complementary slackness 44 | Lmate = VI(n, -1); 45 | Rmate = VI(n, -1); 46 | int mated = 0; 47 | for (int i = 0; i < n; i++) { 48 | for (int j = 0; j < n; j++) { 49 | if (Rmate[j] != -1) continue; 50 | if (fabs(cost[i][j] - u[i] - v[j]) < 1e-10) { 51 | Lmate[i] = j; 52 | Rmate[j] = i; 53 | mated++; 54 | break; 55 | } 56 | } 57 | } 58 | 59 | VD dist(n); 60 | VI dad(n); 61 | VI seen(n); 62 | 63 | // repeat until primal solution is feasible 64 | while (mated < n) { 65 | 66 | // find an unmatched left node 67 | int s = 0; 68 | while (Lmate[s] != -1) s++; 69 | 70 | // initialize Dijkstra 71 | fill(dad.begin(), dad.end(), -1); 72 | fill(seen.begin(), seen.end(), 0); 73 | for (int k = 0; k < n; k++) 74 | dist[k] = cost[s][k] - u[s] - v[k]; 75 | 76 | int j = 0; 77 | while (true) { 78 | 79 | // find closest 80 | j = -1; 81 | for (int k = 0; k < n; k++) { 82 | if (seen[k]) continue; 83 | if (j == -1 || dist[k] < dist[j]) j = k; 84 | } 85 | seen[j] = 1; 86 | 87 | // termination condition 88 | if (Rmate[j] == -1) break; 89 | 90 | // relax neighbors 91 | const int i = Rmate[j]; 92 | for (int k = 0; k < n; k++) { 93 | if (seen[k]) continue; 94 | const double new_dist = dist[j] + cost[i][k] - u[i] - v[k]; 95 | if (dist[k] > new_dist) { 96 | dist[k] = new_dist; 97 | dad[k] = j; 98 | } 99 | } 100 | } 101 | 102 | // update dual variables 103 | for (int k = 0; k < n; k++) { 104 | if (k == j || !seen[k]) continue; 105 | const int i = Rmate[k]; 106 | v[k] += dist[k] - dist[j]; 107 | u[i] -= dist[k] - dist[j]; 108 | } 109 | u[s] += dist[j]; 110 | 111 | // augment along path 112 | while (dad[j] >= 0) { 113 | const int d = dad[j]; 114 | Rmate[j] = Rmate[d]; 115 | Lmate[Rmate[j]] = j; 116 | j = d; 117 | } 118 | Rmate[j] = s; 119 | Lmate[s] = j; 120 | 121 | mated++; 122 | } 123 | 124 | double value = 0; 125 | for (int i = 0; i < n; i++) 126 | value += cost[i][Lmate[i]]; 127 | 128 | return value; 129 | } 130 | 131 | void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] ) 132 | { 133 | /* Input arguments */ 134 | int nOfRows = mxGetM(prhs[0]); 135 | int nOfColumns = mxGetN(prhs[0]); 136 | double* distMatrix = mxGetPr(prhs[0]); 137 | 138 | /* Output arguments */ 139 | plhs[0] = mxCreateDoubleMatrix(nOfRows, nOfColumns, mxREAL); 140 | plhs[1] = mxCreateDoubleMatrix(1, 1, mxREAL); 141 | double *assignment = mxGetPr(plhs[0]); 142 | double *cost = mxGetPr(plhs[1]); 143 | 144 | if (nOfRows == 0 || nOfColumns == 0) 145 | return; 146 | 147 | /* Call C-function */ 148 | double INF = 1e8; 149 | int squareSize = max(nOfRows, nOfColumns); 150 | vector> alldist(squareSize, vector(squareSize, INF)); 151 | for (int i = 0; i < nOfRows; i++) 152 | { 153 | for (int j =0; j < nOfColumns; j++) 154 | { 155 | alldist[i][j] = distMatrix[j*nOfRows + i]; 156 | } 157 | } 158 | 159 | vector Lmate, Rmate; 160 | MinCostMatching(alldist, Lmate, Rmate); 161 | 162 | cost[0] = 0; 163 | for (int i = 0; i < nOfRows; i++) 164 | { 165 | if (Lmate[i] < nOfColumns && alldist[i][Lmate[i]] != INF) 166 | { 167 | assignment[Lmate[i] * nOfRows + i] = 1; 168 | cost[0] += alldist[i][Lmate[i]]; 169 | } 170 | } 171 | 172 | } 173 | 174 | -------------------------------------------------------------------------------- /src/external/motchallenge-devkit/utils/costFunction.m: -------------------------------------------------------------------------------- 1 | function [cost, fp, fn] = costFunction( tr1, tr2, THRESHOLD, world ) 2 | 3 | frames_gt = tr1(:,2); 4 | frames_pred = tr2(:,2); 5 | 6 | % If trajectories don't overlap in time then return cost or inf 7 | overlapTest = (frames_gt(1) >= frames_pred(1) && frames_gt(1) < frames_pred(end)) || ... 8 | (frames_gt(end) >= frames_pred(1) && frames_gt(end) <= frames_pred(end)) || ... 9 | (frames_pred(1) >= frames_gt(1) && frames_pred(1) <= frames_gt(end)) || ... 10 | (frames_pred(end) >= frames_gt(1) && frames_pred(end) <= frames_gt(end)); 11 | 12 | if ~overlapTest 13 | fp = length(frames_pred); 14 | fn = length(frames_gt); 15 | cost = fp + fn; 16 | return; 17 | end 18 | 19 | [isfoundGT, posGT] = ismember_mex(frames_gt, frames_pred); 20 | [isfoundPred, posPred] = ismember_mex(frames_pred, frames_gt); 21 | 22 | % Use points at infinity when no match exists 23 | columns = [7,8]; 24 | if ~world 25 | columns = [3 4 5 6]; 26 | end 27 | 28 | % Ground truth data and the corresponding data from the prediction 29 | pointsGT = tr1(isfoundGT, columns); 30 | pointsGTPred = tr2(posGT(isfoundGT),columns); 31 | 32 | % Prediction data and the corresponding data from ground truth 33 | pointsPred = tr2(isfoundPred, columns); 34 | pointsPredGT = tr1(posPred(isfoundPred),columns); 35 | 36 | unmatchedGT = sum(isfoundGT==0); 37 | unmatchedPred = sum(isfoundPred==0); 38 | 39 | distanceGTvsPred = distanceFunction(pointsGT, pointsGTPred, world); 40 | distancePredvsGT = distanceFunction(pointsPred, pointsPredGT, world); 41 | 42 | if world % Euclidean distance test 43 | fn = unmatchedGT + sum( distanceGTvsPred > THRESHOLD ); 44 | fp = unmatchedPred + sum( distancePredvsGT > THRESHOLD ); 45 | else % IntersectionOverUnion test 46 | fn = unmatchedGT + sum( distanceGTvsPred < THRESHOLD ); 47 | fp = unmatchedPred + sum( distancePredvsGT < THRESHOLD ); 48 | end 49 | cost = fp + fn; 50 | 51 | % Sanity check 52 | tp1 = length(frames_gt) - fn; 53 | tp2 = length(frames_pred) - fp; 54 | 55 | assert(tp1 == tp2, 'Something is wrong in the input. Make sure there are no duplicate frames...'); 56 | 57 | 58 | end 59 | 60 | function distance = distanceFunction(point1, point2, world) 61 | 62 | if world 63 | 64 | % Euclidean distance 65 | distance = sqrt(sum(abs(point1 - point2).^2,2)); 66 | 67 | else 68 | 69 | % Intersection_over_union 70 | box1 = point1; 71 | box2 = point2; 72 | 73 | area1 = box1(:,3) .* box1(:,4); 74 | area2 = box2(:,3) .* box2(:,4); 75 | 76 | l1 = box1(:,1); r1 = box1(:,1) + box1(:,3); t1 = box1(:,2); b1 = box1(:,2) + box1(:,4); 77 | l2 = box2(:,1); r2 = box2(:,1) + box2(:,3); t2 = box2(:,2); b2 = box2(:,2) + box2(:,4); 78 | 79 | x_overlap = max(0, min(r1,r2) - max(l1,l2)); 80 | y_overlap = max(0, min(b1,b2) - max(t1,t2)); 81 | intersectionArea = x_overlap .* y_overlap; 82 | unionArea = area1 + area2 - intersectionArea; 83 | iou = intersectionArea ./ unionArea; 84 | 85 | distance = iou; 86 | 87 | end 88 | 89 | 90 | 91 | end 92 | 93 | -------------------------------------------------------------------------------- /src/external/motchallenge-devkit/utils/evaluateBenchmark.m: -------------------------------------------------------------------------------- 1 | function metsBenchmark = evaluateBenchmark( allMets, world ) 2 | 3 | 4 | % Aggregate scores from all sequences 5 | MT = 0; PT = 0; ML = 0; FRA = 0; 6 | falsepositives = 0; missed = 0; idswitches = 0; 7 | Fgt = 0; distsum = 0; Ngt = 0; sumg = 0; 8 | Nc = 0; 9 | numGT = 0; numPRED = 0; IDTP = 0; IDFP = 0; IDFN = 0; 10 | 11 | for ind = 1:length(allMets) 12 | if isempty(allMets(ind).m) 13 | fprintf('\n\nResults missing for sequence #%d\n', ind) 14 | continue; 15 | end 16 | numGT = numGT + allMets(ind).IDmeasures.numGT; 17 | numPRED = numPRED + allMets(ind).IDmeasures.numPRED; 18 | IDTP = IDTP + allMets(ind).IDmeasures.IDTP; 19 | IDFN = IDFN + allMets(ind).IDmeasures.IDFN; 20 | IDFP = IDFP + allMets(ind).IDmeasures.IDFP; 21 | 22 | MT = MT + allMets(ind).additionalInfo.MT; 23 | PT = PT + allMets(ind).additionalInfo.PT; 24 | ML = ML + allMets(ind).additionalInfo.ML; 25 | FRA = FRA + allMets(ind).additionalInfo.FRA; 26 | Fgt = Fgt + allMets(ind).additionalInfo.Fgt; 27 | Ngt = Ngt + allMets(ind).additionalInfo.Ngt; 28 | Nc = Nc + sum(allMets(ind).additionalInfo.c); 29 | sumg = sumg + sum(allMets(ind).additionalInfo.g); 30 | falsepositives = falsepositives + sum(allMets(ind).additionalInfo.fp); 31 | missed = missed + sum(allMets(ind).additionalInfo.m); 32 | idswitches = idswitches + sum(allMets(ind).additionalInfo.mme); 33 | dists = allMets(ind).additionalInfo.d; 34 | td = allMets(ind).additionalInfo.td; 35 | distsum = distsum + sum(sum(dists)); 36 | end 37 | 38 | IDPrecision = IDTP / (IDTP + IDFP); 39 | IDRecall = IDTP / (IDTP + IDFN); 40 | IDF1 = 2*IDTP/(numGT + numPRED); 41 | if numPRED==0, IDPrecision = 0; end 42 | IDP = IDPrecision * 100; 43 | IDR = IDRecall * 100; 44 | IDF1 = IDF1 * 100; 45 | 46 | 47 | FAR = falsepositives / Fgt; 48 | MOTP = (1-distsum/Nc) * 100; 49 | if world, MOTP = MOTP / td; end 50 | if isnan(MOTP), MOTP = 0; end 51 | MOTAL=(1-(missed+falsepositives+log10(idswitches+1))/sumg)*100; 52 | MOTA=(1-(missed+falsepositives+idswitches)/sumg)*100; 53 | recall=Nc/sumg*100; 54 | precision=Nc/(falsepositives+Nc)*100; 55 | 56 | metsBenchmark = [IDF1, IDP, IDR, recall, precision, FAR, Ngt, MT, PT, ML, falsepositives, missed, idswitches, FRA, MOTA, MOTP, MOTAL]; 57 | 58 | end 59 | 60 | -------------------------------------------------------------------------------- /src/external/motchallenge-devkit/utils/evaluateMultiCam.m: -------------------------------------------------------------------------------- 1 | function [ metsMultiCam ] = evaluateMultiCam( gtMat, resMat, threshold, world ) 2 | %EVALUATEMULTICAM Summary of this function goes here 3 | % Detailed explanation goes here 4 | % Prepare data for overall evaluation 5 | gtMatAll = []; 6 | resMatAll = []; 7 | countF = 0; 8 | for k = 1:length(gtMat) 9 | newF = max(max(gtMat{k}(:,1)),max(resMat{k}(:,1))); 10 | if isempty(newF), newF = 0; end 11 | gtMat{k}(:,1) = gtMat{k}(:,1) + countF; 12 | resMat{k}(:,1) = resMat{k}(:,1) + countF; 13 | countF = countF + newF; 14 | gtMatAll = [gtMatAll; gtMat{k}]; 15 | resMatAll = [resMatAll; resMat{k}]; 16 | end 17 | 18 | metsID = IDmeasures(gtMatAll, resMatAll, threshold, world); 19 | metsMultiCam = [metsID.IDF1, metsID.IDP, metsID.IDR]; 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/external/motchallenge-devkit/utils/parseSequences2.m: -------------------------------------------------------------------------------- 1 | function allseq = parseSequences2(seqmapFile) 2 | % parse sequence map 3 | % seqmapFile - a file containing the sequence names 4 | % to be processed. First line is ignored. e.g. 5 | % 6 | % -------------- 7 | % name 8 | % TUD-Stadtmitte 9 | % TUD-Campus 10 | % -------------- 11 | % 12 | % returns a cell array with all the sequence names 13 | 14 | 15 | assert(exist(seqmapFile,'file')>0,'seqmap file %s does not exist',seqmapFile); 16 | fid = fopen(seqmapFile); 17 | allseq = textscan(fid,'%s','HeaderLines',1); 18 | fclose(fid); 19 | allseq=allseq{1}'; 20 | 21 | 22 | end 23 | -------------------------------------------------------------------------------- /src/external/motchallenge-devkit/utils/printMetrics.m: -------------------------------------------------------------------------------- 1 | function printMetrics(metrics, metricsInfo, dispHeader,dispMetrics,padChar) 2 | % print metrics 3 | % 4 | % ... 5 | % 6 | % extended version with ID Measures 7 | if length(metrics)==17 8 | printMetricsExt(metrics) 9 | return; 10 | end 11 | 12 | % Detections MODP/MODA metrics 13 | if length(metrics)==9 14 | printMetricsDet(metrics) 15 | return; 16 | end 17 | 18 | % default names 19 | if nargin==1 20 | metricsInfo.names.long = {'Recall','Precision','False Alarm Rate', ... 21 | 'GT Tracks','Mostly Tracked','Partially Tracked','Mostly Lost', ... 22 | 'False Positives', 'False Negatives', 'ID Switches', 'Fragmentations', ... 23 | 'MOTA','MOTP', 'MOTA Log'}; 24 | 25 | metricsInfo.names.short = {'Rcll','Prcn','FAR', ... 26 | 'GT','MT','PT','ML', ... 27 | 'FP', 'FN', 'IDs', 'FM', ... 28 | 'MOTA','MOTP', 'MOTAL'}; 29 | 30 | metricsInfo.widths.long = [6 9 16 9 14 17 11 15 15 11 14 5 5 8]; 31 | metricsInfo.widths.short = [5 5 5 4 4 4 4 6 6 5 5 5 5 5]; 32 | 33 | metricsInfo.format.long = {'.1f','.1f','.2f', ... 34 | 'i','i','i','i', ... 35 | 'i','i','i','i', ... 36 | '.1f','.1f','.1f'}; 37 | 38 | metricsInfo.format.short=metricsInfo.format.long; 39 | end 40 | 41 | namesToDisplay=metricsInfo.names.long; 42 | widthsToDisplay=metricsInfo.widths.long; 43 | formatToDisplay=metricsInfo.format.long; 44 | 45 | namesToDisplay=metricsInfo.names.short; 46 | widthsToDisplay=metricsInfo.widths.short; 47 | formatToDisplay=metricsInfo.format.short; 48 | 49 | if nargin<3, dispHeader=1; end 50 | if nargin<4 51 | dispMetrics=1:length(metrics)-1; 52 | end 53 | if nargin<5 54 | padChar={' ',' ','|',' ',' ',' ','|',' ',' ',' ','| ',' ',' ',' '}; 55 | end 56 | 57 | if dispHeader 58 | for m=dispMetrics 59 | printString=sprintf('fprintf(''%%%is%s'',char(namesToDisplay(m)))',widthsToDisplay(m),char(padChar(m))); 60 | eval(printString) 61 | end 62 | fprintf('\n'); 63 | end 64 | 65 | for m=dispMetrics 66 | printString=sprintf('fprintf(''%%%i%s%s'',metrics(m))',widthsToDisplay(m),char(formatToDisplay(m)),char(padChar(m))); 67 | eval(printString) 68 | end 69 | 70 | % if standard, new line 71 | if nargin<4 72 | fprintf('\n'); 73 | end -------------------------------------------------------------------------------- /src/external/motchallenge-devkit/utils/printMetricsExt.m: -------------------------------------------------------------------------------- 1 | function printMetricsExt(metrics, metricsInfo, dispHeader,dispMetrics,padChar) 2 | % print metrics 3 | % 4 | % ... 5 | % 6 | 7 | % default names 8 | if nargin==1 9 | metricsInfo.names.long = {'IDF1', 'IDP', 'IDR', 'Recall','Precision','False Alarm Rate', ... 10 | 'GT Tracks','Mostly Tracked','Partially Tracked','Mostly Lost', ... 11 | 'False Positives', 'False Negatives', 'ID Switches', 'Fragmentations', ... 12 | 'MOTA','MOTP', 'MOTA Log'}; 13 | 14 | metricsInfo.names.short = {'IDF1', 'IDP', 'IDR', 'Rcll','Prcn','FAR', ... 15 | 'GT','MT','PT','ML', ... 16 | 'FP', 'FN', 'IDs', 'FM', ... 17 | 'MOTA','MOTP', 'MOTAL'}; 18 | 19 | metricsInfo.widths.long = [5 4 4 6 9 16 9 14 17 11 15 15 11 14 5 5 8]; 20 | metricsInfo.widths.short = [5 4 4 5 5 5 4 4 4 4 6 6 5 5 5 5 5]; 21 | 22 | metricsInfo.format.long = {'.1f','.1f','.1f', ... 23 | '.1f','.1f','.2f', ... 24 | 'i','i','i','i', ... 25 | 'i','i','i','i', ... 26 | '.1f','.1f','.1f'}; 27 | 28 | metricsInfo.format.short=metricsInfo.format.long; 29 | end 30 | 31 | namesToDisplay=metricsInfo.names.long; 32 | widthsToDisplay=metricsInfo.widths.long; 33 | formatToDisplay=metricsInfo.format.long; 34 | 35 | namesToDisplay=metricsInfo.names.short; 36 | widthsToDisplay=metricsInfo.widths.short; 37 | formatToDisplay=metricsInfo.format.short; 38 | 39 | if nargin<3, dispHeader=1; end 40 | if nargin<4 41 | dispMetrics=1:length(metrics); 42 | end 43 | if nargin<5 44 | padChar={' ',' ','|',' ',' ','| ','',' ',' ','|','',' ',' ','| ',' ',' ',' ',' '}; 45 | end 46 | 47 | if dispHeader 48 | for m=dispMetrics 49 | printString=sprintf('fprintf(''%%%is%s'',char(namesToDisplay(m)))',widthsToDisplay(m),char(padChar(m))); 50 | eval(printString) 51 | end 52 | fprintf('\n'); 53 | end 54 | 55 | for m=dispMetrics 56 | printString=sprintf('fprintf(''%%%i%s%s'',metrics(m))',widthsToDisplay(m),char(formatToDisplay(m)),char(padChar(m))); 57 | eval(printString) 58 | end 59 | 60 | % if standard, new line 61 | if nargin<4 62 | fprintf('\n'); 63 | end -------------------------------------------------------------------------------- /src/external/nestedSortStruct/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Jake Hughey 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /src/external/nestedSortStruct/sortStruct.m: -------------------------------------------------------------------------------- 1 | function [sortedStruct index] = sortStruct(aStruct, fieldName, direction) 2 | % [sortedStruct index] = sortStruct(aStruct, fieldName, direction) 3 | % sortStruct returns a sorted struct array, and can also return an index vector. The 4 | % (one-dimensional) struct array (aStruct) is sorted based on the field specified by the 5 | % string fieldName. The field must a single number or logical, or a char array (usually a 6 | % simple string). 7 | % 8 | % direction is an optional argument to specify whether the struct array should be sorted 9 | % in ascending or descending order. By default, the array will be sorted in ascending 10 | % order. If supplied, direction must equal 1 to sort in ascending order or -1 to sort in 11 | % descending order. 12 | 13 | %% check inputs 14 | if ~isstruct(aStruct) 15 | error('first input supplied is not a struct.') 16 | end % if 17 | 18 | if sum(size(aStruct)>1)>1 % if more than one non-singleton dimension 19 | error('I don''t want to sort your multidimensional struct array.') 20 | end % if 21 | 22 | if ~ischar(fieldName) || ~isfield(aStruct, fieldName) 23 | error('second input is not a valid fieldname.') 24 | end % if 25 | 26 | if nargin < 3 27 | direction = 1; 28 | elseif ~isnumeric(direction) || numel(direction)>1 || ~ismember(direction, [-1 1]) 29 | error('direction must equal 1 for ascending order or -1 for descending order.') 30 | end % if 31 | 32 | %% figure out the field's class, and find the sorted index vector 33 | fieldEntry = aStruct(1).(fieldName); 34 | 35 | if (isnumeric(fieldEntry) || islogical(fieldEntry)) && numel(fieldEntry) == 1 % if the field is a single number 36 | [dummy index] = sort([aStruct.(fieldName)]); 37 | elseif ischar(fieldEntry) % if the field is char 38 | [dummy index] = sort({aStruct.(fieldName)}); 39 | else 40 | error('%s is not an appropriate field by which to sort.', fieldName) 41 | end % if ~isempty 42 | 43 | %% apply the index to the struct array 44 | if direction == 1 % ascending sort 45 | sortedStruct = aStruct(index); 46 | else % descending sort 47 | sortedStruct = aStruct(index(end:-1:1)); 48 | end -------------------------------------------------------------------------------- /src/external/nestedSortStruct/sortStruct2.m: -------------------------------------------------------------------------------- 1 | function [sortedStruct index] = sortStruct2(aStruct, fieldName, direction, check) 2 | % [sortedStruct index] = sortStruct2(aStruct, fieldName, direction) 3 | % sortStruct2 returns a sorted struct array, and can also return an index vector. The 4 | % (one-dimensional) struct array is sorted based on the field specified by the string 5 | % fieldName. The field must a single number or logical, or a char array (usually a simple 6 | % string). 7 | % 8 | % direction is an optional argument to specify whether the struct array should be sorted 9 | % in ascending or descending order. By default, the array will be sorted in ascending 10 | % order. If supplied, direction must equal 1 to sort in ascending order or -1 to sort in 11 | % descending order. 12 | % 13 | % check is an optional argument that should not be used if calling sortStruct2 directly. 14 | % Calls to sortStruct2 by nestedSortStruct2 use check to bypass checking inputs. If check 15 | % equals 1 or is not given, inputs are checked. If check equals 0, inputs are not checked. 16 | 17 | %% check inputs 18 | if nargin < 4 || check 19 | if ~isstruct(aStruct) 20 | error('first input supplied is not a struct.') 21 | end % if 22 | 23 | if sum(size(aStruct)>1)>1 % if more than one non-singleton dimension 24 | error('I don''t want to sort your multidimensional struct array.') 25 | end % if 26 | 27 | if ~ischar(fieldName) || ~isfield(aStruct, fieldName) 28 | error('second input is not a valid fieldname.') 29 | end % if 30 | 31 | if nargin < 3 32 | direction = 1; 33 | elseif ~isnumeric(direction) || numel(direction)>1 || ~ismember(direction, [-1 1]) 34 | error('direction must equal 1 for ascending order or -1 for descending order.') 35 | end % if 36 | end % if check 37 | 38 | %% figure out the field's class, and find the sorted index vector 39 | fieldEntry = aStruct(1).(fieldName); 40 | 41 | if (isnumeric(fieldEntry) || islogical(fieldEntry)) && numel(fieldEntry) == 1 % if the field is a single number 42 | [dummy index] = sort([aStruct.(fieldName)]); 43 | elseif ischar(fieldEntry) % if the field is char 44 | [dummy index] = sort({aStruct.(fieldName)}); 45 | else 46 | error('%s is not an appropriate field by which to sort.', fieldName) 47 | end % if ~isempty 48 | 49 | %% apply the index to the struct array 50 | if direction == 1 % ascending sort 51 | sortedStruct = aStruct(index); 52 | else % descending sort 53 | sortedStruct = aStruct(index(end:-1:1)); 54 | end -------------------------------------------------------------------------------- /src/triplet-reid/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Visual Computing Institute 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/triplet-reid/aggregators.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def mean(embs): 5 | return np.mean(embs, axis=0) 6 | 7 | 8 | def normalized_mean(embs): 9 | embs = mean(embs) 10 | return embs / np.linalg.norm(embs, axis=1, keepdims=True) 11 | 12 | 13 | AGGREGATORS = { 14 | 'mean': mean, 15 | 'normalized_mean': normalized_mean, 16 | } 17 | -------------------------------------------------------------------------------- /src/triplet-reid/embed.m: -------------------------------------------------------------------------------- 1 | function embed(opts) 2 | 3 | net = opts.net; 4 | 5 | cur_dir = pwd; 6 | cd src/triplet-reid 7 | 8 | datasets = {'data/duke_test.csv', 'data/duke_query.csv', 'data/duke_train.csv'}; 9 | 10 | for k = 1:length(datasets) 11 | dataset = datasets{k}; 12 | 13 | command = strcat(opts.python3, ' embed.py' , ... 14 | sprintf(' --experiment_root %s', net.experiment_root), ... 15 | sprintf(' --image_root %s', net.image_root), ... 16 | sprintf(' --dataset %s', dataset)); 17 | 18 | system(command); 19 | 20 | 21 | end 22 | 23 | cd(cur_dir) -------------------------------------------------------------------------------- /src/triplet-reid/embed_detections.m: -------------------------------------------------------------------------------- 1 | function features = embed_detections(opts, detections) 2 | % Computes feature embeddings for given detections 3 | % Detections are in format [cam, frame, left, top, width, height] 4 | net = opts.net; 5 | 6 | % Detection images read in python and embedded 7 | cur_dir = pwd; 8 | cd src/triplet-reid 9 | 10 | % Temporary file to store the bounding box coordinates 11 | features_filename = fullfile(net.experiment_root, 'temp_features.h5'); 12 | detections_filename = fullfile(net.experiment_root, 'temp_detections.mat'); 13 | save(detections_filename, 'detections'); 14 | 15 | command = strcat(opts.python3, ' embed_detections.py' , ... 16 | sprintf(' --experiment_root %s', net.experiment_root), ... 17 | sprintf(' --dataset_path %s', opts.dataset_path), ... 18 | sprintf(' --detections_path %s', detections_filename), ... 19 | sprintf(' --filename %s', features_filename)); 20 | system(command); 21 | 22 | % Load features / delete temp files 23 | features = h5read(features_filename, '/emb'); 24 | delete(detections_filename); 25 | delete(features_filename); 26 | cd(cur_dir) -------------------------------------------------------------------------------- /src/triplet-reid/excluders/diagonal.py: -------------------------------------------------------------------------------- 1 | class Excluder(object): 2 | def __init__(self, gallery_fids): 3 | # Store the gallery data 4 | self.gallery_fids = gallery_fids 5 | 6 | def __call__(self, query_fids): 7 | # Only make sure we don't match the exact same image. 8 | return self.gallery_fids[None] == query_fids[..., None] 9 | -------------------------------------------------------------------------------- /src/triplet-reid/excluders/market1501.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | import numpy as np 5 | 6 | 7 | class Excluder: 8 | """ 9 | In the Market1501 evaluation, we need to exclude both the same PID in 10 | the same camera (CID), as well as "junk" images (PID=-1). 11 | """ 12 | def __init__(self, gallery_fids): 13 | # Setup a regexp for extracing the PID and camera (CID) form a FID. 14 | self.regexp = re.compile('(\S+)_c(\d+)s(\d+)_.*') 15 | 16 | # Parse the gallery_set 17 | self.gallery_pids, self.gallery_cids = self._parse(gallery_fids) 18 | 19 | def __call__(self, query_fids): 20 | # Extract both the PIDs and CIDs from the query filenames: 21 | query_pids, query_cids = self._parse(query_fids) 22 | 23 | # Ignore same pid image within the same camera 24 | cid_matches = self.gallery_cids[None] == query_cids[:,None] 25 | pid_matches = self.gallery_pids[None] == query_pids[:,None] 26 | mask = np.logical_and(cid_matches, pid_matches) 27 | 28 | # Remove all "junk" with the -1 pid. 29 | junk_images = np.repeat(self.gallery_pids[None] == '-1', len(query_pids), 0) 30 | mask = np.logical_or(mask, junk_images) 31 | 32 | return mask 33 | 34 | def _parse(self, fids): 35 | """ Return the PIDs and CIDs extracted from the FIDs. """ 36 | pids = [] 37 | cids = [] 38 | for fid in fids: 39 | filename = os.path.splitext(os.path.basename(fid))[0] 40 | pid, cid, _ = self.regexp.match(filename).groups() 41 | pids.append(pid) 42 | cids.append(cid) 43 | return np.asarray(pids), np.asarray(cids) 44 | -------------------------------------------------------------------------------- /src/triplet-reid/heads/__init__.py: -------------------------------------------------------------------------------- 1 | # Used for the commandline flags. 2 | HEAD_CHOICES = ( 3 | 'direct', 4 | 'direct_normalize', 5 | 'fc1024', 6 | 'fc1024_normalize', 7 | ) 8 | -------------------------------------------------------------------------------- /src/triplet-reid/heads/direct.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | from tensorflow.contrib import slim 3 | 4 | def head(endpoints, embedding_dim, is_training): 5 | endpoints['emb'] = endpoints['emb_raw'] = slim.fully_connected( 6 | endpoints['model_output'], embedding_dim, activation_fn=None, 7 | weights_initializer=tf.orthogonal_initializer(), scope='emb') 8 | 9 | return endpoints 10 | -------------------------------------------------------------------------------- /src/triplet-reid/heads/direct_normalize.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | from tensorflow.contrib import slim 3 | 4 | def head(endpoints, embedding_dim, is_training): 5 | endpoints['emb_raw'] = slim.fully_connected( 6 | endpoints['model_output'], embedding_dim, activation_fn=None, 7 | weights_initializer=tf.orthogonal_initializer(), scope='emb') 8 | endpoints['emb'] = tf.nn.l2_normalize(endpoints['emb_raw'], -1) 9 | 10 | return endpoints 11 | -------------------------------------------------------------------------------- /src/triplet-reid/heads/fc1024.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | from tensorflow.contrib import slim 3 | 4 | def head(endpoints, embedding_dim, is_training): 5 | endpoints['head_output'] = slim.fully_connected( 6 | endpoints['model_output'], 1024, normalizer_fn=slim.batch_norm, 7 | normalizer_params={ 8 | 'decay': 0.9, 9 | 'epsilon': 1e-5, 10 | 'scale': True, 11 | 'is_training': is_training, 12 | 'updates_collections': tf.GraphKeys.UPDATE_OPS, 13 | }) 14 | 15 | endpoints['emb'] = endpoints['emb_raw'] = slim.fully_connected( 16 | endpoints['head_output'], embedding_dim, activation_fn=None, 17 | weights_initializer=tf.orthogonal_initializer(), scope='emb') 18 | 19 | return endpoints 20 | -------------------------------------------------------------------------------- /src/triplet-reid/heads/fc1024_normalize.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | from tensorflow.contrib import slim 3 | 4 | def head(endpoints, embedding_dim, is_training): 5 | endpoints['head_output'] = slim.fully_connected( 6 | endpoints['model_output'], 1024, normalizer_fn=slim.batch_norm, 7 | normalizer_params={ 8 | 'decay': 0.9, 9 | 'epsilon': 1e-5, 10 | 'scale': True, 11 | 'is_training': is_training, 12 | 'updates_collections': tf.GraphKeys.UPDATE_OPS, 13 | }) 14 | 15 | endpoints['emb_raw'] = slim.fully_connected( 16 | endpoints['head_output'], embedding_dim, activation_fn=None, 17 | weights_initializer=tf.orthogonal_initializer(), scope='emb') 18 | endpoints['emb'] = tf.nn.l2_normalize(endpoints['emb_raw'], -1) 19 | 20 | return endpoints 21 | -------------------------------------------------------------------------------- /src/triplet-reid/nets/README.md: -------------------------------------------------------------------------------- 1 | The following files are copy-pasted from TF-slim's model repository but had to be slightly adapted to fit in here. 2 | The original code is governed by the Apache 2.0 license. 3 | Any modifications by us are minor, but marked as such in the comments. 4 | 5 | These are the files concerned: 6 | 7 | - `resnet_utils.py` 8 | - `resnet_v1.py` 9 | - `mobilenet_v1.py` 10 | -------------------------------------------------------------------------------- /src/triplet-reid/nets/__init__.py: -------------------------------------------------------------------------------- 1 | # Used for the commandline flags. 2 | NET_CHOICES = ( 3 | 'mobilenet_v1_1_224', 4 | 'resnet_v1_50', 5 | 'resnet_v1_101', 6 | ) 7 | -------------------------------------------------------------------------------- /src/triplet-reid/nets/mobilenet_v1_1_224.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | 3 | from nets.mobilenet_v1 import mobilenet_v1 4 | from tensorflow.contrib import slim 5 | 6 | 7 | def endpoints(image, is_training): 8 | if image.get_shape().ndims != 4: 9 | raise ValueError('Input must be of size [batch, height, width, 3]') 10 | 11 | image = tf.divide(image, 255.0) 12 | 13 | with tf.contrib.slim.arg_scope(mobilenet_v1_arg_scope(batch_norm_decay=0.9, weight_decay=0.0)): 14 | _, endpoints = mobilenet_v1(image, num_classes=1001, is_training=is_training) 15 | 16 | endpoints['model_output'] = endpoints['global_pool'] = tf.reduce_mean( 17 | endpoints['Conv2d_13_pointwise'], [1, 2], name='global_pool', keep_dims=False) 18 | 19 | return endpoints, 'MobilenetV1' 20 | 21 | 22 | # This is copied and modified from mobilenet_v1.py. 23 | def mobilenet_v1_arg_scope(is_training=True, 24 | batch_norm_decay=0.9997, 25 | batch_norm_epsilon=0.001, 26 | batch_norm_scale=True, 27 | weight_decay=0.00004, 28 | stddev=0.09, 29 | regularize_depthwise=False): 30 | 31 | """Defines the default MobilenetV1 arg scope. 32 | Args: 33 | is_training: Whether or not we're training the model. 34 | batch_norm_decay: The moving average decay when estimating layer activation 35 | statistics in batch normalization. 36 | batch_norm_epsilon: Small constant to prevent division by zero when 37 | normalizing activations by their variance in batch normalization. 38 | batch_norm_scale: If True, uses an explicit `gamma` multiplier to scale the 39 | activations in the batch normalization layer. 40 | weight_decay: The weight decay to use for regularizing the model. 41 | stddev: The standard deviation of the trunctated normal weight initializer. 42 | regularize_depthwise: Whether or not apply regularization on depthwise. 43 | Returns: 44 | An `arg_scope` to use for the mobilenet v1 model. 45 | """ 46 | batch_norm_params = { 47 | 'is_training': is_training, 48 | 'center': True, 49 | 'scale': batch_norm_scale, 50 | 'decay': batch_norm_decay, 51 | 'epsilon': batch_norm_epsilon, 52 | } 53 | 54 | # Set weight_decay for weights in Conv and DepthSepConv layers. 55 | weights_init = tf.truncated_normal_initializer(stddev=stddev) 56 | regularizer = tf.contrib.layers.l2_regularizer(weight_decay) 57 | if regularize_depthwise: 58 | depthwise_regularizer = regularizer 59 | else: 60 | depthwise_regularizer = None 61 | with slim.arg_scope([slim.conv2d, slim.separable_conv2d], 62 | weights_initializer=weights_init, 63 | activation_fn=tf.nn.relu6, normalizer_fn=slim.batch_norm): 64 | with slim.arg_scope([slim.batch_norm], **batch_norm_params): 65 | with slim.arg_scope([slim.conv2d], weights_regularizer=regularizer): 66 | with slim.arg_scope([slim.separable_conv2d], 67 | weights_regularizer=depthwise_regularizer) as sc: 68 | return sc 69 | -------------------------------------------------------------------------------- /src/triplet-reid/nets/resnet_v1_101.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | 3 | from nets.resnet_v1 import resnet_v1_101, resnet_arg_scope 4 | 5 | _RGB_MEAN = [123.68, 116.78, 103.94] 6 | 7 | def endpoints(image, is_training): 8 | if image.get_shape().ndims != 4: 9 | raise ValueError('Input must be of size [batch, height, width, 3]') 10 | 11 | image = image - tf.constant(_RGB_MEAN, dtype=tf.float32, shape=(1,1,1,3)) 12 | 13 | with tf.contrib.slim.arg_scope(resnet_arg_scope(batch_norm_decay=0.9, weight_decay=0.0)): 14 | _, endpoints = resnet_v1_101(image, num_classes=None, is_training=is_training, global_pool=True) 15 | 16 | endpoints['model_output'] = endpoints['global_pool'] = tf.reduce_mean( 17 | endpoints['resnet_v1_101/block4'], [1, 2], name='pool5') 18 | 19 | return endpoints, 'resnet_v1_101' 20 | -------------------------------------------------------------------------------- /src/triplet-reid/nets/resnet_v1_50.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | 3 | from nets.resnet_v1 import resnet_v1_50, resnet_arg_scope 4 | 5 | _RGB_MEAN = [123.68, 116.78, 103.94] 6 | 7 | def endpoints(image, is_training): 8 | if image.get_shape().ndims != 4: 9 | raise ValueError('Input must be of size [batch, height, width, 3]') 10 | 11 | image = image - tf.constant(_RGB_MEAN, dtype=tf.float32, shape=(1,1,1,3)) 12 | 13 | with tf.contrib.slim.arg_scope(resnet_arg_scope(batch_norm_decay=0.9, weight_decay=0.0)): 14 | _, endpoints = resnet_v1_50(image, num_classes=None, is_training=is_training, global_pool=True) 15 | 16 | endpoints['model_output'] = endpoints['global_pool'] = tf.reduce_mean( 17 | endpoints['resnet_v1_50/block4'], [1, 2], name='pool5') 18 | 19 | return endpoints, 'resnet_v1_50' 20 | -------------------------------------------------------------------------------- /src/triplet-reid/train_duke.m: -------------------------------------------------------------------------------- 1 | function train_duke(opts) 2 | 3 | net = opts.net; 4 | 5 | command = strcat(opts.python3, ' train.py' , ... 6 | sprintf(' --train_set %s', net.train_set), ... 7 | sprintf(' --model_name %s', net.model_name), ... 8 | sprintf(' --initial_checkpoint %s', net.initial_checkpoint), ... 9 | sprintf(' --image_root %s', net.image_root), ... 10 | sprintf(' --experiment_root %s', net.experiment_root), ... 11 | sprintf(' --embedding_dim %d', net.embedding_dim), ... 12 | sprintf(' --batch_p %d', net.batch_p), ... 13 | sprintf(' --batch_k %d', net.batch_k), ... 14 | sprintf(' --pre_crop_height %d --pre_crop_width %d', net.pre_crop_height, net.pre_crop_width), ... 15 | sprintf(' --net_input_height %d --net_input_width %d', net.input_height, net.input_width), ... 16 | sprintf(' --margin %s', net.margin), ... 17 | sprintf(' --metric %s', net.metric), ... 18 | sprintf(' --loss %s', net.loss), ... 19 | sprintf(' --learning_rate %ld', net.learning_rate), ... 20 | sprintf(' --train_iterations %d',net.train_iterations), ... 21 | sprintf(' --hard_pool_size %d',net.hard_pool_size), ... 22 | sprintf(' --decay_start_iteration %d', net.decay_start_iteration), ... 23 | sprintf(' --checkpoint_frequency %d', net.checkpoint_frequency)); 24 | 25 | if net.augment 26 | command = [command, ' --augment']; 27 | end 28 | 29 | if net.resume 30 | command = [command, ' --resume']; 31 | end 32 | 33 | if net.hard_pool_size > 0 34 | command = [command, ' --train_embeddings ', net.train_embeddings]; 35 | end 36 | 37 | cur_dir = pwd; 38 | cd src/triplet-reid 39 | system(command); 40 | cd(cur_dir) -------------------------------------------------------------------------------- /src/triplet-reid/train_duke.sh: -------------------------------------------------------------------------------- 1 | python train.py \ 2 | --train_set data/duke_train.csv \ 3 | --model_name resnet_v1_50 \ 4 | --image_root /usr/project/xtmp/ristani/DeepCC/ReID/DukeMTMC-reID \ 5 | --experiment_root C:/Users/Ergys/Documents/MATLAB/DeepCC_release/experiments/demo/models/duke_demo \ 6 | --embedding_dim 128 \ 7 | --batch_p 18 \ 8 | --batch_k 4 \ 9 | --pre_crop_height 288 --pre_crop_width 144 \ 10 | --net_input_height 256 --net_input_width 128 \ 11 | --margin soft \ 12 | --metric euclidean \ 13 | --loss weighted_triplet \ 14 | --learning_rate 3e-4 \ 15 | --train_iterations 25000 \ 16 | --decay_start_iteration 15000 \ 17 | --augment 18 | --checkpoint_frequency 1000 \ 19 | "$@" 20 | -------------------------------------------------------------------------------- /src/triplet-reid/train_duke_hnm.m: -------------------------------------------------------------------------------- 1 | opts = get_opts(); 2 | 3 | % Run few iterations to obtain a preliminary embedding 4 | tmp = opts; 5 | tmp.net.experiment_root = 'experiments/demo_hnm_tmp'; 6 | tmp.net.train_iterations = 5000; 7 | train_duke(tmp); 8 | embed(tmp); 9 | 10 | % Train with hard negative mining 11 | opts.net.experiment_root = 'experiments/demo_hnm'; 12 | opts.net.hard_pool_size = 50; 13 | opts.net.train_embeddings = sprintf('%s/src/triplet-reid/%s/duke_train_embeddings.h5',pwd,tmp.net.experiment_root); 14 | train_duke(opts); 15 | embed(opts); 16 | evaluation_res_duke_fast(opts); -------------------------------------------------------------------------------- /src/util/create_experiment_dir.m: -------------------------------------------------------------------------------- 1 | function create_experiment_dir(opts) 2 | warning off; 3 | mkdir([opts.experiment_root, filesep, opts.experiment_name]); 4 | mkdir([opts.experiment_root, filesep, opts.experiment_name, filesep, 'L0-features']); 5 | mkdir([opts.experiment_root, filesep, opts.experiment_name, filesep, 'L1-tracklets']); 6 | mkdir([opts.experiment_root, filesep, opts.experiment_name, filesep, 'L2-trajectories']); 7 | mkdir([opts.experiment_root, filesep, opts.experiment_name, filesep, 'L3-identities']); 8 | -------------------------------------------------------------------------------- /src/util/error_types.m: -------------------------------------------------------------------------------- 1 | % Computes the types of error (IDFP, IDFN) to be used for visualization or 2 | % diagnorsing the tracker result 3 | 4 | % Tracking Performance Measures as described in the paper: 5 | % 6 | % Performance Measures and a Data Set for Multi-Target, Multi-Camera Tracking. 7 | % E. Ristani, F. Solera, R. S. Zou, R. Cucchiara and C. Tomasi. 8 | % ECCV 2016 Workshop on Benchmarking Multi-Target Tracking. 9 | % 10 | % Ergys Ristani 11 | % Duke University 2016 12 | 13 | function [gtMat2, predMat2] = error_types( groundTruthMat, predictionMat, threshold, world ) 14 | % Input: 15 | % groundTruthMat - frame, ID, left, top, width, height, worldX, worldY 16 | % predictionMat - frame, ID, left, top, width, height, worldX, worldY 17 | % threshold - Ground plane distance (1m) or intersection_over_union 18 | % world - boolean paramenter determining if the evaluation is 19 | % done in the world ground plane or in the image plane 20 | 21 | % Convert input trajectories from .top format to cell arrays. Each cell has 22 | % data for one identity. 23 | idsPred = unique(predictionMat(:,2)); 24 | idsGT = unique(groundTruthMat(:,2)); 25 | ground_truth = cell(length(idsGT),1); 26 | prediction = cell(length(idsPred),1); 27 | for i = 1:length(idsGT) 28 | ground_truth{i} = groundTruthMat(groundTruthMat(:,2) == idsGT(i),:); 29 | ground_truth{i}(:,end+1) = 0; 30 | end 31 | for i = 1:length(idsPred) 32 | prediction{i} = predictionMat(predictionMat(:,2) == idsPred(i),:); 33 | prediction{i}(:,end+1) = 0; 34 | end 35 | 36 | % Initialize cost matrix blocks 37 | cost = zeros(length(prediction) + length(ground_truth)); 38 | cost( length(ground_truth) + 1:end, 1:length(prediction)) = inf; 39 | cost( 1:length(ground_truth), length(prediction) + 1 : end) = inf; 40 | 41 | fp = zeros(size(cost)); 42 | fn = zeros(size(cost)); 43 | 44 | % Compute cost block 45 | [costBlock, fpBlock, fnBlock] = costBlockMex(ground_truth, prediction, threshold, world); 46 | % for i = 1:length(ground_truth) 47 | % for j = 1:length(prediction) 48 | % [costBlock(i,j), fpBlock(i,j), fnBlock(i,j)] = costFunction(ground_truth{i}, prediction{j}, threshold, world); 49 | % end 50 | % end 51 | cost(1:size(costBlock,1),1:size(costBlock,2)) = costBlock; 52 | fp(1:size(costBlock,1),1:size(costBlock,2)) = fpBlock; 53 | fn(1:size(costBlock,1),1:size(costBlock,2)) = fnBlock; 54 | 55 | % Compute FP block 56 | for i = 1:length(prediction) 57 | cost(i+length(ground_truth),i) = size(prediction{i},1); 58 | fp(i+length(ground_truth),i) = size(prediction{i},1); 59 | end 60 | 61 | % Compute FN block 62 | for i = 1:length(ground_truth) 63 | cost(i,i+length(prediction)) = size(ground_truth{i},1); 64 | fn(i,i+length(prediction)) = size(ground_truth{i},1); 65 | 66 | end 67 | 68 | % Solve truth-to-result identity matching 69 | [optimalMatch, totalCost] = MinCostMatching(cost); 70 | for i = 1:size(optimalMatch,1) 71 | assignment(i) = find(optimalMatch(i,:)); 72 | end 73 | 74 | % For visualization 75 | % solutionMatrix = zeros(size(cost)); 76 | % for i = 1:length(assignment), solutionMatrix(i,assignment(i)) = 1; end 77 | 78 | numGT = sum(cellfun(@(x) size(x,1), ground_truth)); 79 | numPRED = sum(cellfun(@(x) size(x,1), prediction)); 80 | 81 | % Count assignment errors 82 | IDFP = 0; 83 | IDFN = 0; 84 | for i = 1:length(assignment) 85 | IDFP = IDFP + fp(i,assignment(i)); 86 | IDFN = IDFN + fn(i,assignment(i)); 87 | end 88 | 89 | IDTP = numGT - IDFN; 90 | % Sanity check 91 | assert(IDTP == numPRED - IDFP); 92 | 93 | IDPrecision = IDTP / (IDTP + IDFP); 94 | if numPRED == 0, IDPrecision = 0; end 95 | IDRecall = IDTP / (IDTP + IDFN); 96 | IDF1 = 2*IDTP/(numGT + numPRED); 97 | 98 | measures.IDP = IDPrecision * 100; 99 | measures.IDR = IDRecall * 100; 100 | measures.IDF1 = IDF1 * 100; 101 | measures.numGT = numGT; 102 | measures.numPRED = numPRED; 103 | measures.IDTP = IDTP; 104 | measures.IDFP = IDFP; 105 | measures.IDFN = IDFN; 106 | 107 | gtTP = cell(length(idsGT),1); 108 | predTP = cell(length(idsPred),1); 109 | sumTP = 0; 110 | for i = 1:length(idsGT) 111 | if assignment(i) <= length(predTP) 112 | [gtTP{i}, predTP{assignment(i)}] = matching_errors(ground_truth{i},prediction{assignment(i)}, threshold, world); 113 | ground_truth{i}(:,end) = gtTP{i}; 114 | prediction{assignment(i)}(:,end) = predTP{assignment(i)}; 115 | 116 | idrec(i) = sum(gtTP{i}) / size(ground_truth{i},1); 117 | sumTP = sumTP + sum(gtTP{i}); 118 | end 119 | end 120 | 121 | gtMat2 = []; 122 | for k = 1:length(ground_truth) 123 | gtMat2 = [gtMat2; ground_truth{k}]; 124 | end 125 | predMat2 = []; 126 | for k = 1:length(prediction) 127 | predMat2 = [predMat2; prediction{k}]; 128 | end 129 | 130 | end 131 | 132 | 133 | -------------------------------------------------------------------------------- /src/util/feetPosition.m: -------------------------------------------------------------------------------- 1 | function [ feet ] = feetPosition( boxes ) 2 | %FEETPOSITION Summary of this function goes here 3 | % Detailed explanation goes here 4 | 5 | x = boxes(:,1) + boxes(:,3)/2; 6 | y = boxes(:,2) + boxes(:,4); 7 | feet = [x, y]; 8 | 9 | end 10 | 11 | -------------------------------------------------------------------------------- /src/util/getBoundingBoxCenters.m: -------------------------------------------------------------------------------- 1 | function [ centers ] = getBoundingBoxCenters( boundingBoxes ) 2 | % Returns the centers of the bounding boxes provided in the format: 3 | % left, top, right, botom 4 | 5 | centers = [ boundingBoxes(:,1) + 0.5*boundingBoxes(:,3), boundingBoxes(:,2) + 0.5* boundingBoxes(:,4)]; 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/util/getSpatialGroupIDs.m: -------------------------------------------------------------------------------- 1 | function spatialGroupIDs = getSpatialGroupIDs(useGrouping, currentDetectionsIDX, detectionCenters, params ) 2 | % Perfroms spatial groupping of detections and returns a vector of IDs 3 | 4 | spatialGroupIDs = ones(length(currentDetectionsIDX), 1); 5 | if useGrouping == true 6 | 7 | pairwiseDistances = pdist2(detectionCenters, detectionCenters); 8 | agglomeration = linkage(pairwiseDistances); 9 | numSpatialGroups = round(params.cluster_coeff * length(currentDetectionsIDX) / params.window_width); 10 | numSpatialGroups = max(numSpatialGroups, 1); 11 | 12 | while true 13 | 14 | spatialGroupIDs = cluster(agglomeration, 'maxclust', numSpatialGroups); 15 | uid = unique(spatialGroupIDs); 16 | freq = [histc(spatialGroupIDs(:),uid)]; 17 | 18 | largestGroupSize = max(freq); 19 | % The BIP solver might run out of memory for large graphs 20 | if largestGroupSize <= 150 21 | return 22 | end 23 | 24 | numSpatialGroups = numSpatialGroups + 1; 25 | 26 | end 27 | 28 | end 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/util/get_bb.m: -------------------------------------------------------------------------------- 1 | function snapshot = get_bb( img, bb ) 2 | %SHOW_BB Summary of this function goes here 3 | % Detailed explanation goes here 4 | bb = round(bb); 5 | snapshot = img(max(1,bb(2)):min(1080,bb(2)+bb(4)),max(1,bb(1)):min(1920,bb(1)+bb(3)),:); 6 | 7 | end 8 | 9 | -------------------------------------------------------------------------------- /src/util/identities2mat.m: -------------------------------------------------------------------------------- 1 | function data = identities2mat( identities ) 2 | 3 | data = zeros(0,8); 4 | 5 | for i = 1:length(identities) 6 | 7 | identity = identities(i); 8 | 9 | for k = 1:length(identity.trajectories) 10 | 11 | newdata = identity.trajectories(k).data; 12 | newdata(:,2) = i; 13 | cam_data = identity.trajectories(k).camera * ones(size(identity.trajectories(k).data,1),1) ; 14 | data = [data; cam_data, newdata]; 15 | 16 | 17 | end 18 | 19 | end 20 | 21 | -------------------------------------------------------------------------------- /src/util/intervalSearch.m: -------------------------------------------------------------------------------- 1 | function result = intervalSearch( data, first, last ) 2 | %INTERVALSEARCH Returns indices of data elements that are within the range 3 | %[first, last] 4 | result = find((data >= first) & (data <= last)); 5 | 6 | end 7 | 8 | -------------------------------------------------------------------------------- /src/util/matching_errors.m: -------------------------------------------------------------------------------- 1 | function [gtTP, predTP] = matching_errors( tr1, tr2, THRESHOLD, world ) 2 | 3 | tr1(:,[1 2]) = tr1(:,[2 1]); 4 | tr2(:,[1 2]) = tr2(:,[2 1]); 5 | frames_gt = tr1(:,2); 6 | frames_pred = tr2(:,2); 7 | 8 | % If trajectories don't overlap in time then return cost or inf 9 | overlapTest = (frames_gt(1) >= frames_pred(1) && frames_gt(1) < frames_pred(end)) || ... 10 | (frames_gt(end) >= frames_pred(1) && frames_gt(end) <= frames_pred(end)) || ... 11 | (frames_pred(1) >= frames_gt(1) && frames_pred(1) <= frames_gt(end)) || ... 12 | (frames_pred(end) >= frames_gt(1) && frames_pred(end) <= frames_gt(end)); 13 | 14 | if ~overlapTest 15 | fp = length(frames_pred); 16 | fn = length(frames_gt); 17 | cost = fp + fn; 18 | gtTP = zeros(size(tr1,1),1); 19 | predTP = zeros(size(tr2,1),1); 20 | return; 21 | end 22 | 23 | [isfoundGT, posGT] = ismember(frames_gt, frames_pred); 24 | [isfoundPred, posPred] = ismember(frames_pred, frames_gt); 25 | 26 | % Use points at infinity when no match exists 27 | columns = [7,8]; 28 | if ~world 29 | columns = [3 4 5 6]; 30 | end 31 | 32 | % Ground truth data and the corresponding data from the prediction 33 | pointsGT = tr1(isfoundGT, columns); 34 | pointsGTPred = tr2(posGT(isfoundGT),columns); 35 | 36 | % Prediction data and the corresponding data from ground truth 37 | pointsPred = tr2(isfoundPred, columns); 38 | pointsPredGT = tr1(posPred(isfoundPred),columns); 39 | 40 | unmatchedGT = sum(isfoundGT==0); 41 | unmatchedPred = sum(isfoundPred==0); 42 | 43 | distanceGTvsPred = distanceFunction(pointsGT, pointsGTPred, world); 44 | distancePredvsGT = distanceFunction(pointsPred, pointsPredGT, world); 45 | 46 | if world % Euclidean distance test 47 | fn = unmatchedGT + sum( distanceGTvsPred > THRESHOLD ); 48 | fp = unmatchedPred + sum( distancePredvsGT > THRESHOLD ); 49 | else % IntersectionOverUnion test 50 | fn = unmatchedGT + sum( distanceGTvsPred < THRESHOLD ); 51 | fp = unmatchedPred + sum( distancePredvsGT < THRESHOLD ); 52 | end 53 | cost = fp + fn; 54 | 55 | % Sanity check 56 | tp1 = length(frames_gt) - fn; 57 | tp2 = length(frames_pred) - fp; 58 | 59 | assert(tp1 == tp2, 'Something is wrong in the input. Make sure there are no duplicate frames...'); 60 | 61 | % Missing detections 62 | gtTP = isfoundGT; 63 | predTP = isfoundPred; 64 | 65 | % Detections too far 66 | gtTooFar = find(distanceGTvsPred < THRESHOLD); 67 | predTooFar = find(distancePredvsGT < THRESHOLD); 68 | indsGTFound = find(isfoundGT); 69 | indsPredFound = find(isfoundPred); 70 | 71 | gtTP(indsGTFound(gtTooFar)) = 0; 72 | predTP(indsPredFound(predTooFar)) = 0; 73 | 74 | end 75 | 76 | function distance = distanceFunction(point1, point2, world) 77 | 78 | if world 79 | 80 | % Euclidean distance 81 | distance = sqrt(sum(abs(point1 - point2).^2,2)); 82 | 83 | else 84 | 85 | % Intersection_over_union 86 | box1 = point1; 87 | box2 = point2; 88 | 89 | area1 = box1(:,3) .* box1(:,4); 90 | area2 = box2(:,3) .* box2(:,4); 91 | 92 | l1 = box1(:,1); r1 = box1(:,1) + box1(:,3); t1 = box1(:,2); b1 = box1(:,2) + box1(:,4); 93 | l2 = box2(:,1); r2 = box2(:,1) + box2(:,3); t2 = box2(:,2); b2 = box2(:,2) + box2(:,4); 94 | 95 | x_overlap = max(0, min(r1,r2) - max(l1,l2)); 96 | y_overlap = max(0, min(b1,b2) - max(t1,t2)); 97 | intersectionArea = x_overlap .* y_overlap; 98 | unionArea = area1 + area2 - intersectionArea; 99 | iou = intersectionArea ./ unionArea; 100 | 101 | distance = iou; 102 | 103 | end 104 | 105 | 106 | 107 | end 108 | 109 | -------------------------------------------------------------------------------- /src/util/pose2bb.m: -------------------------------------------------------------------------------- 1 | function [ bb ] = pose2bb( pose, renderThreshold ) 2 | %POSETOBB Summary of this function goes here 3 | % Detailed explanation goes here 4 | 5 | % Template pose 6 | ref_pose = [0 0; ... %nose 7 | 0, 23; ... % neck 8 | 28 23; ... % rshoulder 9 | 39 66; ... %relbow 10 | 45 108; ... %rwrist 11 | -28 23; ... % lshoulder 12 | -39 66; ... %lelbow 13 | -45 108; ... %lwrist 14 | 20 106; ... %rhip 15 | 20 169; ... %rknee 16 | 20 231; ... %rankle 17 | -20 106; ... %lhip 18 | -20 169; ... %lknee 19 | -20 231; ... %lankle 20 | 5 -7; ... %reye 21 | 11 -8; ... %rear 22 | -5 -7; ... %leye 23 | -11 -8; ... %lear 24 | ]; 25 | 26 | % Template bounding box 27 | ref_bb = [-50, -15; ...%left top 28 | 50, 240]; % right bottom 29 | 30 | pose = reshape(pose,[3,18])'; 31 | valid = pose(:,1) ~= 0 & pose(:,2) ~=0 & pose(:,3) >= renderThreshold; 32 | 33 | if sum(valid) < 2 34 | bb = [0 0 0 0]; 35 | return; 36 | end 37 | 38 | points_det = pose(valid,[1 2]); 39 | points_reference = ref_pose(valid,:); 40 | 41 | % 1a) Compute minimum enclosing rectangle 42 | 43 | base_left = min(points_det(:,1)); 44 | base_top = min(points_det(:,2)); 45 | base_right = max(points_det(:,1)); 46 | base_bottom = max(points_det(:,2)); 47 | 48 | % 1b) Fit pose to template 49 | 50 | % Find transformation parameters 51 | M = size(points_det,1); 52 | B = points_det(:); 53 | A = [points_reference(:,1) zeros(M,1) ones(M,1) zeros(M,1); ... 54 | zeros(M,1) points_reference(:,2) zeros(M,1) ones(M,1)]; 55 | 56 | params = A \ B; 57 | M = 2; 58 | A2 = [ref_bb(:,1) zeros(M,1) ones(M,1) zeros(M,1); ... 59 | zeros(M,1) ref_bb(:,2) zeros(M,1) ones(M,1)]; 60 | 61 | % Visualize 62 | % C = A*params; 63 | % figure(2); 64 | % clf('reset'); 65 | % scatter(B(1:end/2),-B(end/2+1:end),'b'); axis equal; hold on 66 | % scatter(C(1:end/2),-C(end/2+1:end),'r+'); axis equal; hold on 67 | result = A2 * params; 68 | 69 | fit_left = min(result([1,2])); 70 | fit_top = min(result([3,4])); 71 | fit_right = max(result([1,2])); 72 | fit_bottom = max(result([3,4])); 73 | 74 | % 2. Fuse bounding boxes 75 | left = min(base_left,fit_left); 76 | top = min(base_top,fit_top); 77 | right = max(base_right,fit_right); 78 | bottom = max(base_bottom,fit_bottom); 79 | 80 | left = left*1920; 81 | top = top*1080; 82 | right = right*1920; 83 | bottom = bottom*1080; 84 | 85 | height = bottom - top + 1; 86 | width = right - left + 1; 87 | 88 | bb = [left, top, width, height]; 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /src/util/renderPoses.m: -------------------------------------------------------------------------------- 1 | function img = renderPoses( img, poses ) 2 | % Renders OpenPose detections in Matlab 3 | POSE_COCO_PAIRS = 1 + [1,2, 1,5, 2,3, 3,4, 5,6, 6,7, 1,8, 8,9, 9,10, 1,11, 11,12, 12,13, 1,0, 0,14, 14,16, 0,15, 15,17, 2,16, 5,17] ; 4 | POSE_COCO_COLORS_RENDER_GPU = ... 5 | [255, 0, 85; ... 6 | 255, 0, 0; ... 7 | 255, 85, 0; ... 8 | 255, 170, 0; ... 9 | 255, 255, 0; ... 10 | 170, 255, 0; ... 11 | 85, 255, 0; ... 12 | 0, 255, 0; ... 13 | 0, 255, 85; ... 14 | 0, 255, 170; ... 15 | 0, 255, 255; ... 16 | 0, 170, 255; ... 17 | 0, 85, 255; ... 18 | 0, 0, 255; ... 19 | 255, 0, 170; ... 20 | 170, 0, 255; ... 21 | 255, 0, 255; ... 22 | 85, 0, 255]; 23 | renderThreshold = 0.05; 24 | 25 | % // Model-Dependent Parameters 26 | % // COCO 27 | % const std::map POSE_COCO_BODY_PARTS { 28 | % {0, "Nose"}, 29 | % {1, "Neck"}, 30 | % {2, "RShoulder"}, 31 | % {3, "RElbow"}, 32 | % {4, "RWrist"}, 33 | % {5, "LShoulder"}, 34 | % {6, "LElbow"}, 35 | % {7, "LWrist"}, 36 | % {8, "RHip"}, 37 | % {9, "RKnee"}, 38 | % {10, "RAnkle"}, 39 | % {11, "LHip"}, 40 | % {12, "LKnee"}, 41 | % {13, "LAnkle"}, 42 | % {14, "REye"}, 43 | % {15, "LEye"}, 44 | % {16, "REar"}, 45 | % {17, "LEar"}, 46 | % {18, "Background"} 47 | % }; 48 | 49 | % Detailed explanation goes here 50 | for iPose = 1:size(poses,1) 51 | pose = poses(iPose,:); 52 | % % draw circles 53 | % for iPart = 1:18 54 | % hold on; 55 | % plot(1920*pose(3*iPart-2), 1080*pose(3*iPart-1)); 56 | % end 57 | 58 | % draw lines 59 | for iPair = 1:length(POSE_COCO_PAIRS)/2 60 | 61 | if pose(3*POSE_COCO_PAIRS(2*iPair-1) - 0) < renderThreshold || ... 62 | pose(3*POSE_COCO_PAIRS(2*iPair) - 0) < renderThreshold || ... 63 | pose(3*POSE_COCO_PAIRS(2*iPair-1) - 2) == 0 || ... 64 | pose(3*POSE_COCO_PAIRS(2*iPair) - 2) == 0 || ... 65 | pose(3*POSE_COCO_PAIRS(2*iPair-1) - 1) == 0 || ... 66 | pose(3*POSE_COCO_PAIRS(2*iPair) - 1) == 0 67 | continue; 68 | end 69 | 70 | hold on; 71 | pt1 = [size(img,2)*pose(3*POSE_COCO_PAIRS(2*iPair-1) - 2), size(img,1)*pose(3*POSE_COCO_PAIRS(2*iPair-1) - 1)]; 72 | pt2 = [size(img,2)*pose(3*POSE_COCO_PAIRS(2*iPair) - 2), size(img,1)*pose(3*POSE_COCO_PAIRS(2*iPair) - 1)]; 73 | color = POSE_COCO_COLORS_RENDER_GPU(mod(iPair-1,18)+1,:); 74 | % img = cv.line(img, pt1, pt2, 'Thickness', 2, 'Color', color); 75 | minx = max(1, floor(min(pt1(1),pt2(1)))); 76 | miny = max(1, floor(min(pt1(2),pt2(2)))); 77 | maxx = min(1920, ceil(max(pt1(1),pt2(1)))); 78 | maxy = min(1080, ceil(max(pt1(2),pt2(2)))); 79 | subimg = img(miny:maxy,minx:maxx,:); 80 | subimg = cv.line(subimg, pt1 - [minx, miny], pt2-[minx, miny], 'Thickness', 2, 'Color', color); 81 | img(miny:maxy,minx:maxx,:) = subimg; 82 | end 83 | 84 | end 85 | 86 | end 87 | 88 | -------------------------------------------------------------------------------- /src/util/scale_bb.m: -------------------------------------------------------------------------------- 1 | function [ newbb, newpose ] = scale_bb( bb, pose, scalingFactor ) 2 | % Scales bounding box by scaling factor 3 | 4 | newbb([1,2]) = bb([1,2]) - 0.5*(scalingFactor-1) * bb([3,4]); 5 | newbb([3,4]) = bb([3,4]) * scalingFactor; 6 | 7 | % X, Y, strength 8 | newpose = reshape(pose,3,18)'; 9 | 10 | % Scale to original bounding box 11 | newpose(:,1) = (newpose(:,1) - bb(1)/1920.0) / (bb(3)/1920.0); 12 | newpose(:,2) = (newpose(:,2) - bb(2)/1080.0) / (bb(4)/1080.0); 13 | 14 | % Scale to stretched bounding box 15 | newpose(:,1) = (newpose(:,1) + 0.5*(scalingFactor-1))/scalingFactor; 16 | newpose(:,2) = (newpose(:,2) + 0.5*(scalingFactor-1))/scalingFactor; 17 | 18 | % Return in the original format 19 | newpose(newpose(:,3)==0,[1 2]) = 0; 20 | newpose = newpose'; 21 | newpose = newpose(:); 22 | newpose = newpose'; 23 | -------------------------------------------------------------------------------- /src/visualization/data/duke_easy_scores.txt: -------------------------------------------------------------------------------- 1 | 67.0376 48.4422 83.5614 60.3825 2 | 67.0376 48.4422 83.5614 60.3825 3 | 68.8362 49.7086 88.159 63.6622 4 | 72.6222 48.2122 88.0984 58.4864 5 | 68.3192 53.4863 87.6443 68.6157 6 | 71.1437 60.5927 87.2972 74.3506 7 | 71.1437 60.5927 87.2972 74.3506 8 | 73.8175 69.0928 82.8243 77.5231 9 | 84.4251 66.4456 89.8731 70.7334 10 | 82.6388 74.3368 91.1633 82.0049 11 | 84.3691 79.8149 91.6953 86.7457 12 | 13 | -------------------------------------------------------------------------------- /src/visualization/data/duke_hard_scores.txt: -------------------------------------------------------------------------------- 1 | 59.566 39.211 81.2117 53.4598 2 | 59.566 39.211 81.2117 53.4598 3 | 60.5851 40.2177 80.4166 53.3823 4 | 60.5851 40.2177 80.4166 53.3823 5 | 58.2769 43.8663 73.9304 55.649 6 | 58.2769 43.8663 73.9304 55.649 7 | 63.2482 42.5523 81.3663 54.7417 8 | 69.4285 56.8633 80.6971 66.0925 9 | 78.1022 56.5077 85.3221 61.7313 10 | 78.6354 59.3885 90.1264 68.067 11 | 75.8566 62.4472 87.4329 71.9771 12 | 13 | -------------------------------------------------------------------------------- /src/visualization/data/map.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamvitJ/Duke-DeepCC/cfa233a00a819d409b5c7927e5fdad7719aaa87c/src/visualization/data/map.jpg -------------------------------------------------------------------------------- /src/visualization/render_results.m: -------------------------------------------------------------------------------- 1 | function render_results(opts) 2 | % Creates a movie for each camera view to help visualize errors 3 | % Requires that single-camera results exists in experiment folder L2-trajectories 4 | 5 | % IDTP - Green 6 | % IDFP - Blue 7 | % IDFN - Black 8 | tail_colors = [0 0 1; 0 1 0; 0 0 0]; 9 | tail_size = 100; 10 | 11 | colors = distinguishable_colors(1000); 12 | 13 | folder = 'video-results'; 14 | mkdir([opts.experiment_root, filesep, opts.experiment_name, filesep, folder]); 15 | 16 | % Load ground truth 17 | load(fullfile(opts.dataset_path, 'ground_truth', 'trainval.mat')); 18 | 19 | % Render one video per camera 20 | for iCam = 1:opts.num_cam 21 | 22 | % Create video 23 | filename = sprintf('%s/%s/%s/cam%d_%s.mp4',opts.experiment_root, opts.experiment_name, folder, iCam, opts.sequence_names{opts.sequence}); 24 | video = VideoWriter(filename, 'MPEG-4'); 25 | open(video); 26 | 27 | % Load result 28 | predMat = dlmread(sprintf('%s/%s/L2-trajectories/cam%d_%s.txt',opts.experiment_root, opts.experiment_name, iCam,opts.sequence_names{opts.sequence})); 29 | 30 | sequence_interval = opts.sequence_intervals{opts.sequence}; 31 | 32 | % Load relevant ground truth 33 | gtdata = trainData; 34 | filter = gtdata(:,1) == iCam & ismember(gtdata(:,3) + opts.start_frames(iCam) - 1, sequence_interval); 35 | gtdata = gtdata(filter,:); 36 | gtdata = gtdata(:,2:end); 37 | gtdata(:,[1 2]) = gtdata(:,[2 1]); 38 | gtdata = sortrows(gtdata,[1 2]); 39 | gtMat = gtdata; 40 | 41 | % Compute error types 42 | [gtMatViz, predMatViz] = error_types(gtMat,predMat,0.5,0); 43 | gtMatViz = sortrows(gtMatViz, [1 2]); 44 | predMatViz = sortrows(predMatViz, [1 2]); 45 | 46 | for iFrame = global2local(opts.start_frames(iCam), sequence_interval(1)):1:global2local(opts.start_frames(iCam),sequence_interval(end)) 47 | fprintf('Cam %d: %d/%d\n', iCam, iFrame, global2local(opts.start_frames(iCam),sequence_interval(end))); 48 | if mod(iFrame,5) >0 49 | continue; 50 | end 51 | image = opts.reader.getFrame(iCam,iFrame); 52 | 53 | rows = find(predMatViz(:, 1) == iFrame); 54 | identities = predMatViz(rows, 2); 55 | positions = [predMatViz(rows, 3), predMatViz(rows, 4), predMatViz(rows, 5), predMatViz(rows, 6)]; 56 | 57 | if ~isempty(positions) 58 | image = insertObjectAnnotation(image,'rectangle', ... 59 | positions, identities,'TextBoxOpacity', 0.8, 'FontSize', 16, 'Color', 255*colors(identities,:) ); 60 | end 61 | 62 | % Tail Pred 63 | rows = find((predMatViz(:, 1) <= iFrame) & (predMatViz(:,1) >= iFrame - tail_size)); 64 | identities = predMatViz(rows, 2); 65 | 66 | feetposition = feetPosition(predMatViz(rows,3:6)); 67 | is_TP = predMatViz(rows,end); 68 | current_tail_colors = []; 69 | for kkk = 1:length(is_TP) 70 | current_tail_colors(kkk,:) = tail_colors(is_TP(kkk)+1,:); 71 | end 72 | 73 | circles = feetposition; 74 | circles(:,3) = 3; 75 | image = insertShape(image,'FilledCircle',circles,'Color', current_tail_colors*255); 76 | 77 | % IDFN 78 | rows = find((gtMatViz(:, 1) <= iFrame) & (gtMatViz(:,1) >= iFrame - tail_size)); 79 | feetposition = feetPosition(gtMatViz(rows,3:6)); 80 | 81 | is_TP = gtMatViz(rows,end); 82 | current_tail_colors = []; 83 | for kkk = 1:length(is_TP) 84 | current_tail_colors(kkk,:) = tail_colors(3-is_TP(kkk),:); 85 | end 86 | circles = feetposition; 87 | circles(:,3) = 3; 88 | image = insertShape(image,'FilledCircle',circles(~is_TP,:),'Color', current_tail_colors(~is_TP,:)*255); 89 | image = insertText(image,[0 0], sprintf('Cam %d - Frame %d',iCam, iFrame),'FontSize',20); 90 | image = insertText(image,[0 40; 60 40; 120 40], {'IDTP', 'IDFP','IDFN'},'FontSize',20,'BoxColor',{'green','blue','black'},'TextColor',{'white','white','white'}); 91 | 92 | writeVideo(video, image); 93 | 94 | end 95 | close(video); 96 | 97 | end 98 | 99 | -------------------------------------------------------------------------------- /src/visualization/render_state_of_the_art.m: -------------------------------------------------------------------------------- 1 | % Shows state of the art on ID precision/recall plot 2 | % Scores need to be updated from MOTChallenge. To be used as reference. 3 | 4 | opts = get_opts(); 5 | 6 | close all; 7 | difficulty = {'Hard', 'Easy'}; 8 | scenario = {'Multi', 'Single'}; 9 | 10 | % Create folder 11 | folder = 'state-of-the-art'; 12 | mkdir([opts.experiment_root, filesep, opts.experiment_name, filesep, folder]); 13 | 14 | % Difficulty 15 | for ii = 1:2 16 | data = dlmread(sprintf('src/visualization/data/duke_%s_scores.txt',lower(difficulty{ii}))); 17 | 18 | % Scenario 19 | for jj = 1:2 20 | P = data(:,jj*2-1)/100; 21 | R = data(:,jj*2)/100; 22 | figure; 23 | clf('reset'); 24 | axis equal; 25 | axis([0 1 0 1]); 26 | % F-score isocontours 27 | for f = 0.2:0.1:0.9 28 | p = linspace(0.1,1,1000); 29 | r = (p*f)./(2*p-f); 30 | r(r<0) = 1.01; 31 | r(r>1) = 1.01; 32 | hold on; plot(p,r,'g-'); 33 | end 34 | 35 | hold on; scatter(P,R,'filled'); 36 | xlabel('ID Precision'); 37 | ylabel('ID Recall'); 38 | title(sprintf('DukeMTMC %s-Camera %s',scenario{jj},difficulty{ii})); 39 | set(gca, 'Color', 'none'); 40 | figure_name = sprintf('duke_%s_%s.pdf',scenario{jj},difficulty{ii}); 41 | figure_name = fullfile(opts.experiment_root, opts.experiment_name, folder, figure_name); 42 | export_fig('-transparent', figure_name); 43 | 44 | end 45 | end -------------------------------------------------------------------------------- /src/visualization/render_trajectories_side.m: -------------------------------------------------------------------------------- 1 | opts = get_opts(); 2 | 3 | % Load ground truth trajectories (or your own) 4 | load(fullfile(opts.dataset_path, 'ground_truth', 'trainval.mat')); 5 | trajectories = trainData; 6 | 7 | % Create folder 8 | folder = 'video-results'; 9 | mkdir([opts.experiment_root, filesep, opts.experiment_name, filesep, folder]); 10 | video_name = fullfile(opts.experiment_root, opts.experiment_name, folder, 'duke_sideview.mp4'); 11 | 12 | 13 | % Params 14 | colors = distinguishable_colors(1000); 15 | tail_size = 300; 16 | fps = 120; 17 | rois = opts.ROIs; 18 | ids = unique(trajectories(:,2)); 19 | 20 | % Convert frames to global clock 21 | for iCam = 1:8 22 | inds = find(trajectories(:,1) == iCam); 23 | trajectories(inds,3) = local2global(opts.start_frames(iCam),trajectories(inds,3)); 24 | end 25 | 26 | 27 | %% Render side view 28 | 29 | vid = VideoWriter(video_name, 'MPEG-4'); 30 | open(vid); 31 | 32 | % Placeholders for tags 33 | inds = [271,1; 271, 481; 271, 961; 271,1441; 1,961; 1,1441; 1,481; 1,1]; 34 | 35 | 36 | for frame = startFrame:fps:endFrame 37 | fprintf('Frame %d/%d\n', frame, endFrame); 38 | 39 | data = trajectories(trajectories(:,3) == frame,:); 40 | ids = unique(data(:,2)); 41 | finalimg = zeros(540,1920,3); 42 | 43 | % Will read through each camera separately (slow) 44 | % TODO: Render each camera separately, then combine into mosaic (fast) 45 | for iCam = 1:opts.num_cam 46 | 47 | img = opts.reader.getFrame(iCam, global2local(opts.start_frames(iCam),frame)); 48 | 49 | % Shade ROI with blue 50 | roi = rois{iCam}; 51 | roimask = ones(size(img,1),size(img,2)); 52 | roimask = cv.fillPoly(roimask, roi); 53 | img = double(img); 54 | c3 = img(:,:,3); 55 | c3(logical(~roimask)) = 128 + 0.5*c3(logical(~roimask)); 56 | img(:,:,3) = c3; 57 | img = uint8(img); 58 | 59 | % Draw all tails for current camera frame 60 | for k = 1:length(ids) 61 | id = ids(k); 62 | mask = logical((trajectories(:,1) == iCam) .* (trajectories(:,2) == id) .* (trajectories(:,3) >= frame - tail_size) .* (trajectories(:,3) < frame)); 63 | bb= trajectories(mask, [4 5 6 7]); 64 | feet = feetPosition(bb); 65 | if ~isempty(bb) 66 | img = insertObjectAnnotation(img,'rectangle', bb(end,:), id, 'Color', 255*colors(1+mod(id,1000),:)); 67 | end 68 | 69 | img = cv.polylines(img, feet, 'Closed', false, 'Thickness', 4, 'Color', 255*colors(1+mod(id,1000),:)); 70 | 71 | end 72 | img = imresize(img,0.25); 73 | % Add image to mosaic 74 | finalimg(inds(iCam,1):inds(iCam,1)+270-1,inds(iCam,2):inds(iCam,2)+480-1,:) = img; 75 | end 76 | 77 | % Add camera tags 78 | finalimg = uint8(finalimg); 79 | finalimg = insertText(finalimg,[0 510], sprintf('Frame %d', frame),'FontSize',20); 80 | finalimg = insertText(finalimg,[0 0], 'Camera 8', 'FontSize',20, 'BoxColor', 'black','BoxOpacity',0.8,'TextColor','white'); 81 | finalimg = insertText(finalimg,[480 0], 'Camera 7', 'FontSize',20, 'BoxColor', 'black','BoxOpacity',0.8,'TextColor','white'); 82 | finalimg = insertText(finalimg,[960 0], 'Camera 5', 'FontSize',20, 'BoxColor', 'black','BoxOpacity',0.8,'TextColor','white'); 83 | finalimg = insertText(finalimg,[1440 0], 'Camera 6', 'FontSize',20, 'BoxColor', 'black','BoxOpacity',0.8,'TextColor','white'); 84 | finalimg = insertText(finalimg,[0 270], 'Camera 1', 'FontSize',20, 'BoxColor', 'black','BoxOpacity',0.8,'TextColor','white'); 85 | finalimg = insertText(finalimg,[480 270], 'Camera 2', 'FontSize',20, 'BoxColor', 'black','BoxOpacity',0.8,'TextColor','white'); 86 | finalimg = insertText(finalimg,[960 270], 'Camera 3', 'FontSize',20, 'BoxColor', 'black','BoxOpacity',0.8,'TextColor','white'); 87 | finalimg = insertText(finalimg,[1440 270], 'Camera 4', 'FontSize',20, 'BoxColor', 'black','BoxOpacity',0.8,'TextColor','white'); 88 | 89 | % Write mosaic frame to video 90 | writeVideo(vid,finalimg); 91 | end 92 | close(vid); -------------------------------------------------------------------------------- /src/visualization/render_trajectories_top.m: -------------------------------------------------------------------------------- 1 | opts = get_opts(); 2 | 3 | % Load ground truth trajectories (or your own) 4 | load(fullfile(opts.dataset_path, 'ground_truth', 'trainval.mat')); 5 | trajectories = trainData; 6 | 7 | % Load map 8 | map = imread('src/visualization/data/map.jpg'); 9 | 10 | % Create folder 11 | folder = 'video-results'; 12 | mkdir([opts.experiment_root, filesep, opts.experiment_name, filesep, folder]); 13 | video_name = fullfile(opts.experiment_root, opts.experiment_name, folder, 'duke_topview.mp4'); 14 | 15 | % Params 16 | colors = distinguishable_colors(1000); 17 | tail_size = 300; 18 | fps = 120; 19 | rois = opts.ROIs; 20 | ids = unique(trajectories(:,2)); 21 | interval = opts.sequence_intervals{opts.sequence}; 22 | startFrame = interval(1); 23 | endFrame = interval(end); 24 | 25 | % Convert frames to global clock 26 | for iCam = 1:8 27 | inds = find(trajectories(:,1) == iCam); 28 | trajectories(inds,3) = local2global(opts.start_frames(iCam),trajectories(inds,3)); 29 | end 30 | 31 | % Delineate the regions of interest 32 | roimask = ones(size(map,1),size(map,2)); 33 | for k = 1:8 34 | roi = rois{k}; 35 | mapped = world2map(image2world(roi,k)); 36 | map = cv.polylines(map, mapped, 'Thickness',1.5); 37 | roimask = cv.fillPoly(roimask, mapped); 38 | end 39 | roimask = 1-roimask/13; 40 | map = double(map); 41 | map(:,:,1) = map(:,:,1) .* roimask; 42 | map(:,:,2) = map(:,:,2) .* roimask; 43 | map(:,:,3) = map(:,:,3) .* roimask; 44 | map = uint8(map); 45 | 46 | %% Top View 47 | 48 | vid = VideoWriter(video_name, 'MPEG-4'); 49 | open(vid); 50 | for frame = startFrame:fps:endFrame 51 | fprintf('Frame %d/%d\n', frame, endFrame); 52 | 53 | img = map; 54 | data = trajectories(trajectories(:,3) == frame,:); 55 | ids = unique(data(:,2)); 56 | 57 | polylines = []; 58 | polycolors = []; 59 | 60 | for k = 1:length(ids) 61 | id = ids(k); 62 | mask = logical((trajectories(:,2) == id) .* (trajectories(:,3) >= frame - tail_size) .* (trajectories(:,3) < frame)); 63 | 64 | mapped = world2map(trajectories(mask, [8 9])); 65 | polylines{k} = mapped; 66 | polycolors{k} = colors(1+mod(id,1000),:); 67 | img = cv.polylines(img, polylines{k}, 'Closed', false, 'Thickness', 4, 'Color', 255*colors(1+mod(id,1000),:)); 68 | 69 | end 70 | img = rot90(img,3); 71 | img = insertText(img,[0 0], sprintf('Frame %d', frame),'FontSize',20); 72 | writeVideo(vid,img); 73 | 74 | end 75 | close(vid); 76 | -------------------------------------------------------------------------------- /src/visualization/show_detections.m: -------------------------------------------------------------------------------- 1 | % Demo visualizing OpenPose detections 2 | opts = get_opts(); 3 | 4 | frame = 245000; 5 | cam = 2; 6 | 7 | load(fullfile(opts.dataset_path, 'detections', 'OpenPose', sprintf('camera%d.mat',cam))); 8 | 9 | %% 10 | 11 | img = opts.reader.getFrame(cam, frame); 12 | poses = detections(detections(:,1) == cam & detections(:,2) == frame,3:end); 13 | 14 | img = renderPoses(img, poses); 15 | % Transform poses into boxes 16 | bboxes = []; 17 | for i = 1:size(poses,1) 18 | bboxes(i,:) = pose2bb(poses(i,:), opts.render_threshold); 19 | bboxes(i,:) = scale_bb(bboxes(i,:), poses(i,:), 1.25); 20 | end 21 | img = insertObjectAnnotation(img,'rectangle',bboxes, ones(size(bboxes,1),1)); 22 | figure, imshow(img); 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/visualization/view_distance_distribution.m: -------------------------------------------------------------------------------- 1 | function view_distance_distribution(opts) 2 | % Shows the distance distribution between positive and negative pairs for 3 | % the appearance model specified in opts.net_weights 4 | 5 | data = readtable('src/triplet-reid/data/duke_test.csv', 'Delimiter',','); 6 | 7 | labels = data.Var1; 8 | paths = data.Var2; 9 | %% Compute features 10 | features = h5read(fullfile('src/triplet-reid', opts.net.experiment_root, 'duke_test_embeddings.h5'),'/emb'); 11 | features = features'; 12 | dist = pdist2(features,features); 13 | %% Visualize distance distribution 14 | same_label = triu(pdist2(labels,labels) == 0,1); 15 | different_label = triu(pdist2(labels,labels) ~= 0); 16 | pos_dists = dist(same_label); 17 | neg_dists = dist(different_label); 18 | 19 | figure; 20 | histogram(pos_dists,100,'Normalization','probability', 'FaceColor', 'b'); 21 | hold on; 22 | histogram(neg_dists,100,'Normalization','probability','FaceColor','r'); 23 | title('Normalized distribution of distances among positive and negative pairs'); 24 | legend('Positive','Negative'); 25 | 26 | % figure; 27 | % histogram(pos_dists,100); 28 | % hold on; 29 | % histogram(neg_dists,100); 30 | % title('Unnormalized distribution of distances among positive and negative pairs'); 31 | % legend('Positive','Negative'); 32 | 33 | --------------------------------------------------------------------------------