#include <visp3/core/vpConfig.h>
#if defined(VISP_HAVE_CATCH2) && defined(VISP_HAVE_DATASET) && (VISP_HAVE_DATASET_VERSION >= 0x030702)
#include <visp3/core/vpIoTools.h>
#include <visp3/core/vpImageConvert.h>
#include <visp3/io/vpImageIo.h>
#include <visp3/rbt/vpRBTracker.h>
#include <visp3/rbt/vpRBSilhouetteMeTracker.h>
#include <visp3/rbt/vpRBSilhouetteCCDTracker.h>
#include <visp3/rbt/vpRBKltTracker.h>
#include <visp3/rbt/vpRBDenseDepthTracker.h>
#include <visp3/ar/vpPanda3DFrameworkManager.h>
#include "test_utils.h"
#if defined(VISP_HAVE_NLOHMANN_JSON)
#include VISP_NLOHMANN_JSON(json.hpp)
#endif
#define CATCH_CONFIG_RUNNER
#include <catch_amalgamated.hpp>
#ifdef ENABLE_VISP_NAMESPACE
#endif
bool opt_no_display = false;
struct SequenceFrame
{
};
class Sequence
{
public:
Sequence(const std::string &path)
{
m_path = path;
loadCameraSettings();
loadFrames();
loadGroundTruth();
}
unsigned int getImageHeight() const { return m_h; }
unsigned int getImageWidth() const { return m_w; }
vpCameraParameters
cam()
const {
return m_cam; }
void loadGroundTruth()
{
std::ifstream f(groundTruthPath);
if (!f.good()) {
throw vpException(
vpException::ioError,
"Could not open ground truth file %s", groundTruthPath.c_str());
}
nlohmann::json
j = nlohmann::json::parse(f);
f.close();
}
void loadCameraSettings()
{
}
std::ifstream cf(camFile);
if (!cf.good()) {
}
const nlohmann::json
j = nlohmann::json::parse(cf);
m_cam.initPersProjWithoutDistortion(
j.at(
"px"),
j.at(
"py"),
j.at(
"u0"),
j.at(
"v0"));
m_h =
j.at(
"h"), m_w =
j.at(
"w");
m_depthScale =
j.at(
"depthScale");
}
const std::string getColorFrame(unsigned int index)
{
std::stringstream colorName;
colorName << "color_image_" << std::setfill('0') << std::setw(4) << index << ".png";
}
const std::string getDepthFrame(unsigned int index)
{
std::stringstream colorName;
colorName << "depth_image_" << std::setfill('0') << std::setw(4) << index << ".npz";
}
void initTracker(vpRBTracker &tracker)
{
tracker.setCameraParameters(m_cam, m_h, m_w);
}
void loadFrames()
{
unsigned int frameIndex = 0;
while (true) {
SequenceFrame frame;
const std::string colorFramePath = getColorFrame(frameIndex);
break;
}
const std::string depthFramePath = getDepthFrame(frameIndex);
vpImage<uint16_t> depthRaw(depth_data.
data<uint16_t>(), m_h, m_w,
true);
frame.depth.resize(m_h, m_w);
for (
unsigned int i = 0;
i < m_h * m_w; ++
i) {
frame.depth.bitmap[
i] =
static_cast<float>(depthRaw.bitmap[
i]) * m_depthScale;
}
}
m_frames.push_back(frame);
++frameIndex;
}
}
unsigned int numFrames() const
{
return m_frames.size();
}
SequenceFrame getFrame(unsigned int index) const
{
return m_frames[index];
}
std::map<std::string, vpHomogeneousMatrix> getInitialPoses(vpRBTracker &tracker, const std::string &initsFolder, const std::string &objectsFolder, const std::vector<std::string> &objectNames)
{
std::map<std::string, vpHomogeneousMatrix> result;
for (const std::string &objectName: objectNames) {
std::ifstream f(objectInitFile);
if (f.good()) {
vpHomogeneousMatrix
cMo = nlohmann::json::parse(f);
result[objectName] =
cMo;
}
else {
throw vpException(
vpException::ioError,
"There was an issue opening init file %s", objectInitFile.c_str());
}
f.close();
}
else {
tracker.initClick(m_frames[0].IRGB, objectClickInitFile,
true);
std::ofstream f(objectInitFile);
if (f.good()) {
}
}
}
return result;
}
vpHomogeneousMatrix getGroundTruthGridPose(unsigned int index) const
{
return m_cMg[index];
}
private:
std::string m_path;
vpCameraParameters m_cam;
float m_depthScale;
unsigned int m_h, m_w;
std::vector<SequenceFrame> m_frames;
std::vector<vpHomogeneousMatrix> m_cMg;
};
struct RunData
{
RunData(
const std::string &config,
double errorT,
double errorR) : configName(
config), thresholdErrorT(errorT), thresholdErrorR(errorR) { }
const std::string configName;
const double thresholdErrorT;
const double thresholdErrorR;
};
SCENARIO("Running tracker on sequences with ground truth", "[rbt]")
{
if (opt_no_display) {
std::cout << "Display is disabled for tests, skipping..." << std::endl;
}
else {
GIVEN("A sequence")
{
Sequence sequence(sequencePath);
sequence.initTracker(tracker);
unsigned int h = sequence.getImageHeight(),
w = sequence.getImageWidth();
"Gray", displayI,
"Color", displayRGB,
"Depth", displayDepth,
"Mask", displayMask
);
const std::vector<std::string> objectNames = { "dragon", "cube", "stomach", "lower_teeth" };
std::map<std::string, vpHomogeneousMatrix> init_cMos = sequence.getInitialPoses(tracker, initsFolder, modelsPath, objectNames);
const double EPSILON_M = 0.002, EPSILON_DEG = 0.2;
const std::map<std::string, std::vector<RunData>> configMap = {
{ "dragon", {
RunData("ccd.json", 0.03, 10.0),
RunData("ccd-temporal-smoothing.json", 0.03, 27.0),
RunData("depth-ccd-mask.json", 0.025, 8.0),
RunData("depth-ccd.json", 0.025, 8.0),
#ifdef VP_HAVE_RB_KLT_TRACKER
RunData("depth-klt-ccd-mask.json", 0.02, 5.0),
RunData("depth-klt.json", 0.02, 5.5),
RunData("depth-klt-me-multi.json", 0.02, 5.0),
RunData("depth-klt-me-single.json", 0.025, 10.0),
#endif
}
},
{ "cube", {
RunData("ccd-temporal-smoothing.json", 0.03, 10.0),
RunData("ccd.json", 0.03, 10.0),
RunData("depth-ccd.json", 0.03, 10.0),
RunData("depth-ccd-mask.json", 0.03, 10.0),
#ifdef VP_HAVE_RB_KLT_TRACKER
RunData("depth-klt.json", 0.03, 10.0),
RunData("depth-klt-ccd-mask.json", 0.03, 5.0),
RunData("depth-klt-me-multi.json", 0.03, 5.0),
RunData("depth-klt-me-single.json", 0.03, 10.0),
#endif
}
},
{ "stomach", {
RunData("ccd-temporal-smoothing.json", 0.02, 10.0),
RunData("ccd.json", 0.02, 4.0),
RunData("depth-ccd.json", 0.025, 5.0),
RunData("depth-ccd-mask.json", 0.03, 10.0),
#ifdef VP_HAVE_RB_KLT_TRACKER
RunData("depth-klt-ccd-mask.json", 0.03, 5.0),
RunData("depth-klt.json", 0.03, 3.0),
RunData("depth-klt-me-multi.json", 0.03, 5.0),
RunData("depth-klt-me-single.json", 0.03, 10.0),
#endif
}
},
};
for (const std::string &objectName: objectNames) {
const auto configsIt = configMap.find(objectName);
if (configsIt == configMap.end()) {
std::cout << "No config found for " << objectName << std::endl;
continue;
}
const std::vector<RunData> objectConfigs = configsIt->second;
for (const RunData &runConfig: objectConfigs) {
const std::string configName = runConfig.configName;
std::cout << "Running tracker on object " << objectName << " with configuration " << configName << std::endl;
tracker.loadConfigurationFile(configPath);
tracker.setPose(init_cMos.find(objectName)->second);
std::vector<vpHomogeneousMatrix> poses;
for (
unsigned int i = 0;
i < sequence.numFrames(); ++
i) {
SequenceFrame frame = sequence.getFrame(i);
poses.push_back(cMo);
displayI = frame.I;
displayRGB = frame.IRGB;
for (
unsigned int j = 0;
j < frame.depth.getSize(); ++
j) {
displayDepth.bitmap[
j] =
static_cast<unsigned char>(std::min(frame.depth.bitmap[j], 1.f) * 255.f);
}
tracker.display(displayI, displayRGB, displayDepth);
}
std::vector<vpHomogeneousMatrix> first_gMos;
for (
unsigned int i = 0;
i < 10; ++
i) {
first_gMos.push_back(sequence.getGroundTruthGridPose(0).inverse() * poses[i]);
}
std::vector<double> errorsT, errorsR;
for (
unsigned int i = 0;
i < sequence.numFrames(); ++
i) {
const double errorT =
error.getTranslationVector().frobeniusNorm();
errorsT.push_back(errorT);
errorsR.push_back(errorR);
}
std::sort(errorsT.begin(), errorsT.end()); std::sort(errorsR.begin(), errorsR.end());
unsigned int index90p = static_cast<unsigned int>(round(0.9 * errorsT.size()));
double error90pT = errorsT[index90p], error90pR = errorsR[index90p];
std::cout << error90pT << "m, " << error90pR << "°" << std::endl;
if (error90pT > (runConfig.thresholdErrorT + EPSILON_M) || error90pR > (runConfig.thresholdErrorR + EPSILON_DEG)) {
std::stringstream ss;
ss << "Using object " << objectName << " with config " << configName << ":" << std::endl;
ss << "\tMaximum tolerated median error:\t" << runConfig.thresholdErrorT << "m, " << runConfig.thresholdErrorR << "°" << std::endl;
ss << "\tActual median error:\t\t\t" << error90pT << "m, " << error90pR << "°" << std::endl;
FAIL(ss.str());
}
}
}
}
}
}
int main(int argc, char *argv[])
{
Catch::Session session;
auto cli = session.cli()
| Catch::Clara::Opt(opt_no_display)["--no-display"]("Disable display");
session.cli(cli);
const int returnCode = session.applyCommandLine(argc, argv);
if (returnCode != 0) {
return returnCode;
}
const int numFailed = session.run();
return numFailed;
}
#else
int main()
{
return EXIT_SUCCESS;
}
#endif
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...