├── .dockerignore
├── .gitignore
├── resource
├── main.jpg
├── paper_preview.png
├── thirdparty
│ ├── msh2shd
│ └── LICENSE.txt
├── blender
│ ├── studio.blend
│ └── README.md
└── scripts
│ ├── plot_error.py
│ ├── poisson_6.sh
│ ├── plot_dist_weight.py
│ ├── decimate_meshes.sh
│ ├── gen_regions.sh
│ ├── dl_tests.sh
│ ├── meshlab_poisson.mlx
│ ├── meshlab_poisson_6.mlx
│ ├── affine_tform_meshes.sh
│ ├── remesh_graph.sh
│ ├── abs_tform.mlx
│ ├── meshlab_scale.mlx
│ ├── meshlab_isotropic_remesh.mlx
│ ├── meshlab_transform.mlx
│ ├── meshlab_decimate.mlx
│ └── dot_similarity.m
├── meshtracker
├── include
│ ├── prog_opts.h
│ ├── constants.h
│ ├── log.h
│ ├── cpd_utils.h
│ ├── tri_mesh.h
│ ├── keyframer.h
│ ├── nonrigid_icp.h
│ ├── cpd_matrix.h
│ ├── cpd_icp.h
│ ├── gauss_newton_solver.h
│ ├── cpd_normalization.h
│ ├── cpd_gauss_transform.h
│ ├── kalman_smoothing.h
│ ├── cpd_nonrigid.h
│ ├── matching.h
│ ├── mesh_segmentation.h
│ ├── deform_graph.h
│ ├── cloud_processing.h
│ ├── mesh_tracking.h
│ ├── tests.h
│ ├── cpd_transform.h
│ ├── utils.h
│ └── mesh_processing.h
├── CMakeLists.txt
├── solver_options.conf
├── src
│ ├── cpd_gauss_transform_make_default.cpp
│ ├── cpd_transform.cpp
│ ├── cpd_matrix.cpp
│ ├── cpd_normalization.cpp
│ ├── cpd_gauss_transform.cpp
│ ├── cpd_nonrigid.cpp
│ ├── cpd_utils.cpp
│ ├── keyframer.cpp
│ ├── nonrigid_icp.cpp
│ ├── cpd_icp.cpp
│ ├── cloud_processing.cpp
│ └── kalman_smoothing.cpp
└── main.cpp
├── dockers
├── dockerfile_cgal
├── build_dockers.sh
├── dockerfile_meshlab
├── dockerfile_pcl
├── dockerfile_base
└── dockerfile_build
├── CMakeLists.txt
└── README.md
/.dockerignore:
--------------------------------------------------------------------------------
1 | .git/**
2 | resource/tests/**
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/**
2 | mt.code-workspace
3 | .vscode
4 | resource/**
--------------------------------------------------------------------------------
/resource/main.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/V-Sense/AutoMeshTracker/HEAD/resource/main.jpg
--------------------------------------------------------------------------------
/resource/paper_preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/V-Sense/AutoMeshTracker/HEAD/resource/paper_preview.png
--------------------------------------------------------------------------------
/resource/thirdparty/msh2shd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/V-Sense/AutoMeshTracker/HEAD/resource/thirdparty/msh2shd
--------------------------------------------------------------------------------
/resource/blender/studio.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/V-Sense/AutoMeshTracker/HEAD/resource/blender/studio.blend
--------------------------------------------------------------------------------
/meshtracker/include/prog_opts.h:
--------------------------------------------------------------------------------
1 | #ifndef PROG_OPTS_H
2 | #define PROG_OPTS_H
3 |
4 | namespace icp {
5 |
6 | struct IcpOptions {
7 |
8 | bool use_adaptive;
9 | bool parallel;
10 |
11 | };
12 |
13 | }
14 |
15 | #endif
16 |
--------------------------------------------------------------------------------
/resource/blender/README.md:
--------------------------------------------------------------------------------
1 | # Studio Blender file
2 | studio.blend is a simple blender scenario used to render the results in the paper and video.
3 | You can use [stop-motion-obj](https://github.com/neverhood311/Stop-motion-OBJ) to load the output obj sequences and playback with the blender animation timeline
4 |
--------------------------------------------------------------------------------
/resource/scripts/plot_error.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 | import numpy as np
3 | import sys
4 | import os
5 |
6 | fname = sys.argv[1]
7 | e = np.loadtxt(fname)
8 |
9 | plt.plot(e)
10 | plt.ylabel('Feasibility Score')
11 | plt.xlabel('Frame Number')
12 | outname = fname[0:len(fname)-4] + '.png'
13 | plt.savefig(outname)
--------------------------------------------------------------------------------
/resource/scripts/poisson_6.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # set meshlabserver dir and script location
4 | MSERVER="/meshlab/distrib/meshlabserver"
5 | MLABSCRIPT_POISSON="/MeshTracker/resource/scripts/meshlab_poisson_6.mlx"
6 |
7 | # perform poisson remesh
8 | xvfb-run -a -s "-screen 0 800x600x24" $MSERVER -i $1 -o $2 -s $MLABSCRIPT_POISSON
9 |
10 |
--------------------------------------------------------------------------------
/resource/scripts/plot_dist_weight.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 | import numpy as np
3 | import sys
4 |
5 | fname = sys.argv[1]
6 | e = np.loadtxt(fname)
7 |
8 | plt.plot(e)
9 | bottom, top = plt.ylim()
10 | plt.ylim(0,top)
11 | plt.ylabel('val')
12 | plt.xlabel('neighbor')
13 | outname = fname[0:len(fname)-4] + '.png'
14 | plt.savefig(outname)
--------------------------------------------------------------------------------
/meshtracker/include/constants.h:
--------------------------------------------------------------------------------
1 | #ifndef CONSTANTS_H
2 | #define CONSTANTS_H
3 |
4 | // Deform Graph
5 | inline int kDG_KNN = 8; // for graph solve
6 | // inline const int kDeform_KNN = 8; // for graph deform
7 | // TODO: add to deform code
8 |
9 | // Path to solver options
10 | inline const std::string kSolverOptionsPath =
11 | "/MeshTracker/meshtracker/solver_options.conf";
12 |
13 | #endif // CONSTANTS_H
14 |
15 |
--------------------------------------------------------------------------------
/meshtracker/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | include_directories(src)
2 | include_directories(include)
3 | file(GLOB HEADERS include/*.h)
4 | file(GLOB SOURCES src/*.cpp)
5 |
6 | add_executable(${PROJECTNAME} ${SOURCES} main.cpp)
7 |
8 | target_link_libraries(${PROJECTNAME}
9 | ${PCL_LIBRARIES}
10 | ${CGAL_LIBRARIES}
11 | ${OpenCV_LIBS}
12 | ${Boost_LIBRARIES}
13 | ${SuiteSparse_CHOLMOD_LIBRARY_RELEASE})
14 |
--------------------------------------------------------------------------------
/resource/scripts/decimate_meshes.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | MSERVER="/meshlab/distrib/meshlabserver"
4 | MLABSCRIPT="/MeshTracker/resource/scripts/meshlab_decimate.mlx"
5 |
6 | first_frame=00101
7 | last_frame=00886
8 | ext="obj"
9 | length=$(expr $last_frame - $first_frame)
10 | file_pref="Mesh-F"
11 | file_postf="."${ext}
12 |
13 | for frame in $(seq -w $first_frame $last_frame)
14 | do
15 |
16 | fname=${file_pref}${frame}${file_postf}
17 | xvfb-run -a -s "-screen 0 800x600x24" $MSERVER -i $fname -o $fname -s $MLABSCRIPT
18 |
19 | done
20 |
--------------------------------------------------------------------------------
/dockers/dockerfile_cgal:
--------------------------------------------------------------------------------
1 | FROM mesh_tracker:pcl
2 |
3 | ARG NUM_JOBS=3
4 | RUN echo "Number of jobs to execute for make deps: "
5 | RUN echo ${NUM_JOBS}
6 |
7 | # Fixes no GMP warning
8 | RUN apt-get install -y libmpfr-dev libgmp-dev libboost-all-dev
9 |
10 | # CGAL 5.2
11 | RUN git clone https://github.com/CGAL/cgal.git \
12 | && cd cgal \
13 | && git checkout 485e672c26e71b2f10be2e48e5c98ed83e3762f7 \
14 | && mkdir build \
15 | && cd build \
16 | && cmake -DCGAL_HEADER_ONLY=OFF -DCMAKE_BUILD_TYPE=Release .. \
17 | && make -j${NUM_JOBS} \
18 | && make install
--------------------------------------------------------------------------------
/resource/scripts/gen_regions.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | matlab_script_path="/MeshTracker/resource/scripts"
4 |
5 | # Run MATLAB script that takes feature vectors as input and outputs keyframe indices
6 | echo "-------------------------------------------------"
7 | echo "KEYFRAME EXTRACTION VIA SHAPE SIMILARITY"
8 | echo "-------------------------------------------------"
9 |
10 | descriptors=$1
11 | feas=$2
12 | min_region=10
13 | matlab "$@" -nodesktop -nosplash -r "addpath(\"${matlab_script_path}\"); dot_similarity(\"${descriptors}\",\"${feas}\",$min_region); quit"
14 |
15 | exit;
16 |
--------------------------------------------------------------------------------
/resource/scripts/dl_tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | gURL=1gI7_U5DuoNSRKe03PHRrLy5MzLaD7KFU
4 |
5 | # match more than 26 word characters
6 | ggID=$(echo "$gURL" | egrep -o '(\w|-){26,}')
7 |
8 | ggURL='https://drive.google.com/uc?export=download'
9 |
10 | apt-get install curl
11 | curl -sc /tmp/gcokie "${ggURL}&id=${ggID}" >/dev/null
12 | getcode="$(awk '/_warning_/ {print $NF}' /tmp/gcokie)"
13 |
14 | cmd='curl --insecure -C - -LOJb /tmp/gcokie "${ggURL}&confirm=${getcode}&id=${ggID}"'
15 | echo -e "Downloading from "$gURL"...\n"
16 | cd /MeshTracker/resource
17 | eval $cmd
18 | unzip tests.zip && rm tests.zip
19 |
--------------------------------------------------------------------------------
/resource/scripts/meshlab_poisson.mlx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/resource/scripts/meshlab_poisson_6.mlx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/resource/scripts/affine_tform_meshes.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Perform an affine transformation to a sequence of meshes
4 |
5 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )
6 |
7 | MSERVER="/meshlab/distrib/meshlabserver"
8 | MLABSCRIPT="/MeshTracker/resource/scripts/abs_tform.mlx"
9 |
10 | first_frame=00001
11 | last_frame=00075
12 | ext="ply"
13 | length=$(expr $last_frame - $first_frame)
14 | file_pref="Frame_"
15 | file_postf="_hd_t."${ext}
16 |
17 | for frame in $(seq -w $first_frame $last_frame)
18 | do
19 |
20 | fname=${file_pref}${frame}${file_postf}
21 | xvfb-run -a -s "-screen 0 800x600x24" $MSERVER -i $fname -o $fname -s $MLABSCRIPT
22 |
23 | done
24 |
--------------------------------------------------------------------------------
/dockers/build_dockers.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # enter the number of jobs to run in parallel for make commands(default=3)
4 | NUM_JOBS=$1
5 |
6 | ## Build dependencies in stages
7 | docker build --build-arg NUM_JOBS=${NUM_JOBS} -t mesh_tracker:base -f ./dockerfile_base .
8 | docker build --build-arg NUM_JOBS=${NUM_JOBS} -t mesh_tracker:meshlab -f ./dockerfile_meshlab .
9 | docker build --build-arg NUM_JOBS=${NUM_JOBS} -t mesh_tracker:pcl -f ./dockerfile_pcl .
10 | docker build --build-arg NUM_JOBS=${NUM_JOBS} -t mesh_tracker:cgal -f ./dockerfile_cgal .
11 |
12 | ## Main code and immediatey thirdparty deps contained here
13 | cd ..
14 | docker build --build-arg NUM_JOBS=${NUM_JOBS} -t mesh_tracker:latest -f ./dockers/dockerfile_build .
--------------------------------------------------------------------------------
/resource/scripts/remesh_graph.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # set meshlabserver dir and script location
4 | MSERVER="/meshlab/distrib/meshlabserver"
5 | MLABSCRIPT_POISSON="/MeshTracker/resource/scripts/meshlab_poisson.mlx"
6 | MLABSCRIPT_ISOTROPIC="/MeshTracker/resource/scripts/meshlab_isotropic_remesh.mlx"
7 |
8 | # set instant meshes dir
9 | IMESH="/MeshTracker/resource/thirdparty/Instant_Meshes"
10 |
11 | # perform poisson mesh
12 | xvfb-run -a -s "-screen 0 800x600x24" $MSERVER -i $1 -o /tmp/remeshed_graph.ply -s $MLABSCRIPT_POISSON
13 |
14 | # perform uniform remesh
15 | # $MSERVER -i /tmp/remeshed_graph.ply -o $2 -s $MLABSCRIPT_ISOTROPIC
16 | $IMESH -o $2 -i -s $3 -r 6 -p 6 /tmp/remeshed_graph.ply
17 |
18 | # rm remeshed_graph.ply
19 |
--------------------------------------------------------------------------------
/meshtracker/solver_options.conf:
--------------------------------------------------------------------------------
1 | // More options are available but these are most relevant
2 | // for fine tuning this application
3 |
4 | // default: 50
5 | max_num_iterations = 25
6 |
7 | // default: 1e4
8 | initial_trust_region_radius = 1e4
9 |
10 | // default: 1e16
11 | max_trust_region_radius = 1e16
12 |
13 | // default: 1e-32
14 | min_trust_region_radius = 1e-32
15 |
16 | // default: 1e-3
17 | min_relative_decrease = 1e-3
18 |
19 | // default: 1e6
20 | min_lm_diagonal = 1e6
21 |
22 | // default: 1e32
23 | max_lm_diagonal = 1e32
24 |
25 | // default: 5
26 | max_num_consecutive_invalid_steps = 5
27 |
28 | // default: 1e-6
29 | function_tolerance = 1e-5
30 |
31 | // default: 1e-10
32 | gradient_tolerance = 1e-8
33 |
34 | // default: 1e-8
35 | parameter_tolerance = 1e-7
36 |
37 | // options are DENSE_SCHUR or (default) SPARSE_NORMAL_CHOLESKY
38 | linear_solver_type = SPARSE_NORMAL_CHOLESKY
39 |
40 | // default: false
41 | minimizer_progress_to_stdout = true
42 |
--------------------------------------------------------------------------------
/dockers/dockerfile_meshlab:
--------------------------------------------------------------------------------
1 | FROM mesh_tracker:base
2 |
3 | ARG NUM_JOBS
4 |
5 | RUN git clone --recursive --branch Meshlab-2020.03 https://github.com/cnr-isti-vclab/meshlab.git
6 | # WORKDIR /meshlab/
7 | # RUN git checkout f0a3e261ea3abf5ad3067a875b8a90f0dc2d1422
8 |
9 | # Running to ovveride "sudo" in 0_setup_env_ubuntu.sh
10 | RUN apt-get update
11 | ENV DEBIAN_FRONTEND noninteractive
12 | RUN apt-get install -y qt5-default qttools5-dev-tools qtscript5-dev libqt5xmlpatterns5-dev mesa-common-dev libglu1-mesa-dev lib3ds-dev libglew-dev libeigen3-dev libopenctm-dev libgmp-dev libqhull-dev
13 |
14 |
15 | # # set meshlabserver build on by default
16 | # RUN sed -i 's/#add_subdirectory(meshlabserver)/add_subdirectory(meshlabserver)/g' /meshlab/src/CMakeLists.txt
17 | WORKDIR /meshlab/install/linux/
18 | RUN sh linux_build.sh
19 |
20 | RUN export PATH=${PATH}:/meshlab/distrib
21 |
22 | WORKDIR /
23 |
24 | # To actually use meshlabserver you need to use a virtual framebuffer and export the display
25 |
26 | # Xvfb :100
27 | # export DISPLAY=:100.0
28 |
--------------------------------------------------------------------------------
/dockers/dockerfile_pcl:
--------------------------------------------------------------------------------
1 | FROM mesh_tracker:meshlab
2 |
3 | ARG NUM_JOBS=3
4 | RUN echo "Number of jobs to execute for make deps: "
5 | RUN echo ${NUM_JOBS}
6 |
7 | # Install VTK-8.2.0 first
8 | WORKDIR /
9 | RUN wget https://www.vtk.org/files/release/8.2/VTK-8.2.0.zip && unzip VTK-8.2.0.zip && rm VTK-8.2.0.zip
10 |
11 | WORKDIR /VTK-8.2.0
12 | RUN mkdir build && cd build \
13 | && cmake -DCMAKE_BUILD_TYPE=Release .. \
14 | && make -j${NUM_JOBS} \
15 | && make install
16 |
17 | WORKDIR /
18 |
19 | # PCL 1.8.1 # pull a fixed cmake file from future commit to fix lz4 issue
20 | RUN git clone https://github.com/PointCloudLibrary/pcl.git
21 | RUN cd pcl \
22 | && git checkout f38c3cfd496c89b04858ebecf6f7afe4ad4bce50 \
23 | # && git checkout d98313133b014553ab1b1b5b112f9aade837d55c cmake/Modules/FindFLANN.cmake \
24 | && mkdir build \
25 | && cd build \
26 | && cmake -DBUILD_GPU=OFF -DBUILD_visualization=OFF -DCMAKE_BUILD_TYPE=Release .. \
27 | && make -j${NUM_JOBS} \
28 | && make install \
29 | && cd ../../ \
30 | && rm -rf pcl \
31 | && cd
32 |
--------------------------------------------------------------------------------
/resource/scripts/abs_tform.mlx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/meshtracker/src/cpd_gauss_transform_make_default.cpp:
--------------------------------------------------------------------------------
1 | // cpd - Coherent Point Drift
2 | // Copyright (C) 2017 Pete Gadomski
3 | //
4 | // This program is free software; you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation; either version 2 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU General Public License along
15 | // with this program; if not, write to the Free Software Foundation, Inc.,
16 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 |
18 | #include "cpd_gauss_transform.h"
19 |
20 | namespace cpd {
21 | std::unique_ptr GaussTransform::make_default() {
22 | return std::unique_ptr(new GaussTransformDirect());
23 | }
24 | } // namespace cpd
25 |
--------------------------------------------------------------------------------
/meshtracker/src/cpd_transform.cpp:
--------------------------------------------------------------------------------
1 | // cpd - Coherent Point Drift
2 | // Copyright (C) 2017 Pete Gadomski
3 | //
4 | // This program is free software; you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation; either version 2 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU General Public License along
15 | // with this program; if not, write to the Free Software Foundation, Inc.,
16 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 |
18 | #include "cpd_transform.h"
19 |
20 | namespace cpd {
21 |
22 | void Result::denormalize(const Normalization& normalization) {
23 | points = points * normalization.fixed_scale +
24 | normalization.fixed_mean.transpose().replicate(points.rows(), 1);
25 | }
26 | } // namespace cpd
27 |
--------------------------------------------------------------------------------
/meshtracker/include/log.h:
--------------------------------------------------------------------------------
1 | #ifndef LOG_H
2 | #define LOG_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | namespace logging = boost::log;
14 |
15 | static void init_logging
16 | (
17 | const int & verbosity,
18 | const std::string & fname
19 | )
20 | {
21 | logging::register_simple_formatter_factory("Severity");
22 |
23 |
24 | logging::add_file_log(
25 | boost::log::keywords::file_name = fname,
26 | boost::log::keywords::format = "[%TimeStamp%] %Message%",
27 | boost::log::keywords::auto_flush = true // potential performance increase when false
28 | );
29 |
30 | // Severity Levels:
31 | // (0) trace,
32 | // (1) debug,
33 | // (2) info,
34 | // (3) warning,
35 | // (4) error,
36 | // (5) fatal
37 |
38 | logging::core::get()->set_filter
39 | (
40 | logging::trivial::severity >= logging::trivial::severity_level(verbosity)
41 | );
42 |
43 | logging::add_common_attributes();
44 | }
45 |
46 | #endif
--------------------------------------------------------------------------------
/meshtracker/src/cpd_matrix.cpp:
--------------------------------------------------------------------------------
1 | // cpd - Coherent Point Drift
2 | // Copyright (C) 2017 Pete Gadomski
3 | //
4 | // This program is free software; you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation; either version 2 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU General Public License along
15 | // with this program; if not, write to the Free Software Foundation, Inc.,
16 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 |
18 | #include "cpd_matrix.h"
19 |
20 | namespace cpd {
21 |
22 | Matrix apply_transformation_matrix(Matrix points, const Matrix& transform) {
23 | Matrix::Index rows = points.rows();
24 | Matrix::Index cols = points.cols() + 1;
25 | points.conservativeResize(rows, cols);
26 | points.col(cols - 1) = Vector::Ones(rows);
27 | Matrix transformed_points = points * transform.transpose();
28 | return transformed_points.leftCols(cols - 1);
29 | }
30 | } // namespace cpd
31 |
--------------------------------------------------------------------------------
/meshtracker/include/cpd_utils.h:
--------------------------------------------------------------------------------
1 | // cpd - Coherent Point Drift
2 | // Copyright (C) 2017 Pete Gadomski
3 | //
4 | // This program is free software; you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation; either version 2 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU General Public License along
15 | // with this program; if not, write to the Free Software Foundation, Inc.,
16 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 |
18 | /// \file
19 | ///
20 | /// The always-present, always-ambiguous utils file.
21 |
22 | #pragma once
23 |
24 | #include "cpd_matrix.h"
25 |
26 | namespace cpd {
27 |
28 | /// Loads a matrix from a delimited text file.
29 | Matrix matrix_from_path(const std::string& path);
30 |
31 | /// Computes the default sigma2 for the given matrices.
32 | double default_sigma2(const Matrix& fixed, const Matrix& moving);
33 |
34 | /// Computes the affinity matrix between the two matrices.
35 | Matrix affinity(const Matrix& fixed, const Matrix& moving, double beta);
36 | } // namespace cpd
37 |
--------------------------------------------------------------------------------
/dockers/dockerfile_base:
--------------------------------------------------------------------------------
1 | FROM ubuntu:20.04
2 |
3 | ARG NUM_JOBS
4 | RUN echo "Number of jobs to execute for make deps: "
5 | RUN echo ${NUM_JOBS}
6 |
7 | RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
8 | build-essential \
9 | git \
10 | wget \
11 | unzip \
12 | cmake \
13 | cmake-curses-gui \
14 | libgtk2.0-dev \
15 | pkg-config \
16 | libavcodec-dev \
17 | libavformat-dev \
18 | python3-dev \
19 | python3-numpy \
20 | python3-pip \
21 | python-setuptools \
22 | libtbb-dev \
23 | libjpeg-dev \
24 | libpng-dev \
25 | libtiff-dev \
26 | mpi-default-dev \
27 | openmpi-bin \
28 | openmpi-common \
29 | libflann-dev \
30 | libeigen3-dev \
31 | libboost-all-dev \
32 | libvtk6-dev \
33 | libqhull* \
34 | freeglut3-dev \
35 | libglew-dev \
36 | pkg-config \
37 | libproj-dev \
38 | libatlas-base-dev \
39 | libsuitesparse-dev \
40 | libassimp-dev \
41 | libglfw3-dev \
42 | libglfw3 \
43 | cimg-dev \
44 | graphviz \
45 | lcov \
46 | ca-certificates \
47 | software-properties-common \
48 | g++ \
49 | qt5-qmake \
50 | qtscript5-dev \
51 | libqt5xmlpatterns5-dev \
52 | libqt5opengl5-dev \
53 | assimp-utils \
54 | nano \
55 | lz4 \
56 | xvfb \
57 | && rm -rf /var/lib/apt/lists/*
58 |
59 | RUN cd / && wget https://github.com/Kitware/CMake/releases/download/v3.18.3/cmake-3.18.3-Linux-x86_64.tar.gz && tar -xvf *.tar.gz && rm cmake-3.18.3-Linux-x86_64.tar.gz && mv cmake-3.18.3-Linux-x86_64 cmake-3.18.3
60 |
--------------------------------------------------------------------------------
/meshtracker/include/tri_mesh.h:
--------------------------------------------------------------------------------
1 | #ifndef _TRI_MESH_H_
2 | #define _TRI_MESH_H_
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | typedef pcl::PointCloud::Ptr CloudPtr;
17 | typedef pcl::geometry::PolygonMesh> Mesh;
18 |
19 | class TriMesh
20 | {
21 | public:
22 | struct PolyIndex
23 | {
24 | unsigned int vert_index[3];
25 | unsigned int norm_index[3];
26 | };
27 | std::vector vertex_coord;//
28 | std::vector norm_coord;
29 | std::vector polyIndex;
30 | std::vector face_norm;
31 | Eigen::Vector3d BoundingBox_Min, BoundingBox_Max; //min max
32 | int vert_num;
33 | int poly_num;
34 |
35 | TriMesh() : vert_num(0), poly_num(0) {}
36 | TriMesh(const char* filename);
37 | TriMesh(pcl::PolygonMesh& pcl_mesh);
38 | CloudPtr ToPclPointCloud();
39 | void updateNorm();
40 | void prepareFaceNeighbours(std::vector>& neighbours);
41 | void saveOBJ(const char* filename);
42 | void savePLY(const char* filename);
43 | void getBoundingBox(Eigen::Vector3d& Min, Eigen::Vector3d& Max);
44 | private:
45 | void readPLY(const char* filename);
46 | void readOBJ(const char* filename);
47 | };
48 |
49 | #endif/*_TRI_MESH_H_*/
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/meshtracker/include/keyframer.h:
--------------------------------------------------------------------------------
1 | #ifndef _KEYFRAMER_H
2 | #define _KEYFRAMER_H
3 |
4 | #include
5 | #include
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | #include "mesh_processing.h"
18 |
19 | typedef pcl::geometry::PolygonMesh> Mesh;
20 |
21 |
22 | class KeyFramer{
23 |
24 |
25 | public:
26 | KeyFramer(){}
27 | ~KeyFramer(){}
28 |
29 | // Takes list of meshes as input, calculates keyframe locations and returns
30 | // kf indices for input list.
31 | std::vector GetKeyframeIndices(const std::string& _filename);
32 |
33 | // Read mesh list
34 | std::vector ReadMeshList(const std::string& _filename);
35 |
36 | // Computes the area of a surface mesh
37 | double ComputeMeshArea(const pcl::PolygonMesh& mesh);
38 |
39 | unsigned int ComputeMeshGenus(const Mesh& mesh);
40 |
41 | unsigned int ComputeMeshGenus(const pcl::PolygonMesh& mesh);
42 |
43 | void GenerateSPHDescriptors(
44 | const std::string & meshlist,
45 | const std::string & exe);
46 |
47 | // This function only exists to implement feasibility score of Collet et. al
48 | void GenerateFeasScore(
49 | std::vector meshlist,
50 | std::string outpath);
51 |
52 | void GenKeyframesAndRegions(
53 | const std::vector & meshlist);
54 |
55 | };
56 | #endif
57 |
--------------------------------------------------------------------------------
/meshtracker/src/cpd_normalization.cpp:
--------------------------------------------------------------------------------
1 | // cpd - Coherent Point Drift
2 | // Copyright (C) 2017 Pete Gadomski
3 | //
4 | // This program is free software; you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation; either version 2 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU General Public License along
15 | // with this program; if not, write to the Free Software Foundation, Inc.,
16 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 |
18 | #include "cpd_normalization.h"
19 |
20 | namespace cpd {
21 |
22 | Normalization::Normalization(const Matrix& f, const Matrix& m, bool linked)
23 | : fixed_mean(f.colwise().mean())
24 | , fixed(f - fixed_mean.transpose().replicate(f.rows(), 1))
25 | , fixed_scale(std::sqrt(fixed.array().pow(2).sum() / fixed.rows()))
26 | , moving_mean(m.colwise().mean())
27 | , moving(m - moving_mean.transpose().replicate(m.rows(), 1))
28 | , moving_scale(std::sqrt(moving.array().pow(2).sum() / moving.rows())) {
29 | if (linked) {
30 | double scale = std::max(fixed_scale, moving_scale);
31 | fixed_scale = scale;
32 | moving_scale = scale;
33 | }
34 | fixed /= fixed_scale;
35 | moving /= moving_scale;
36 | }
37 | } // namespace cpd
38 |
--------------------------------------------------------------------------------
/meshtracker/include/nonrigid_icp.h:
--------------------------------------------------------------------------------
1 | /*
2 | Modified by Matt Moynihan, https://github.com/mjkmoynihan
3 | Original Author: Yizhi Tang
4 | From: https://github.com/Tonsty/Non-Rigid-Registar
5 | Accessed September 7th 2018
6 | */
7 |
8 | #ifndef NONRIGID_ICP_H
9 | #define NONRIGID_ICP_H
10 |
11 | #include
12 |
13 | typedef vtkSmartPointer VTKMesh;
14 |
15 | typedef VTKMesh Template;
16 | typedef VTKMesh Target;
17 | typedef std::pair Edge;
18 | typedef boost::shared_ptr< std::vector > Edges;
19 | typedef vtkSmartPointer Vertices;
20 | typedef boost::shared_ptr< std::vector > Weights;
21 |
22 | class NonRigidICP
23 | {
24 | public:
25 | NonRigidICP(
26 | Template _template,
27 | Target _target):
28 | template_(_template),
29 | target_(_target){}
30 |
31 | ~NonRigidICP(){}
32 |
33 | void init();
34 |
35 | void initCompute();
36 |
37 | void initCompute(const std::vector>&);
38 |
39 | void edgesInit();
40 |
41 | void nearestSearchInit();
42 |
43 | void verticesInit();
44 |
45 | void correspondencesInit();
46 |
47 | void setCorrespondences(
48 | const std::vector>& mt_matches);
49 |
50 | void weightsInit();
51 |
52 | int compute(float alpha, float gamma);
53 |
54 | inline Template GetTemplate(){
55 | return this->template_;
56 | }
57 |
58 | protected:
59 | Template template_;
60 | Target target_;
61 |
62 | Edges edges_;
63 | Vertices vertices_;
64 |
65 | // corr[src_id] = target_id ?
66 | Vertices correspondences_;
67 | Weights weights_;
68 |
69 | private:
70 | vtkSmartPointer cellLocator_;
71 | };
72 |
73 | #endif
74 |
--------------------------------------------------------------------------------
/resource/scripts/meshlab_scale.mlx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/meshtracker/include/cpd_matrix.h:
--------------------------------------------------------------------------------
1 | // cpd - Coherent Point Drift
2 | // Copyright (C) 2017 Pete Gadomski
3 | //
4 | // This program is free software; you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation; either version 2 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU General Public License along
15 | // with this program; if not, write to the Free Software Foundation, Inc.,
16 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 |
18 | /// \file
19 | ///
20 | /// Basic typedefs for our flavors of Eigen matrices.
21 |
22 | #pragma once
23 |
24 | #include
25 |
26 |
27 | namespace cpd {
28 |
29 | /// Our base matrix class.
30 | typedef Eigen::MatrixXd Matrix;
31 |
32 | /// Typedef for our specific type of vector.
33 | typedef Eigen::VectorXd Vector;
34 |
35 | /// Typedef for an index vector, used to index other matrices.
36 | typedef Eigen::Matrix IndexVector;
37 |
38 | /// Typedef for our specific type of array.
39 | typedef Eigen::ArrayXd Array;
40 |
41 | /// Apply a transformation matrix to a set of points.
42 | ///
43 | /// The transformation matrix should be one column wider than the point matrix.
44 | Matrix apply_transformation_matrix(Matrix points, const Matrix& transform);
45 | } // namespace cpd
46 |
--------------------------------------------------------------------------------
/dockers/dockerfile_build:
--------------------------------------------------------------------------------
1 | FROM mesh_tracker:cgal
2 |
3 | ARG NUM_JOBS=8
4 | RUN echo "Number of jobs to execute for make deps: "
5 | RUN echo ${NUM_JOBS}
6 |
7 | # add GCC 9 and make it default compiller
8 | RUN add-apt-repository ppa:ubuntu-toolchain-r/test \
9 | && apt-get update && apt-get install -y --no-install-recommends \
10 | gcc-9 g++-9 \
11 | && export CC=/usr/bin/gcc-9 \
12 | && export CXX=/usr/bin/g++-9 \
13 | && apt-get autoremove -y \
14 | && apt-get clean
15 |
16 | # fix suitesparse linking error
17 | RUN sed -i 's///g' /usr/include/eigen3/Eigen/CholmodSupport
18 |
19 | RUN cd / && wget https://github.com/Kitware/CMake/releases/download/v3.18.3/cmake-3.18.3-Linux-x86_64.tar.gz && tar -xvf *.tar.gz && rm cmake-3.18.3-Linux-x86_64.tar.gz && mv cmake-3.18.3-Linux-x86_64 cmake-3.18.3
20 |
21 | WORKDIR /MeshTracker
22 | COPY /CMakeLists.txt /MeshTracker/CMakeLists.txt
23 | COPY /meshtracker /MeshTracker/meshtracker
24 |
25 | RUN mkdir -p build && cd build && /cmake-3.18.3/bin/cmake -DCMAKE_BUILD_TYPE=Release .. && make -j${NUM_JOBS} && make install
26 | # RUN sed -i.bak "/\b\(__CUDACC_VER__ is no longer supported\)\b/d" /usr/local/cuda/include/crt/common_functions.h
27 |
28 | # Sometimes need to update shared lib cache
29 | RUN ldconfig
30 |
31 | COPY /resource/ /MeshTracker/resource/
32 |
33 | # Third-Party Resources
34 | RUN cd /MeshTracker/resource/thirdparty && wget https://instant-meshes.s3.eu-central-1.amazonaws.com/instant-meshes-linux.zip && unzip *.zip && mv 'Instant Meshes' Instant_Meshes && rm *.zip
35 |
36 | RUN mkdir -p /input
37 | RUN mkdir -p /result/smooth
38 |
39 | # Tests
40 | RUN sh /MeshTracker/resource/scripts/dl_tests.sh
41 | RUN chmod -R u=rw /MeshTracker/resource/tests/
42 | RUN chmod -R u=rwx /MeshTracker/resource/scripts/
43 |
44 |
45 |
--------------------------------------------------------------------------------
/meshtracker/include/cpd_icp.h:
--------------------------------------------------------------------------------
1 | #ifndef CPD_ICP_H
2 | #define CPD_ICP_H
3 |
4 | using CloudPtr = pcl::PointCloud::Ptr;
5 | using MatCloudPtr = pcl::PointCloud::Ptr;
6 |
7 | struct CPD_Result{
8 |
9 | CloudPtr aligned_cloud;
10 | pcl::PolygonMesh aligned_mesh;
11 | CloudPtr subsampled_moving;
12 | CloudPtr subsampled_fixed;
13 | CloudPtr lo_res_aligned;
14 |
15 | };
16 |
17 | class CPDICP {
18 |
19 | public:
20 | CPDICP(){};
21 | ~CPDICP(){};
22 |
23 | // conversion functions
24 | cpd::Matrix PCLToCpdMat(const CloudPtr & ptcloud);
25 | CloudPtr CpdMatToPCL(const cpd::Matrix & mat);
26 |
27 | cpd::NonrigidResult NonRigidRegistrationImpl(
28 | const cpd::Matrix & fixed,
29 | const cpd::Matrix & moving,
30 | const double & tolerance);
31 |
32 | // encapsulated function call to cpd::getAffinity
33 | void getAffinity(
34 | const cpd::Matrix & fixed,
35 | const cpd::Matrix & moving,
36 | cpd::Matrix & _aff)
37 | {
38 | cpd::Nonrigid nr;
39 | nr.getAffinity(fixed,moving,_aff);
40 | }
41 |
42 | // Use cpd to align high res meshses by downsampling and applying low-res
43 | // alignment to a high res mesh.
44 | pcl::PolygonMesh AlignHighResMeshes(
45 | const pcl::PolygonMesh & _mesh_fixed,
46 | const pcl::PolygonMesh & _mesh_moving,
47 | const double & tolerance);
48 |
49 | // Overrride for CloudPtr input
50 | CPD_Result AlignHighResClouds(
51 | const CloudPtr & _cloud_fixed,
52 | const CloudPtr & _cloud_moving,
53 | const double & tolerance);
54 |
55 | inline void SetTrackedMeshNum(const int & mesh_num){
56 | tracked_mesh_num=mesh_num;
57 | }
58 |
59 | inline int GetTrackedMeshNum() const{
60 | return tracked_mesh_num;
61 | }
62 |
63 |
64 |
65 | private:
66 | int tracked_mesh_num = 0;
67 | const int kSamplesUpperLimit = 800;
68 | const int kSamplesLowerLimit = 50;
69 |
70 | };
71 |
72 | #endif
73 |
--------------------------------------------------------------------------------
/resource/scripts/meshlab_isotropic_remesh.mlx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/meshtracker/include/gauss_newton_solver.h:
--------------------------------------------------------------------------------
1 | #ifndef GAUSS_NEWTON_SOLVER_H
2 | #define GAUSS_NEWTON_SOLVER_H
3 |
4 | #include "utils.h"
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | #include
11 |
12 | #include
13 | #include
14 | #include
15 |
16 | #include
17 | #include
18 |
19 | // Node idx, corrspondence pairs
20 | typedef std::vector> NodeCorrs;
22 |
23 | struct GNParams
24 | {
25 | GNParams(
26 | double alpha_rigid = 500.0, // 1000.0
27 | double alpha_smooth = 500.0, //1000.0
28 | double alpha_point = 0.1, //0.1
29 | double alpha_plane = 1.0) : //1.0
30 | m_alpha_rigid(alpha_rigid),
31 | m_alpha_smooth(alpha_smooth),
32 | m_alpha_point(alpha_point),
33 | m_alpha_plane(alpha_plane)
34 | {}
35 |
36 | // weights
37 | double m_alpha_rigid = 0.0;
38 | double m_alpha_smooth = 0.0;
39 | double m_alpha_point = 0.0;
40 | double m_alpha_plane = 0.0;
41 |
42 | void RelaxParams(void)
43 | {
44 | this->m_alpha_rigid /= 2.0;
45 | this->m_alpha_smooth /= 2.0;
46 | }
47 | };
48 |
49 | class GaussNewtonSolver
50 | {
51 | private:
52 | GNParams params_;
53 |
54 | public:
55 | GaussNewtonSolver(){};
56 | ~GaussNewtonSolver(){};
57 |
58 | inline void SetParams(const GNParams p)
59 | {
60 | this->params_=p;
61 | }
62 |
63 | // Solve use Gauss-Newton Iterative Minimization.
64 | // returns residual energy
65 | double solve(
66 | const std::vector> & node_node_neigh,
67 | const std::vector> & node_node_weights,
68 | const NodeCorrs & constraints,
69 | const std::vector> & con_node_neigh,
70 | const std::vector> & con_neigh_weights,
71 | std::vector & node_pos,
72 | Eigen::VectorXd & affine_vector);
73 | };
74 |
75 | #endif
76 |
--------------------------------------------------------------------------------
/meshtracker/include/cpd_normalization.h:
--------------------------------------------------------------------------------
1 | // cpd - Coherent Point Drift
2 | // Copyright (C) 2017 Pete Gadomski
3 | //
4 | // This program is free software; you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation; either version 2 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU General Public License along
15 | // with this program; if not, write to the Free Software Foundation, Inc.,
16 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 |
18 | /// \file
19 | ///
20 | /// Utility to scale/offset points to (roughly) conform to unit shape centered
21 | /// at zero.
22 |
23 | #pragma once
24 |
25 | #include "cpd_matrix.h"
26 |
27 | namespace cpd {
28 |
29 | /// The results of normalizing data to a unit cube (or whatever dimensionality).
30 | struct Normalization {
31 | /// The average of the fixed points, that was subtracted from those data.
32 | Vector fixed_mean;
33 | /// The fixed points.
34 | Matrix fixed;
35 | /// The scaling factor for the fixed points.
36 | double fixed_scale;
37 | /// The average of the moving points, that was subtracted from those data.
38 | Vector moving_mean;
39 | /// The moving points.
40 | Matrix moving;
41 | /// The scaling factor for the moving points.
42 | double moving_scale;
43 |
44 | /// Creates a new normalization for the provided matrices.
45 | ///
46 | /// If `linked = true`, apply the same scaling to both sets of points. This
47 | /// is recommended if you are working with data that should not be scaled,
48 | /// e.g. LiDAR data. If `linked = false`, each point set is scaled
49 | /// seperately.
50 | ///
51 | /// Myronenko's original implementation only had `linked = false` logic.
52 | Normalization(const Matrix& fixed, const Matrix& moving,
53 | bool linked = true);
54 | };
55 | } // namespace cpd
56 |
--------------------------------------------------------------------------------
/meshtracker/include/cpd_gauss_transform.h:
--------------------------------------------------------------------------------
1 | // cpd - Coherent Point Drift
2 | // Copyright (C) 2017 Pete Gadomski
3 | //
4 | // This program is free software; you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation; either version 2 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU General Public License along
15 | // with this program; if not, write to the Free Software Foundation, Inc.,
16 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 |
18 | /// \file
19 | ///
20 | /// Basic correspondence/error calculation between two datasets, using the
21 | /// direct method of the Gauss transform.
22 |
23 | #pragma once
24 |
25 | #include "cpd_matrix.h"
26 | #include
27 |
28 | namespace cpd {
29 |
30 | /// Probability matrices produced by comparing two data sets with a
31 | /// `GaussTransform`.
32 | struct Probabilities {
33 | /// The probability matrix, multiplied by the identity matrix.
34 | Vector p1;
35 | /// The probability matrix, transposes, multiplied by the identity matrix.
36 | Vector pt1;
37 | /// The probability matrix multiplied by the fixed points.
38 | Matrix px;
39 | /// The total error.
40 | double l;
41 | /// The correspondence vector between the two datasets.
42 | IndexVector correspondence;
43 | };
44 |
45 | /// Abstract base class for Gauss transforms.
46 | class GaussTransform {
47 | public:
48 | /// Returns the default Gauss transform as a unique ptr.
49 | static std::unique_ptr make_default();
50 |
51 | /// Computes the Gauss transform.
52 | virtual Probabilities compute(const Matrix& fixed, const Matrix& moving,
53 | double sigma2, double outliers) const = 0;
54 | };
55 |
56 | /// The direct Gauss transform.
57 | class GaussTransformDirect : public GaussTransform {
58 | public:
59 | Probabilities compute(const Matrix& fixed, const Matrix& moving,
60 | double sigma2, double outliers) const;
61 | };
62 | } // namespace cpd
63 |
--------------------------------------------------------------------------------
/resource/thirdparty/LICENSE.txt:
--------------------------------------------------------------------------------
1 | ----- Spherical Harmonics Copyright ----
2 | Spherical Harmonic Transform Kit 2.5
3 |
4 | Sean Moore, Dennis Healy, Dan Rockmore, Peter Kostelec
5 | smoore@bbn.com, {healy,rockmore,geelong}@cs.dartmouth.edu
6 |
7 | Contact: Peter Kostelec
8 | geelong@cs.dartmouth.edu
9 |
10 |
11 | Copyright 1997, 1998 Sean Moore, Dennis Healy,
12 | Dan Rockmore, Peter Kostelec
13 |
14 |
15 | This program is free software; you can redistribute it and/or modify
16 | it under the terms of the GNU General Public License as published by
17 | the Free Software Foundation; either version 2 of the License, or
18 | (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | You should have received a copy of the GNU General Public License
26 | along with this program; if not, write to the Free Software
27 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 |
29 |
30 | Commercial use is absolutely prohibited.
31 |
32 |
33 | ---- GAPS copyright -----
34 | Copyright (c) 2007 Thomas Funkhouser
35 |
36 | Permission is hereby granted, free of charge, to any person obtaining a copy
37 | of this software and associated documentation files (the "Software"), to deal
38 | in the Software without restriction, including without limitation the rights
39 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
40 | copies of the Software, and to permit persons to whom the Software is
41 | furnished to do so, subject to the following conditions:
42 |
43 | The above copyright notice and this permission notice shall be included in
44 | all copies or substantial portions of the Software.
45 |
46 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
47 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
48 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
49 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
50 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
51 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
52 | THE SOFTWARE.
53 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.4.1)
2 |
3 |
4 | SET(PROJECTNAME "MeshTracker")
5 | PROJECT(${PROJECTNAME} C CXX)
6 |
7 | set (CMAKE_BUILD_TYPE "Release")
8 |
9 | set (CMAKE_CXX_STANDARD 17)
10 |
11 | include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
12 |
13 | # Add CGAL
14 | FIND_PACKAGE(CGAL REQUIRED)
15 | IF (CGAL_FOUND)
16 | message(CGAL_FOUND)
17 | include_directories(${CGAL_INCLUDE_DIRS})
18 | link_directories(${CGAL_LIBRARY_DIRS})
19 | include(${CGAL_USE_FILE})
20 | ENDIF (CGAL_FOUND)
21 |
22 | # Add Eigen3
23 | FIND_PACKAGE(Eigen3 REQUIRED)
24 | IF (EIGEN3_FOUND)
25 | message(EIGEN3_FOUND)
26 | INCLUDE_DIRECTORIES(${EIGEN3_INCLUDE_DIR})
27 | ENDIF (EIGEN3_FOUND)
28 |
29 | # Add SuiteSparse
30 | find_package(SuiteSparse REQUIRED COMPONENTS CHOLMOD)
31 | if (SUITESPARSE_FOUND)
32 | message(SUITESPARSE_FOUND)
33 | add_library (SuiteSparse::Cholmod INTERFACE IMPORTED)
34 | target_link_libraries (SuiteSparse::Cholmod INTERFACE ${SuiteSparse_CHOLMOD_LIBRARY_RELEASE})
35 | target_include_directories (SuiteSparse::Cholmod INTERFACE ${SuiteSparse_INCLUDE_DIRS})
36 | ENDIF(SUITESPARSE_FOUND)
37 |
38 | # Add OpenCV
39 | find_package(OpenCV REQUIRED)
40 | if (OPENCV_FOUND)
41 | message(OPENCV_FOUND)
42 | INCLUDE_DIRECTORIES(${OPENCV_INCLUDE_DIRS})
43 | ENDIF (OPENCV_FOUND)
44 |
45 | # Add Point Cloud Library
46 | find_package(PCL 1.8 REQUIRED)
47 | if (PCL_FOUND)
48 | message(PCL_FOUND)
49 | list(REMOVE_ITEM PCL_LIBRARIES "vtkproj4")
50 | add_definitions(-DPCL_NEW=1)
51 | include_directories(${PCL_INCLUDE_DIRS})
52 | link_directories(${PCL_LIBRARY_DIRS})
53 | add_definitions(${PCL_DEFINITIONS})
54 | endif (PCL_FOUND)
55 |
56 | # Add Boost
57 | FIND_PACKAGE(Boost COMPONENTS program_options log REQUIRED)
58 | IF (Boost_FOUND)
59 | message(BOOST_FOUND)
60 | ADD_DEFINITIONS(-DBOOST_LOG_DYN_LINK)
61 | INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
62 | ENDIF (Boost_FOUND)
63 |
64 | #Add VTK
65 | FIND_PACKAGE(VTK REQUIRED)
66 | IF (VTK_FOUND)
67 | message(VTK_FOUND)
68 | INCLUDE (${VTK_USE_FILE})
69 | ENDIF (VTK_FOUND)
70 |
71 | find_package(OpenMP)
72 | if (OPENMP_FOUND)
73 | message(OPENMP_FOUND)
74 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
75 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
76 | set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
77 | endif()
78 |
79 | add_subdirectory(meshtracker)
80 |
81 | install(TARGETS ${PROJECTNAME} DESTINATION bin)
--------------------------------------------------------------------------------
/meshtracker/include/kalman_smoothing.h:
--------------------------------------------------------------------------------
1 | #ifndef KALMAN_SMOOTHING_H
2 | #define KALMAN_SMOOTHING_H
3 |
4 | class KalmanSmoother
5 | {
6 |
7 | public:
8 | KalmanSmoother(){};
9 | ~KalmanSmoother(){};
10 |
11 | inline void setMeshNames(const std::vector & _meshNames)
12 | {
13 | meshNames_ = _meshNames;
14 | }
15 |
16 | inline const std::vector & getMeshNames() const
17 | {
18 | return meshNames_;
19 | }
20 |
21 | inline void setMeshes(const std::vector & _meshes)
22 | {
23 | meshes_ = _meshes;
24 | }
25 |
26 | inline const std::vector & getMeshes() const
27 | {
28 | return meshes_;
29 | }
30 |
31 | inline void setClouds(const std::vector::Ptr> & _clouds)
32 | {
33 | clouds_ = _clouds;
34 | }
35 |
36 | inline const std::vector::Ptr> & getClouds() const
37 | {
38 | return clouds_;
39 | }
40 |
41 | inline void setKeyFrameIndices(const std::vector & _keyFrameIndices)
42 | {
43 | keyFrameIndices_ = _keyFrameIndices;
44 | }
45 |
46 | inline const std::vector &getKeyFrameIndices() const
47 | {
48 | return keyFrameIndices_;
49 | }
50 |
51 | // Read the mesh sequence and keyframes from a text file
52 | int readMeshes(const std::string & _fileName);
53 |
54 | // Finds the keyframe indices in the mesh sequence
55 | int ReadRegions(const std::string & _fileName);
56 |
57 | // Smooths the mesh sequence keyframe to keyframe
58 | // It is simpler but not the rigth way
59 | void smoothLinearly();
60 |
61 | //
62 | void smoothInRegions();
63 |
64 | // 3D Kalman filter to smooth the sequence
65 | void filterKalman(
66 | const std::vector & _input,
67 | std::vector & _output) const;
68 |
69 | // Takes the smooth clouds and builds meshes
70 | std::vector setupSmoothMeshes(bool _export = true) const;
71 |
72 | private:
73 | //
74 | void setupClouds();
75 |
76 | std::vector meshNames_;
77 | std::vector meshes_;
78 | std::vector::Ptr> clouds_;
79 | std::vector keyFrameIndices_;
80 | std::vector> regions_;
81 | std::vector::Ptr> smoothClouds_;
82 | };
83 |
84 | #endif
85 |
--------------------------------------------------------------------------------
/meshtracker/src/cpd_gauss_transform.cpp:
--------------------------------------------------------------------------------
1 | // cpd - Coherent Point Drift
2 | // Copyright (C) 2017 Pete Gadomski
3 | //
4 | // This program is free software; you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation; either version 2 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU General Public License along
15 | // with this program; if not, write to the Free Software Foundation, Inc.,
16 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 |
18 | #define _USE_MATH_DEFINES
19 |
20 | #include "cpd_gauss_transform.h"
21 | #include
22 | #include
23 |
24 | namespace cpd {
25 | Probabilities GaussTransformDirect::compute(const Matrix& fixed,
26 | const Matrix& moving, double sigma2,
27 | double outliers) const {
28 | double ksig = -2.0 * sigma2;
29 | size_t cols = fixed.cols();
30 | double outlier_tmp =
31 | (outliers * moving.rows() * std::pow(-ksig * M_PI, 0.5 * cols)) /
32 | ((1 - outliers) * fixed.rows());
33 | Vector p = Vector::Zero(moving.rows());
34 | Vector p1 = Vector::Zero(moving.rows());
35 | Vector p1_max = Vector::Zero(moving.rows());
36 | Vector pt1 = Vector::Zero(fixed.rows());
37 | Matrix px = Matrix::Zero(moving.rows(), cols);
38 | IndexVector correspondence = IndexVector::Zero(moving.rows());
39 | double l = 0.0;
40 |
41 | for (Matrix::Index i = 0; i < fixed.rows(); ++i) {
42 | double sp = 0;
43 | for (Matrix::Index j = 0; j < moving.rows(); ++j) {
44 | double razn = (fixed.row(i) - moving.row(j)).array().pow(2).sum();
45 | p(j) = std::exp(razn / ksig);
46 | sp += p(j);
47 | }
48 | sp += outlier_tmp;
49 | pt1(i) = 1 - outlier_tmp / sp;
50 | for (Matrix::Index j = 0; j < moving.rows(); ++j) {
51 | p1(j) += p(j) / sp;
52 | px.row(j) += fixed.row(i) * p(j) / sp;
53 | if (p(j) / sp > p1_max(j)) {
54 | correspondence(j) = i;
55 | p1_max(j) = p(j) / sp;
56 | }
57 | }
58 | l += -std::log(sp);
59 | }
60 | l += cols * fixed.rows() * std::log(sigma2) / 2;
61 | return { p1, pt1, px, l, correspondence };
62 | }
63 | } // namespace cpd
64 |
--------------------------------------------------------------------------------
/meshtracker/src/cpd_nonrigid.cpp:
--------------------------------------------------------------------------------
1 | // cpd - Coherent Point Drift
2 | // Copyright (C) 2017 Pete Gadomski
3 | //
4 | // This program is free software; you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation; either version 2 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU General Public License along
15 | // with this program; if not, write to the Free Software Foundation, Inc.,
16 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 |
18 | #include "cpd_nonrigid.h"
19 |
20 | namespace cpd {
21 |
22 | void Nonrigid::init(const Matrix& fixed, const Matrix& moving) {
23 |
24 | m_g = affinity(moving, moving, m_beta);
25 | m_w = Matrix::Zero(moving.rows(), moving.cols());
26 | m_w = Matrix::Ones(moving.rows(), moving.cols());
27 | // kIteration=0;
28 | }
29 |
30 | void Nonrigid::getAffinity(const Matrix& fixed, const Matrix& moving, Matrix& _aff){
31 | _aff = affinity(fixed, moving, m_beta);
32 | }
33 |
34 | void Nonrigid::modify_probabilities(Probabilities& probabilities) const {
35 | probabilities.l += m_lambda / 2.0 * (m_w.transpose() * m_g * m_w).trace();
36 | }
37 |
38 | NonrigidResult Nonrigid::compute_one(const Matrix& fixed, const Matrix& moving,
39 | const Probabilities& probabilities,
40 | double sigma2) const {
41 | size_t cols = fixed.cols();
42 | auto dp = probabilities.p1.asDiagonal();
43 |
44 |
45 | Matrix to_solve =dp * m_g + m_lambda * sigma2 *
46 | Matrix::Identity(moving.rows(), moving.rows());
47 | Matrix w = (to_solve).colPivHouseholderQr().solve(probabilities.px - dp * moving);
48 | NonrigidResult result;
49 | result.points = moving + m_g * w;
50 | result.tform = w;
51 | double np = probabilities.p1.sum();
52 | result.sigma2 = std::abs(
53 | ((fixed.array().pow(2) * probabilities.pt1.replicate(1, cols).array())
54 | .sum() +
55 | (result.points.array().pow(2) *
56 | probabilities.p1.replicate(1, cols).array())
57 | .sum() -
58 | 2 * (probabilities.px.transpose() * result.points).trace()) /
59 | (np * cols));
60 | return result;
61 | }
62 |
63 | NonrigidResult nonrigid(const Matrix& fixed, const Matrix& moving) {
64 | Nonrigid nonrigid;
65 | return nonrigid.run(fixed, moving);
66 | }
67 | } // namespace cpd
68 |
--------------------------------------------------------------------------------
/resource/scripts/meshlab_transform.mlx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/meshtracker/include/cpd_nonrigid.h:
--------------------------------------------------------------------------------
1 | // cpd - Coherent Point Drift
2 | // Copyright (C) 2017 Pete Gadomski
3 | //
4 | // This program is free software; you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation; either version 2 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU General Public License along
15 | // with this program; if not, write to the Free Software Foundation, Inc.,
16 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 |
18 | /// \file
19 | ///
20 | /// Nonrigid coherent point drift transform.
21 |
22 | #pragma once
23 |
24 | #include "cpd_transform.h"
25 |
26 | namespace cpd {
27 |
28 | /// Default value for beta.
29 | const double DEFAULT_BETA = 5.0; //default 3
30 | /// Default value for lambda.
31 | const double DEFAULT_LAMBDA = 5.0; //default 3
32 |
33 | /// The result of a nonrigid coherent point drift run.
34 | struct NonrigidResult : public Result {};
35 |
36 | /// Nonrigid coherent point drift.
37 | class Nonrigid : public Transform {
38 | public:
39 | Nonrigid()
40 | : Transform()
41 | , m_lambda(DEFAULT_LAMBDA)
42 | , m_beta(DEFAULT_BETA)
43 | , m_linked(DEFAULT_LINKED) {}
44 |
45 | /// Initialize this transform for the provided matrices.
46 | void init(const Matrix& fixed, const Matrix& moving);
47 |
48 | void getAffinity(const Matrix& fixed, const Matrix& moving, Matrix& _aff);
49 |
50 | /// Modifies the probabilities with some affinity and weight information.
51 | void modify_probabilities(Probabilities& probabilities) const;
52 |
53 | /// Sets the beta.
54 | Nonrigid& beta(double beta) {
55 | m_beta = beta;
56 | return *this;
57 | }
58 |
59 | /// Sets the lambda.
60 | Nonrigid& lambda(double lambda) {
61 | m_lambda = lambda;
62 | return *this;
63 | }
64 |
65 | /// Computes one iteration of the nonrigid transformation.
66 | NonrigidResult compute_one(const Matrix& fixed, const Matrix& moving,
67 | const Probabilities& probabilities,
68 | double sigma2) const;
69 |
70 | /// Sets whether the scalings of the two datasets are linked.
71 | Nonrigid& linked(bool linked) {
72 | m_linked = linked;
73 | return *this;
74 | }
75 |
76 | virtual bool linked() const { return m_linked; }
77 |
78 | private:
79 | Matrix m_g;
80 | Matrix m_w;
81 | double m_lambda;
82 | double m_beta;
83 | bool m_linked;
84 | };
85 |
86 | /// Runs a nonrigid registration on two matrices.
87 | NonrigidResult nonrigid(const Matrix& fixed, const Matrix& moving);
88 | } // namespace cpd
89 |
--------------------------------------------------------------------------------
/meshtracker/src/cpd_utils.cpp:
--------------------------------------------------------------------------------
1 | // cpd - Coherent Point Drift
2 | // Copyright (C) 2017 Pete Gadomski
3 | //
4 | // This program is free software; you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation; either version 2 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU General Public License along
15 | // with this program; if not, write to the Free Software Foundation, Inc.,
16 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 |
18 | #include
19 | #include
20 | #include
21 |
22 | #include "cpd_matrix.h"
23 | #include "cpd_utils.h"
24 |
25 | namespace cpd {
26 |
27 | Matrix matrix_from_path(const std::string& path) {
28 | std::ifstream file(path);
29 | if (!file.is_open()) {
30 | std::stringstream msg;
31 | msg << "Unable to open file for reading: " << path;
32 | throw std::runtime_error(msg.str());
33 | }
34 | std::string line;
35 | std::vector> rows;
36 | while (std::getline(file, line)) {
37 | std::vector row;
38 | std::stringstream ss(line);
39 | double n;
40 | while (ss >> n) {
41 | row.push_back(n);
42 | // TODO support other delimiters than commas
43 | if (ss.peek() == ',') {
44 | ss.ignore();
45 | }
46 | }
47 | if (!rows.empty() && rows.back().size() != row.size()) {
48 | std::stringstream msg;
49 | msg << "Irregular number of rows: " << rows.back().size() << ", "
50 | << row.size();
51 | throw std::runtime_error(msg.str());
52 | }
53 | rows.push_back(row);
54 | }
55 | if (rows.empty()) {
56 | return Matrix(0, 0);
57 | }
58 | size_t nrows = rows.size();
59 | size_t ncols = rows[0].size();
60 | Matrix matrix(nrows, ncols);
61 | for (size_t i = 0; i < nrows; ++i) {
62 | for (size_t j = 0; j < ncols; ++j) {
63 | matrix(i, j) = rows[i][j];
64 | }
65 | }
66 | return matrix;
67 | }
68 |
69 | double default_sigma2(const Matrix& fixed, const Matrix& moving) {
70 | return ((moving.rows() * (fixed.transpose() * fixed).trace()) +
71 | (fixed.rows() * (moving.transpose() * moving).trace()) -
72 | 2 * fixed.colwise().sum() * moving.colwise().sum().transpose()) /
73 | (fixed.rows() * moving.rows() * fixed.cols());
74 | }
75 |
76 | Matrix affinity(const Matrix& x, const Matrix& y, double beta) {
77 | double k = -2.0 * beta * beta;
78 | double k_rcp = 1.0/k;
79 | size_t x_rows = x.rows();
80 | size_t y_rows = y.rows();
81 | Matrix g;
82 | try {
83 | g = Matrix(x_rows, y_rows);
84 | } catch (const std::bad_alloc& err) {
85 | std::stringstream msg;
86 | msg << "Unable to allocate " << x_rows << " by " << y_rows
87 | << " affinity matrix, try again with fewer points";
88 | throw std::runtime_error(msg.str());
89 | }
90 | for (size_t i = 0; i < y_rows; ++i) {
91 | g.col(i) = ((x.array() - y.row(i).replicate(x_rows, 1).array())
92 | .pow(2)
93 | .rowwise()
94 | .sum() /
95 | k)
96 | .exp();
97 | }
98 | return g;
99 | }
100 | } // namespace cpd
101 |
--------------------------------------------------------------------------------
/resource/scripts/meshlab_decimate.mlx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/meshtracker/include/matching.h:
--------------------------------------------------------------------------------
1 | #ifndef MATCHING_H
2 | #define MATCHING_H
3 |
4 | #include "cloud_processing.h"
5 | #include "mesh_processing.h"
6 | #include "log.h"
7 |
8 | //
9 | typedef std::pair match_conf;
10 | typedef pcl::PointCloud::Ptr CloudPtr;
11 |
12 | // Use to find correspondence pairs between point clouds or meshes.
13 | // Functions can return confidences or use KNN to assume matches where
14 | // none are found.
15 | class Matcher
16 | {
17 | private:
18 | // TODO: Getter setter for source target with template func
19 | // to accept either mesh or pt cloud
20 |
21 | public:
22 | Matcher(){};
23 | ~Matcher(){};
24 |
25 | // Simple pcl nearest neighbour search that will match 1-to-1 for each input
26 | std::vector GetKNNMatches(
27 | const CloudPtr & source,
28 | const CloudPtr & query,
29 | const int & search_length = 10);
30 |
31 | // Simple pcl nearest neighbour search that will match 1-to-1 for each input
32 | std::vector GetKNNMatches(
33 | const pcl::PolygonMesh & source,
34 | const pcl::PolygonMesh & query,
35 | const int & search_length = 10);
36 |
37 | // Simple pcl nearest neighbour search that will match 1-to-1 for each input
38 | std::vector GetKNNMatches(
39 | const pcl::PointXYZRGBNormal & source,
40 | const CloudPtr & query,
41 | const int & tolerance,
42 | int search_length = 10);
43 |
44 | // Returns only the point correspondences which conform to strict matching rules.
45 | // i.e. sparse matching only
46 | std::vector GetConstrainedMatches(
47 | const pcl::PolygonMesh & source,
48 | const pcl::PolygonMesh & query,
49 | double align_tolerance = 0.25);
50 |
51 | // Returns only the point correspondences which conform to strict matching rules.
52 | // i.e. sparse matching only
53 | std::vector GetConstrainedMatches(
54 | const CloudPtr & source,
55 | const CloudPtr & query,
56 | double align_tolerance = 0.25,
57 | float radius = 0); // flag to auto calculate max_radius
58 |
59 | // Returns only the point correspondences which conform to strict matching rules.
60 | // i.e. sparse matching only
61 | std::vector GetConstrainedMatches(
62 | const pcl::PointXYZRGBNormal & source,
63 | const CloudPtr & query,
64 | const double & tolerance,
65 | int search_length = 10);
66 |
67 | // Performs KNN search from source to query, returns exact 1-to-1 matches as well
68 | // as confidence between [0, 1.0] based on normal alignment
69 | std::vector GetMatchesAndConfidence(
70 | CloudPtr & source,
71 | CloudPtr & query,
72 | const float normal_tol = 0.5,
73 | const float max_ray_dist = 2.0);
74 |
75 | // // Debug method outputs a colorized cloud which illustrates matching
76 | // void VisualizeMatches(
77 | // const std::vector& matches,
78 | // const CloudPtr& source,
79 | // const CloudPtr& query,
80 | // CloudPtr& _colored_source,
81 | // CloudPtr& _colored_query);
82 |
83 | // override for polygonmesh input
84 | void VisualizeMatches(
85 | const std::vector & matches,
86 | const pcl::PolygonMesh & source,
87 | const pcl::PolygonMesh & query,
88 | pcl::PolygonMesh & _colored_source,
89 | pcl::PolygonMesh & _colored_query);
90 |
91 | // Override for basic KNNMatches
92 | void VisualizeMatches(
93 | const std::vector & matches,
94 | const CloudPtr & source,
95 | const CloudPtr & query,
96 | CloudPtr & _colored_source,
97 | CloudPtr & _colored_query);
98 |
99 | // Override for basic KNNMatches
100 | void VisualizeMatches(
101 | const std::vector & matches,
102 | const CloudPtr & source,
103 | const CloudPtr & query,
104 | CloudPtr & _colored_source,
105 | CloudPtr & _colored_query);
106 | };
107 |
108 |
109 |
110 | #endif
--------------------------------------------------------------------------------
/resource/scripts/dot_similarity.m:
--------------------------------------------------------------------------------
1 |
2 | function keyframe_indices = dot_similarity(descriptor_path,feas_path,max_region_length)
3 |
4 | % grab the output path
5 | [filepath] = fileparts(char(descriptor_path));
6 |
7 | descriptors = load(descriptor_path);
8 |
9 | for row_idx = 1:size(descriptors,1)
10 | for row_idx_2 = 1:size(descriptors,1)
11 |
12 | %Generate similarity using simple dot product of features
13 | similarity(row_idx,row_idx_2)=dot(descriptors(row_idx,:),descriptors(row_idx_2,:));
14 |
15 | end
16 | end
17 |
18 | % normalize and plot
19 | sim_norm=normalize(similarity,'range');
20 | heatmap(sim_norm);
21 |
22 | % get average sim score per frame
23 | averaged_signal = mean(sim_norm,2);
24 |
25 | % load feas score
26 | feas = load(feas_path);
27 |
28 | % filter data with moving average
29 | % define coeffs
30 | a=1;
31 | % b=repmat(1/4,1,window_size);
32 | window_size = max_region_length/2; %ceil(seq_length * 0.025);
33 | b=(1/window_size)*ones(1,window_size);
34 |
35 | filtered_signal=filter(b,a,averaged_signal);
36 |
37 | plot(filtered_signal,'b','LineWidth',1);
38 | set(get(gca, 'XLabel'), 'String', 'Frame number');
39 | set(get(gca, 'YLabel'), 'String', 'Score');
40 | hold on;
41 |
42 | % define region locations about least similar points
43 | %local_minima=islocalmin(filtered_signal,'MinProminence',min_prominence);
44 | local_minima=islocalmin(filtered_signal,'MinSeparation',ceil(max_region_length/2),'SamplePoints',1:length(filtered_signal));
45 | region_stops=find(local_minima);
46 | region_stops(end+1) = length(filtered_signal);
47 |
48 |
49 | % push out last region stop if it's close enough to the end of the
50 | % sequence
51 | if region_stops(end) - region_stops(end-1) < max_region_length
52 | region_stops(end - 1) = region_stops(end);
53 | region_stops(end) = [];
54 | end
55 |
56 | msg=sprintf('Found %d regions in %d input frames.\n',length(region_stops), size(descriptors,1));
57 | disp(msg);
58 |
59 | % integrate boundary proximity score
60 | start_r = 1;
61 | region_starts = [1];
62 | for region_id = 1:length(region_stops)
63 | end_r = region_stops(region_id);
64 | feas(start_r) = 0;
65 | feas(start_r+1) = feas(start_r+1) * 0.5;
66 | feas(end_r) = 0;
67 | feas(end_r-1) = feas(end_r-1) * 0.5;
68 | start_r = end_r;
69 | end
70 |
71 | % for each region, set keyframe at most similar frame
72 | start_r = 1;
73 | keyframe_indices = int32.empty;
74 | region_starts = [1];
75 | for region_id = 1:length(region_stops)
76 | end_r = region_stops(region_id);
77 | region = feas(start_r:end_r,:);
78 | [val,idx] = max(region);
79 | keyframe_indices(end+1) = start_r + idx - 1;
80 | region_starts(end+1) = end_r+1;
81 | start_r = end_r;
82 |
83 | plot(keyframe_indices(end),val,'r.','MarkerSize',40);
84 | line_h = filtered_signal(start_r);
85 | line([start_r start_r], [0 line_h],'Color','magenta','LineStyle','--','LineWidth',1);
86 | end
87 |
88 | plot(feas,'r','LineWidth',1);
89 |
90 | % save output to intermediary file
91 | idcs = strfind(filepath,'/');
92 | outdir = filepath(1:idcs(end-1)-1);
93 | fileID=fopen(outdir +"/input/regions.txt",'w');
94 | for i = 1:length(keyframe_indices)
95 | fprintf(fileID,'%d,%d,%d\n',region_starts(i)-1,region_stops(i)-1,keyframe_indices(i)-1);
96 | end
97 | fclose(fileID);
98 |
99 | leg=legend('Similarity Score','Keyframe Indices','Region Boundaries');
100 | leg.FontSize = 20;
101 | ax=gca;
102 | ax.YAxis.FontSize = 20;
103 | ax.XAxis.FontSize = 20;
104 | set(gcf, 'Units', 'Normalized', 'OuterPosition', [0, 0.04, 0.5, 0.75]);
105 | title('Keyframe Indices and Region Boundaries');
106 | ax.TitleFontSizeMultiplier = 2;
107 | saveas(gcf,outdir+"/input/sim_score_filtered.png");
108 | %
109 | % hold off
110 | % plot(filtered_signal,'b','LineWidth',2);
111 | % set(get(gca, 'XLabel'), 'String', 'Frame number');
112 | % set(get(gca, 'YLabel'), 'String', 'Score');
113 | % hold on
114 | % feas = load('feas.txt');
115 | % feas_n = normalize(feas,'range')
116 | % plot(feas_n,'r','LineWidth',2);
117 | % leg=legend('Similarity Score','Feasibility Score');
118 | % leg.FontSize = 20;
119 | % ax=gca;
120 | % ax.YAxis.FontSize = 20;
121 | % ax.XAxis.FontSize = 20;
122 | % set(gcf, 'Units', 'Normalized', 'OuterPosition', [0, 0.04, 0.5, 0.75]);
123 | % title('Similarity Vs Feasibility')
124 | % ax.TitleFontSizeMultiplier = 2;
125 | % saveas(gcf,"sim_feas.png");
126 |
127 |
128 |
129 |
130 | end
131 |
--------------------------------------------------------------------------------
/meshtracker/include/mesh_segmentation.h:
--------------------------------------------------------------------------------
1 | #ifndef MESH_SEGMENTATION_H
2 | #define MESH_SEGMENTATION_H
3 |
4 | // vertex_id , segment_id
5 | typedef std::map VidSidMap;
6 | typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
7 | typedef CGAL::Surface_mesh SM;
8 |
9 | class MeshSegmentation
10 | {
11 |
12 | private:
13 | // default paramters seem good for 25K meshes of typical human body poses
14 | // generally aim to create as many segments as possible and then fuse the small ones
15 |
16 | int m_num_clusters = 20; // number of segment levels about large sdf values, recc = 20
17 | double m_lambda = 0.1; // smoothness param (0.0 - 1.0), recc = 0.1
18 | size_t m_num_segments = 0; //init member variable
19 | int m_seg_size_threshold = 100; // minimum allowed number of vertices for a segment
20 | bool m_seg_size_passed = false;
21 |
22 | public:
23 | MeshSegmentation(){};
24 | ~MeshSegmentation(){};
25 |
26 | // Takes a CGAL::Surface_mesh, performs segmentation based
27 | // on Shape diameter function. Outputs a vector of vert_id->seg_id pairs
28 | void Segment(
29 | const pcl::PolygonMesh & mesh,
30 | VidSidMap & output);
31 |
32 | // Looks for segments smaller than m_seg_size_threshold and fuse with nearest
33 | // connected neighbour
34 | VidSidMap FuseSmallSegments(
35 | const VidSidMap & vs_map,
36 | const pcl::PolygonMesh & mesh,
37 | const int & number_of_segments);
38 |
39 | // Finds connected segments and merges indices with the smallest neighbor.
40 | // Returns the index of the fused segment
41 | int FuseWithSmallestConnectedSegment(
42 | VidSidMap & vs_map_,
43 | const pcl::PolygonMesh & mesh,
44 | const size_t & ctr_sid);
45 |
46 | // Circulates each vertex in mesh to find a connected vertex with different
47 | // seg_id. Result is first unique segment. Returns -1 if none found
48 | int FindNeighbourSegment(
49 | const VidSidMap & vs_map,
50 | const int & seg_id,
51 | const pcl::PolygonMesh & _mesh) const;
52 |
53 | inline void SetNumClusters(const int & nc)
54 | {
55 | m_num_clusters = nc;
56 | }
57 |
58 | inline void SetLambda(const double & l)
59 | {
60 | m_lambda = l;
61 | }
62 |
63 | inline size_t GetNumSegments() const
64 | {
65 | return m_num_segments;
66 | }
67 |
68 | inline void SetNumSegments(const size_t & ns)
69 | {
70 | m_num_segments = ns;
71 | }
72 |
73 | inline void SetSizeCheckPass(const bool & state)
74 | {
75 | m_seg_size_passed = state;
76 | }
77 |
78 | // Returns a list of all indices for given segment id
79 | std::vector GetSegIndices(
80 | const VidSidMap & map,
81 | const int & seg_id);
82 |
83 | // Returns a vector of connected seg ids
84 | std::vector GetSegmentConnectivity(
85 | const int & seg_id,
86 | const VidSidMap & vs_map,
87 | const pcl::PolygonMesh & _mesh) const;
88 |
89 | // Reorders the seg_ids such that 0 is the most-connected descending
90 | void ReOrderSegMapByConnectivity(
91 | VidSidMap & vs_map,
92 | const pcl::PolygonMesh & mesh,
93 | const int & num_segs);
94 |
95 | // returns vertex indices of self and k-connected segments
96 | std::vector GetSelfAndConnectedSegIndices(
97 | const VidSidMap & vs_map,
98 | const pcl::PolygonMesh & mesh,
99 | const int & seg_id,
100 | const int & k = 2);
101 |
102 | // returns the seg_ids for input and connected segments
103 | std::vector GetSelfAndConnectedSegIds(
104 | const VidSidMap & vs_map,
105 | const pcl::PolygonMesh & mesh,
106 | const int & seg_id);
107 |
108 | // Given a VidSidMap vertex seg_id map, returns a polygonmesh for the given
109 | // seg_id consisting of all the faces around the input vertices. No conisderation
110 | // is given for overlapping faces.
111 | // NOTE: Ideally we would just add the required faces by index but for some
112 | // reason we get a seg fault from PCL when attempting to manually triangulate
113 | // input vertices. Hence, the implementation below takes the less efficient route
114 | // of adding every face and then slectively removing faces not associated with
115 | // the input vertex set.
116 | pcl::PolygonMesh MeshSegFromVertexIds(
117 | const pcl::PolygonMesh & global_mesh,
118 | const VidSidMap & vs_map,
119 | const int & seg_id);
120 |
121 | // Prints the distribution of points among discovered segments, can be returned
122 | std::map GetSegSizes(
123 | const VidSidMap & map,
124 | const bool & print);
125 |
126 | // Save segments to file for debug
127 | void ExportSegments(
128 | const VidSidMap & vs_map,
129 | const pcl::PolygonMesh & mesh,
130 | const std::string & out_path) const;
131 | };
132 |
133 | #endif
134 |
--------------------------------------------------------------------------------
/meshtracker/include/deform_graph.h:
--------------------------------------------------------------------------------
1 | #ifndef DEFORM_GRAPH_H
2 | #define DEFORM_GRAPH_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include
10 | #include
11 | #include
12 |
13 | #include
14 |
15 | #include
16 | #include
17 | #include
18 | #include
19 |
20 | #include "tri_mesh.h"
21 | #include "mesh_processing.h"
22 | #include "matching.h"
23 | #include "gauss_newton_solver.h"
24 | #include "utils.h"
25 |
26 | typedef pcl::PointCloud::Ptr CloudPtr;
27 |
28 | // Node idx, corrspondence pairs
29 | typedef std::vector> NodeCorrs;
31 |
32 | class DeformGraph
33 | {
34 |
35 | private:
36 | pcl::PolygonMesh graph_structure_;
37 | double node_sample_radius_ = 1.0; // init value only
38 | double dmax_scaler_ = 1.5; // Scales the inclusion radius during vert-node weight assignment
39 | size_t debug_vertex_ = 2000;
40 |
41 | public:
42 |
43 | std::vector node_norm;
44 | std::vector node_rots;
45 | std::vector node_rots_mat;
46 | std::vector node_trans;
47 | std::vector node_pos;
48 | std::vector> node_neigh;
49 | std::vector> node_node_weights;
50 | std::vector> vert_node_weights;
51 |
52 | double max_dist_neigh; // used for building neighbours
53 | int k_neigh;
54 |
55 | DeformGraph()
56 | {
57 | k_neigh = kDG_KNN;
58 | };
59 |
60 | ~DeformGraph(){};
61 |
62 | void BuildGraph(
63 | const TriMesh & mesh,
64 | pcl::PolygonMesh & in_graph,
65 | int k_nn = kDG_KNN);
66 |
67 | inline void SetMGraphStructure(pcl::PolygonMesh & mesh)
68 | {
69 | this->graph_structure_ = mesh;
70 | }
71 |
72 | inline void SetSampleRadius(const double & radius)
73 | {
74 | this->node_sample_radius_ = radius;
75 | }
76 |
77 | void BuildNeighbours();
78 |
79 | void CalculateNodeNodeWeights();
80 |
81 | std::vector> CalculateVertNodeWeights(
82 | const std::vector & verts,
83 | const std::vector> & vert_neigh);
84 |
85 | std::vector> CalculateVertNodeWeights(
86 | const pcl::PolygonMesh & in_mesh,
87 | const std::vector> & vert_neigh);
88 |
89 | void UpdateGraph(const Eigen::VectorXd & X);
90 |
91 | // Per-vertex get the knn nodes of influence for input mesh
92 | // in_graph is used as it will provide better correspondence
93 | std::vector> GetVertNodeNeighbours(
94 | pcl::PolygonMesh & in_mesh);
95 |
96 | // Get vert-node matches using graph connectivity and sampling radius
97 | // constraints. Requires cloud so that we don't have to
98 | // calculate norms erry time
99 | std::vector GetVertNodeMatches(
100 | const pcl::PointXYZRGBNormal & source,
101 | const pcl::PolygonMesh & query,
102 | const CloudPtr & query_cloud);
103 |
104 | std::vector> GetConNodeNeighbours(
105 | const std::vector & verts,
106 | int search_size = 12);
107 |
108 | // Smooth out the deform space to prevent high frequency noise
109 | void NormalizeTforms();
110 |
111 | void Deform(
112 | const std::vector> & node_influence_list,
113 | const std::vector> & vert_node_weights,
114 | const pcl::PolygonMesh & input_graph,
115 | TriMesh & d_mesh);
116 |
117 | // Initialize the vector of stacked affine transforms
118 | // 12 * num_nodes, [R|t] : 9 R, 3 t
119 | Eigen::VectorXd InitAffineVector(size_t nodeSize) const;
120 |
121 | //constraints_index is in respect to graph nodes' index
122 | //TODO: we could probably make a pair for index-weight access
123 | double OptimizeOnce(
124 | DeformGraph & graph,
125 | GNParams & params,
126 | std::vector> & con_node_neigh,
127 | std::vector> & con_node_weights,
128 | const NodeCorrs & cons);
129 |
130 | void DebugRandomVertNodeDeformInfluence(
131 | std::vector> node_influence_list,
132 | std::vector> vert_node_weights,
133 | CloudPtr moving_cloud,
134 | pcl::PolygonMesh graph_mesh,
135 | std::string out_path);
136 |
137 | };
138 |
139 | #endif
140 | ////////////////////////////////////////////////////////////////////////////////
141 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MeshTracker
2 |
3 | A segmentation-based tracking algorithm for registering volumetric video meshes (ply/obj) in C++.
4 | This is the official implementation of the paper:
5 | >Moynihan, M., Ruano, S., Pagés, R. and Smolic, A., 2021. Autonomous Tracking For Volumetric Video Sequences. In Proceedings of the IEEE/CVF Winter Conference on Applications of Computer Vision (pp. 1660-1669).
6 |
7 | ## Video
8 | [](https://youtu.be/JwO2obk0tJM)
9 |
10 | ## Getting Started
11 |
12 | * Clone the repository into your workspace and see instructions below for install:
13 |
14 | ### Installing
15 |
16 | 1. Install Docker
17 |
18 | 1. Download this repo and run the following from the same location as Dockerfile.
19 | NUM_JOBS is just an integer which will tell Make how many threads to use (Default=3).
20 |
21 | ```
22 | cd dockers && sh ./build_dockers.sh NUM_JOBS
23 | ```
24 |
25 | ** Use as many jobs as your cpu/ram will allow as the docker building is quite slow! **
26 |
27 | ### Usage
28 |
29 | To run the code on your own input simply run the following command
30 | ```
31 | docker run --rm -v :/input -v