40#include <visp3/core/vpConfig.h>
42#if defined(VISP_HAVE_CATCH2) && defined(VISP_HAVE_DATASET) && (VISP_HAVE_DATASET_VERSION >= 0x030702)
44#include <visp3/core/vpIoTools.h>
45#include <visp3/core/vpImageConvert.h>
46#include <visp3/io/vpImageIo.h>
47#include <visp3/rbt/vpRBTracker.h>
49#include <visp3/rbt/vpRBSilhouetteMeTracker.h>
50#include <visp3/rbt/vpRBSilhouetteCCDTracker.h>
51#include <visp3/rbt/vpRBKltTracker.h>
52#include <visp3/rbt/vpRBDenseDepthTracker.h>
53#include <visp3/ar/vpPanda3DFrameworkManager.h>
55#include "test_utils.h"
57#if defined(VISP_HAVE_NLOHMANN_JSON)
58#include VISP_NLOHMANN_JSON(json.hpp)
61#define CATCH_CONFIG_RUNNER
62#include <catch_amalgamated.hpp>
64#ifdef ENABLE_VISP_NAMESPACE
68bool opt_no_display =
false;
72 vpImage<unsigned char> I;
81 Sequence(
const std::string &path)
89 unsigned int getImageHeight()
const {
return m_h; }
90 unsigned int getImageWidth()
const {
return m_w; }
91 vpCameraParameters
cam()
const {
return m_cam; }
93 void loadGroundTruth()
96 std::ifstream f(groundTruthPath);
98 throw vpException(
vpException::ioError,
"Could not open ground truth file %s", groundTruthPath.c_str());
100 nlohmann::json
j = nlohmann::json::parse(f);
101 m_cMg =
j.at(
"grid");
109 void loadCameraSettings()
115 std::ifstream cf(camFile);
120 const nlohmann::json
j = nlohmann::json::parse(cf);
121 m_cam.initPersProjWithoutDistortion(
j.at(
"px"),
j.at(
"py"),
j.at(
"u0"),
j.at(
"v0"));
122 m_h =
j.at(
"h"), m_w =
j.at(
"w");
123 m_depthScale =
j.at(
"depthScale");
126 const std::string getColorFrame(
unsigned int index)
128 std::stringstream colorName;
129 colorName <<
"color_image_" << std::setfill(
'0') << std::setw(4) << index <<
".png";
133 const std::string getDepthFrame(
unsigned int index)
135 std::stringstream colorName;
136 colorName <<
"depth_image_" << std::setfill(
'0') << std::setw(4) << index <<
".npz";
140 void initTracker(vpRBTracker &tracker)
142 tracker.setCameraParameters(m_cam, m_h, m_w);
147 unsigned int frameIndex = 0;
150 const std::string colorFramePath = getColorFrame(frameIndex);
157 const std::string depthFramePath = getDepthFrame(frameIndex);
160 vpImage<uint16_t> depthRaw(depth_data.
data<uint16_t>(), m_h, m_w,
true);
161 frame.depth.resize(m_h, m_w);
162 for (
unsigned int i = 0;
i < m_h * m_w; ++
i) {
163 frame.depth.bitmap[
i] =
static_cast<float>(depthRaw.bitmap[
i]) * m_depthScale;
167 m_frames.push_back(frame);
172 unsigned int numFrames()
const
174 return m_frames.size();
176 SequenceFrame getFrame(
unsigned int index)
const
178 return m_frames[index];
181 std::map<std::string, vpHomogeneousMatrix> getInitialPoses(vpRBTracker &tracker,
const std::string &initsFolder,
const std::string &objectsFolder,
const std::vector<std::string> &objectNames)
183 std::map<std::string, vpHomogeneousMatrix> result;
184 for (
const std::string &objectName: objectNames) {
190 std::ifstream f(objectInitFile);
192 vpHomogeneousMatrix
cMo = nlohmann::json::parse(f);
193 result[objectName] =
cMo;
196 throw vpException(
vpException::ioError,
"There was an issue opening init file %s", objectInitFile.c_str());
203 tracker.initClick(m_frames[0].IRGB, objectClickInitFile,
true);
204 vpHomogeneousMatrix
cMo;
206 std::ofstream f(objectInitFile);
208 nlohmann::json
j =
cMo;
216 vpHomogeneousMatrix getGroundTruthGridPose(
unsigned int index)
const
223 vpCameraParameters m_cam;
225 unsigned int m_h, m_w;
226 std::vector<SequenceFrame> m_frames;
227 std::vector<vpHomogeneousMatrix> m_cMg;
232 RunData(
const std::string &config,
double errorT,
double errorR) : configName(
config), thresholdErrorT(errorT), thresholdErrorR(errorR) { }
233 const std::string configName;
234 const double thresholdErrorT;
235 const double thresholdErrorR;
239SCENARIO(
"Running tracker on sequences with ground truth",
"[rbt]")
241 if (opt_no_display) {
242 std::cout <<
"Display is disabled for tests, skipping..." << std::endl;
254 Sequence sequence(sequencePath);
256 sequence.initTracker(tracker);
257 unsigned int h = sequence.getImageHeight(),
w = sequence.getImageWidth();
265 "Depth", displayDepth,
269 const std::vector<std::string> objectNames = {
"dragon",
"cube",
"stomach",
"lower_teeth" };
271 std::map<std::string, vpHomogeneousMatrix> init_cMos = sequence.getInitialPoses(tracker, initsFolder, modelsPath, objectNames);
272 const double EPSILON_M = 0.002, EPSILON_DEG = 0.2;
273 const std::map<std::string, std::vector<RunData>> configMap = {
275 RunData(
"ccd.json", 0.03, 10.0),
276 RunData(
"ccd-temporal-smoothing.json", 0.03, 27.0),
277 RunData(
"depth-ccd-mask.json", 0.025, 8.0),
278 RunData(
"depth-ccd.json", 0.025, 8.0),
279#ifdef VP_HAVE_RB_KLT_TRACKER
280 RunData(
"depth-klt-ccd-mask.json", 0.02, 5.0),
281 RunData(
"depth-klt.json", 0.02, 5.5),
282 RunData(
"depth-klt-me-multi.json", 0.02, 5.0),
283 RunData(
"depth-klt-me-single.json", 0.025, 10.0),
288 RunData(
"ccd-temporal-smoothing.json", 0.03, 10.0),
289 RunData(
"ccd.json", 0.03, 10.0),
290 RunData(
"depth-ccd.json", 0.03, 10.0),
291 RunData(
"depth-ccd-mask.json", 0.03, 10.0),
292#ifdef VP_HAVE_RB_KLT_TRACKER
293 RunData(
"depth-klt.json", 0.03, 10.0),
294 RunData(
"depth-klt-ccd-mask.json", 0.03, 5.0),
295 RunData(
"depth-klt-me-multi.json", 0.03, 5.0),
296 RunData(
"depth-klt-me-single.json", 0.03, 10.0),
301 RunData(
"ccd-temporal-smoothing.json", 0.02, 10.0),
302 RunData(
"ccd.json", 0.02, 4.0),
303 RunData(
"depth-ccd.json", 0.025, 5.0),
304 RunData(
"depth-ccd-mask.json", 0.03, 10.0),
305#ifdef VP_HAVE_RB_KLT_TRACKER
306 RunData(
"depth-klt-ccd-mask.json", 0.03, 5.0),
307 RunData(
"depth-klt.json", 0.03, 3.0),
308 RunData(
"depth-klt-me-multi.json", 0.03, 5.0),
309 RunData(
"depth-klt-me-single.json", 0.03, 10.0),
316 for (
const std::string &objectName: objectNames) {
320 tracker.setModelPath(modelPath);
321 const auto configsIt = configMap.find(objectName);
322 if (configsIt == configMap.end()) {
323 std::cout <<
"No config found for " << objectName << std::endl;
326 const std::vector<RunData> objectConfigs = configsIt->second;
328 for (
const RunData &runConfig: objectConfigs) {
329 const std::string configName = runConfig.configName;
330 std::cout <<
"Running tracker on object " << objectName <<
" with configuration " << configName << std::endl;
333 tracker.loadConfigurationFile(configPath);
336 tracker.setPose(init_cMos.find(objectName)->second);
337 std::vector<vpHomogeneousMatrix> poses;
339 for (
unsigned int i = 0;
i < sequence.numFrames(); ++
i) {
340 SequenceFrame frame = sequence.getFrame(i);
344 poses.push_back(cMo);
347 displayRGB = frame.IRGB;
348 for (
unsigned int j = 0;
j < frame.depth.getSize(); ++
j) {
349 displayDepth.bitmap[
j] =
static_cast<unsigned char>(std::min(frame.depth.bitmap[j], 1.f) * 255.f);
354 tracker.display(displayI, displayRGB, displayDepth);
358 tracker.displayMask(displayMask);
366 std::vector<vpHomogeneousMatrix> first_gMos;
367 for (
unsigned int i = 0;
i < 10; ++
i) {
368 first_gMos.push_back(sequence.getGroundTruthGridPose(0).inverse() * poses[i]);
371 std::vector<double> errorsT, errorsR;
372 for (
unsigned int i = 0;
i < sequence.numFrames(); ++
i) {
376 const double errorT =
error.getTranslationVector().frobeniusNorm();
378 errorsT.push_back(errorT);
379 errorsR.push_back(errorR);
381 std::sort(errorsT.begin(), errorsT.end()); std::sort(errorsR.begin(), errorsR.end());
382 unsigned int index90p =
static_cast<unsigned int>(round(0.9 * errorsT.size()));
383 double error90pT = errorsT[index90p], error90pR = errorsR[index90p];
385 std::cout << error90pT <<
"m, " << error90pR <<
"°" << std::endl;
387 if (error90pT > (runConfig.thresholdErrorT + EPSILON_M) || error90pR > (runConfig.thresholdErrorR + EPSILON_DEG)) {
388 std::stringstream ss;
389 ss <<
"Using object " << objectName <<
" with config " << configName <<
":" << std::endl;
390 ss <<
"\tMaximum tolerated median error:\t" << runConfig.thresholdErrorT <<
"m, " << runConfig.thresholdErrorR <<
"°" << std::endl;
391 ss <<
"\tActual median error:\t\t\t" << error90pT <<
"m, " << error90pR <<
"°" << std::endl;
401int main(
int argc,
char *argv[])
403 Catch::Session session;
404 auto cli = session.cli()
405 | Catch::Clara::Opt(opt_no_display)[
"--no-display"](
"Disable display");
408 const int returnCode = session.applyCommandLine(argc, argv);
409 if (returnCode != 0) {
413 const int numFailed = session.run();
static const vpColor none
static const vpColor yellow
static void display(const vpImage< unsigned char > &I)
static void displayFrame(const vpImage< unsigned char > &I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, double size, const vpColor &color=vpColor::none, unsigned int thickness=1, const vpImagePoint &offset=vpImagePoint(0, 0), const std::string &frameName="", const vpColor &textColor=vpColor::black, const vpImagePoint &textOffset=vpImagePoint(15, 15))
static void flush(const vpImage< unsigned char > &I)
Implementation of an homogeneous matrix and operations on such kind of matrices.
vpHomogeneousMatrix inverse() const
static vpHomogeneousMatrix mean(const std::vector< vpHomogeneousMatrix > &vec_M)
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
static void read(vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
Definition of the vpImage class member functions.
static double deg(double rad)
static vpPanda3DFrameworkManager & getInstance()
Class implementing the Render-Based Tracker (RBT).
VISP_EXPORT npz_t npz_load(const std::string &fname)
std::vector< std::shared_ptr< vpDisplay > > makeDisplayGrid(unsigned int rows, unsigned int cols, unsigned int startX, unsigned int startY, unsigned int paddingX, unsigned int paddingY, Args &... args)
Create a grid of displays, given a set of images. All the displays will be initialized in the correct...