diff --git a/.gitignore b/.gitignore index ec4828aa..79251b17 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ py_bind/wheelhouse/* build_artifacts # conda smithy ci-skeleton end *.whl +py_bind/optv/optv/*.h diff --git a/docs/readme_debugging.md b/docs/readme_debugging.md new file mode 100644 index 00000000..62646053 --- /dev/null +++ b/docs/readme_debugging.md @@ -0,0 +1,162 @@ +# OpenPTV liboptv Developer README + +## Overview + +This library (`liboptv`) is part of the OpenPTV project and provides core algorithms for particle tracking velocimetry, including 2D and 3D tracking routines. The codebase is C99, uses CMake for building, and includes a comprehensive suite of unit tests using the [Check](https://libcheck.github.io/check/) framework. + +This README is for developers who want to **debug**, **extend**, or **test** the library, especially using Visual Studio Code (VS Code) or other modern IDEs. + +--- + +## Prerequisites + +- **Linux** (tested on Ubuntu) +- **CMake** (>=3.10) +- **GCC** (with gdb for debugging) +- **Check** unit testing framework (`libcheck-dev`) +- **VS Code** (recommended, with C/C++ extension) +- **Electric Fence** (`electric-fence`, optional, for memory debugging) + +Install dependencies (Ubuntu example): + +```bash +sudo apt-get update +sudo apt-get install build-essential cmake libcheck-dev electric-fence gdb +``` + +--- + +## Building the Library + +```bash +cd /path/to/openptv/liboptv +mkdir -p build +cd build +cmake -DCMAKE_BUILD_TYPE=Debug .. +make +``` + +- The default build type is **Debug** (with debug symbols, no optimization). +- The shared library will be built as `liboptv.so` in `build/src/`. + +--- + +## Running Tests + +To run all tests: + +```bash +make verify +``` + +Or, using CTest: + +```bash +cd build +ctest +``` + +To run a specific test (e.g., only 3D tracking): + +```bash +ctest -R track3d +``` + +To see output on failure: + +```bash +CTEST_OUTPUT_ON_FAILURE=1 ctest -V -R track3d +``` + +--- + +## Debugging with VS Code + +1. **Open the project root in VS Code:** + + ```bash + code /path/to/openptv/liboptv + ``` + +2. **Build in Debug mode** (see above). + +3. **Set up `.vscode/launch.json`:** + + Example for debugging the 3D tracking test: + + ```json + { + "version": "0.2.0", + "configurations": [ + { + "name": "Debug check_track3d", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/tests/check_track3d", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/build/tests", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] + } + ``` + +4. **Set breakpoints** in any source file (e.g., `src/track3d.c`). + +5. **Start debugging** from the Run & Debug panel. + +--- + +## Tips for Debugging + +- **Step into library code:** Ensure you build with `-g -O0` (Debug mode, no optimization). +- **Flush output:** Add `fflush(stdout);` after `printf` to see output immediately during tests. +- **Run a single test:** Use `ctest -R testname` or pass arguments to your test executable. +- **Check source paths:** Open the original source files in VS Code for breakpoints to work. + +--- + +## Directory Structure + +- `src/` — Core library source files +- `include/` — Public headers +- `tests/` — Unit and integration tests (Check framework) +- `build/` — Build directory (created by you) +- `.vscode/` — VS Code configuration (optional) + +--- + +## Common Issues + +- **Cannot step into src code:** + Make sure you built with debug symbols and no optimization. Clean and rebuild if needed. +- **Linker errors for efence:** + Install `electric-fence` or comment out its usage in `CMakeLists.txt`. +- **CTest output is buffered:** + Use `fflush(stdout);` or run with `CTEST_OUTPUT_ON_FAILURE=1`. + +--- + +## Contributing + +- Follow the code style of existing files. +- Add tests for new features or bugfixes. +- Document your changes. + +--- + +## Further Help + +If you have questions or issues, open an issue on the OpenPTV GitHub or contact the maintainers. + +--- \ No newline at end of file diff --git a/liboptv/include/correspondences.h b/liboptv/include/correspondences.h index 78f39ae2..02ab40ba 100644 --- a/liboptv/include/correspondences.h +++ b/liboptv/include/correspondences.h @@ -8,7 +8,7 @@ #include "calibration.h" #include "epi.h" -#define nmax 202400 +#define nmax 20240 typedef struct diff --git a/liboptv/include/track3d.h b/liboptv/include/track3d.h new file mode 100644 index 00000000..1a013108 --- /dev/null +++ b/liboptv/include/track3d.h @@ -0,0 +1,18 @@ +#ifndef TRACK3D_H +#define TRACK3D_H + +#include "parameters.h" +#include "vec_utils.h" +#include "imgcoord.h" +#include "multimed.h" +#include "orientation.h" +#include "calibration.h" +#include "track.h" +#include "tracking_frame_buf.h" + + +void track3d_loop(tracking_run *run_info, int step); +int find_candidates_in_3d(frame *frm, vec3d pos, double dx, double dy, double dz, int *indices, int max_cands); + + +#endif // TRACK3D_H \ No newline at end of file diff --git a/liboptv/src/CMakeLists.txt b/liboptv/src/CMakeLists.txt index 6ba24e37..82e49dc8 100644 --- a/liboptv/src/CMakeLists.txt +++ b/liboptv/src/CMakeLists.txt @@ -1,4 +1,3 @@ - # Turn on more warnings if(MSVC) # Force to always compile with W4 @@ -14,8 +13,7 @@ endif() include_directories("../include/") -add_library (optv SHARED tracking_frame_buf.c calibration.c parameters.c lsqadj.c ray_tracing.c trafo.c vec_utils.c image_processing.c multimed.c imgcoord.c epi.c orientation.c sortgrid.c segmentation.c correspondences.c track.c tracking_run.c) - +add_library (optv SHARED tracking_frame_buf.c calibration.c parameters.c lsqadj.c ray_tracing.c trafo.c vec_utils.c image_processing.c multimed.c imgcoord.c epi.c orientation.c sortgrid.c segmentation.c correspondences.c track.c tracking_run.c track3d.c) if(UNIX) @@ -31,3 +29,8 @@ if(WIN32) endif(WIN32) install(TARGETS optv DESTINATION lib) + +# Set Debug as the default build type if not specified +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) +endif() diff --git a/liboptv/src/track3d.c b/liboptv/src/track3d.c new file mode 100644 index 00000000..8c0920ac --- /dev/null +++ b/liboptv/src/track3d.c @@ -0,0 +1,203 @@ +/******************************************************************************* +** +** Title: ptv +** +** Author: Heinrich Stueer +** +** Description: Main modul of track. +** Dies ist eine abgespeckte Version vom Malik und Papantoniou (allerdings mit +** ein paar Anderungen) +** Created: 12.02.1998 +** Changes: +** +*******************************************************************************/ +/* +Copyright (c) 1990-2011 ETH Zurich + +See the file license.txt for copying permission. +*/ + +/* ----- recent changes ------------------------------------------------------- + ad holten, 12-2012 + replaced: + if (filenumber < 10) sprintf (filein, +"res/rt_is.%1d", filenumber); else if (filenumber < 100) sprintf (filein, +"res/rt_is.%2d", filenumber); + else sprintf (filein, +"res/rt_is.%3d", filenumber); by sprintf (filein, "res/rt_is.%d", filenumber); + + replaced: + fp = fopen (filein, "r"); + if (! fp) printf("Can't open ascii file: %s\n", filein); + by + fp = fopen_rp (filein); (fopen_rp prints an error message on +failure) if (!fp) return; +------------------------------------------------------------------------------*/ + +#include "tracking_run.h" +#include "track3d.h" +#include "track.h" +#include +#include +#define _USE_MATH_DEFINES +#include + + +void track3d_loop(tracking_run *run_info, int step) { + // Shortcuts into the tracking_run struct + framebuf_base *fb = run_info->fb; + track_par *tpar = run_info->tpar; + + frame *prev = fb->buf[0]; + frame *curr = fb->buf[1]; // current frame + frame *next = fb->buf[2]; + + // frame *prev = fb_get_frame(fb, 0); + // frame *curr = fb_get_frame(fb, 1); + // frame *next = fb_get_frame(fb, 2); + int i, d, k; + int orig_parts = curr->num_parts; + int cand_indices[MAX_CANDS]; + float decis[MAX_CANDS]; + int linkdecis[MAX_CANDS]; + vec3d predicted, vel; + int nvel; + int count1 = 0; // Count of links established in level 1 + + double dx = tpar->dvxmax; + double dy = tpar->dvymax; + double dz = tpar->dvzmax; + + // Level 1: Particles with previous links + for (i = 0; i < orig_parts; i++) { + P *curr_path_inf = &curr->path_info[i]; + if (curr_path_inf->prev < 0) continue; + int prev_idx = curr_path_inf->prev; + if (prev_idx < 0 || prev_idx >= prev->num_parts) continue; + P *prev_path_inf = &prev->path_info[prev_idx]; + for (d = 0; d < 3; d++) + predicted[d] = 2 * curr_path_inf->x[d] - prev_path_inf->x[d]; + int num_cands = find_candidates_in_3d(next, predicted, dx, dy, dz, cand_indices, MAX_CANDS); + for (k = 0; k < num_cands; k++) { + float acc = 0.0; + for (d = 0; d < 3; d++) { + float diff = curr_path_inf->x[d] - 2 * next->path_info[cand_indices[k]].x[d] + prev_path_inf->x[d]; + acc += diff * diff; + } + decis[k] = sqrtf(acc); + linkdecis[k] = cand_indices[k]; + } + if (num_cands > 1) { + sort(num_cands, decis, linkdecis); + } + if (num_cands > 0 && next->path_info[linkdecis[0]].prev < 0) { + curr_path_inf->next = linkdecis[0]; + next->path_info[linkdecis[0]].prev = i; + count1++; + } else { + curr_path_inf->next = -1; + } + } + + // Level 2: No previous link, but neighbors have previous links + for (i = 0; i < orig_parts; i++) { + P *curr_path_inf = &curr->path_info[i]; + if (curr_path_inf->prev >= 0 || curr_path_inf->next >= 0) continue; + nvel = 0; + for (d = 0; d < 3; d++) vel[d] = 0.0; + for (int j = 0; j < orig_parts; j++) { + if (j == i) continue; + P *nbr = &curr->path_info[j]; + if (fabs(nbr->x[0] - curr_path_inf->x[0]) < dx && + fabs(nbr->x[1] - curr_path_inf->x[1]) < dy && + fabs(nbr->x[2] - curr_path_inf->x[2]) < dz && + nbr->prev >= 0) { + for (d = 0; d < 3; d++) + vel[d] += nbr->x[d] - prev->path_info[nbr->prev].x[d]; + nvel++; + } + } + if (nvel == 0) continue; + for (d = 0; d < 3; d++) vel[d] /= nvel; + for (d = 0; d < 3; d++) + predicted[d] = curr_path_inf->x[d] + vel[d]; + int num_cands = find_candidates_in_3d(next, predicted, dx, dy, dz, cand_indices, MAX_CANDS); + for (k = 0; k < num_cands; k++) { + float acc = 0.0; + for (d = 0; d < 3; d++) { + float diff = curr_path_inf->x[d] - 2 * next->path_info[cand_indices[k]].x[d] + predicted[d]; + acc += diff * diff; + } + decis[k] = sqrtf(acc); + linkdecis[k] = cand_indices[k]; + } + if (num_cands > 1) { + sort(num_cands, decis, linkdecis); + } + if (num_cands > 0 && next->path_info[linkdecis[0]].prev < 0) { + curr_path_inf->next = linkdecis[0]; + next->path_info[linkdecis[0]].prev = i; + count1++; + } else { + curr_path_inf->next = -1; + } + } + + // Level 3: No previous link, no neighbors with previous links + for (i = 0; i < orig_parts; i++) { + P *curr_path_inf = &curr->path_info[i]; + if (curr_path_inf->prev >= 0 || curr_path_inf->next >= 0) continue; + for (d = 0; d < 3; d++) + predicted[d] = curr_path_inf->x[d]; + int num_cands = find_candidates_in_3d(next, predicted, dx, dy, dz, cand_indices, MAX_CANDS); + for (k = 0; k < num_cands; k++) { + float acc = 0.0; + for (d = 0; d < 3; d++) { + float diff = curr_path_inf->x[d] - 2 * next->path_info[cand_indices[k]].x[d] + predicted[d]; + acc += diff * diff; + } + decis[k] = sqrtf(acc); + linkdecis[k] = cand_indices[k]; + } + if (num_cands > 1) { + sort(num_cands, decis, linkdecis); + } + if (num_cands > 0 && next->path_info[linkdecis[0]].prev < 0) { + curr_path_inf->next = linkdecis[0]; + next->path_info[linkdecis[0]].prev = i; + count1++; + } else { + curr_path_inf->next = -1; + } + } + /* end of creation of links with decision check */ + + printf("track3d step: %d, curr: %d, next: %d, links: %d\n", + step, fb->buf[1]->num_parts, fb->buf[2]->num_parts, + count1); + + /* for the average of particles and links */ // NOLINT // NOLINT + run_info->npart = run_info->npart + fb->buf[1]->num_parts; + run_info->nlinks = run_info->nlinks + count1; + + fb_next(fb); + fb_write_frame_from_start(fb, step); + if (step < run_info->seq_par->last - 2) { + fb_read_frame_at_end(fb, step + 3, 0); + } +} + +// Returns the number of candidates found within a 3D box centered at pos +int find_candidates_in_3d(frame *frm, vec3d pos, double dx, double dy, double dz, int *indices, int max_cands) { + int i, count = 0; + for (i = 0; i < frm->num_parts; i++) { + if (fabs(frm->path_info[i].x[0] - pos[0]) < dx && + fabs(frm->path_info[i].x[1] - pos[1]) < dy && + fabs(frm->path_info[i].x[2] - pos[2]) < dz) { + if (count < max_cands) { + indices[count++] = i; + } + } + } + return count; +} diff --git a/liboptv/tests/CMakeLists.txt b/liboptv/tests/CMakeLists.txt index ff259979..ab264ce3 100644 --- a/liboptv/tests/CMakeLists.txt +++ b/liboptv/tests/CMakeLists.txt @@ -55,12 +55,14 @@ add_dependencies(check_epi optv testing_data) add_executable(check_segmentation EXCLUDE_FROM_ALL check_segmentation.c) add_dependencies(check_segmentation optv testing_data) -add_executable(check_track EXCLUDE_FROM_ALL check_track.c) -add_dependencies(check_track optv testing_data) - add_executable(check_correspondences EXCLUDE_FROM_ALL check_correspondences.c) add_dependencies(check_correspondences optv testing_data) +add_executable(check_track EXCLUDE_FROM_ALL check_track.c) +add_dependencies(check_track optv testing_data) + +add_executable(check_track3d EXCLUDE_FROM_ALL ../tests/check_track3d.c) +add_dependencies(check_track3d optv testing_data) set(LIBS ${LIBS} ${CHECK_LIBRARIES} optv) @@ -121,8 +123,11 @@ add_test(check_correspondences ${CMAKE_CURRENT_BINARY_DIR}/check_correspondences target_link_libraries(check_track ${LIBS}) add_test(check_track ${CMAKE_CURRENT_BINARY_DIR}/check_track) +target_link_libraries(check_track3d ${LIBS}) +add_test(check_track3d ${CMAKE_CURRENT_BINARY_DIR}/check_track3d) + add_custom_target(verify COMMAND ${CMAKE_CTEST_COMMAND}) add_dependencies(verify check_fb check_calibration check_parameters check_lsqadj check_ray_tracing check_trafo check_vec_utils check_image_proc check_multimed check_imgcoord check_orientation check_epi check_sortgrid check_segmentation -check_correspondences check_track) +check_correspondences check_track check_track3d) diff --git a/liboptv/tests/check_track.c b/liboptv/tests/check_track.c index 9a2d232d..cb4c0ca6 100644 --- a/liboptv/tests/check_track.c +++ b/liboptv/tests/check_track.c @@ -842,8 +842,8 @@ START_TEST(test_new_particle) } END_TEST -Suite* fb_suite(void) { - Suite *s = suite_create ("ttools"); +Suite* track_suite(void) { + Suite *s = suite_create ("track"); TCase *tc = tcase_create ("predict test"); tcase_add_test(tc, test_predict); suite_add_tcase (s, tc); @@ -914,7 +914,7 @@ Suite* fb_suite(void) { int main(void) { int number_failed; - Suite *s = fb_suite (); + Suite *s = track_suite (); SRunner *sr = srunner_create (s); // srunner_run_all (sr, CK_ENV); srunner_run_all (sr, CK_VERBOSE); diff --git a/liboptv/tests/check_track3d.c b/liboptv/tests/check_track3d.c new file mode 100644 index 00000000..d7dee04a --- /dev/null +++ b/liboptv/tests/check_track3d.c @@ -0,0 +1,334 @@ +/* Unit tests for the 3D tracking (track3d_loop). Uses the Check framework. + This is a copy of check_track.c, but calls track3d_loop instead of trackcorr_c_loop. + Copyright 2025 OpenPTV contributors +*/ + +#include +#include +#include +#include +#include +#include +#include "track.h" +#include "track3d.h" +#include "calibration.h" +#include +#include +#include + +#define EPS 1E-5 + +// Helper functions +void read_all_calibration(Calibration *calib[], int num_cams) { + char ori_tmpl[] = "cal/cam%d.tif.ori"; + char added_tmpl[] = "cal/cam%d.tif.addpar"; + char ori_name[256],added_name[256]; + int cam; + for (cam = 0; cam < num_cams; cam++) { + sprintf(ori_name, ori_tmpl, cam + 1); + sprintf(added_name, added_tmpl, cam + 1); + calib[cam] = read_calibration(ori_name, added_name, NULL); + } +} + +int copy_res_dir(char *src, char *dest) { + DIR *dirp; + struct dirent *dp; + FILE *in_f, *out_f; + int errno; + char file_name[256]; + char buf[8192]; + ssize_t result; + dirp = opendir(src); + while (dirp) { + errno = 0; + if ((dp = readdir(dirp)) != NULL) { + if (dp->d_name[0] == '.') continue; + strncpy(file_name, src, 255); + file_name[255] = '\0'; // Ensure null termination + strncat(file_name, dp->d_name, 255 - strlen(file_name) - 1); // Adjust size for remaining space + in_f = fopen(file_name, "r"); + strncpy(file_name, dest, 255); + file_name[255] = '\0'; // Ensure null termination + strncat(file_name, dp->d_name, 255 - strlen(file_name) - 1); // Adjust size for remaining space + out_f = fopen(file_name, "w"); + while (!feof(in_f)) { + result = fread(buf, 1, sizeof(buf), in_f); + fwrite(buf, 1, result, out_f); + } + fclose(in_f); + fclose(out_f); + } else { + closedir(dirp); + return 1; + } + } + return 0; +} + +int empty_res_dir() { + DIR *dirp; + struct dirent *dp; + int errno; + char file_name[256]; + dirp = opendir("res/"); + while (dirp) { + errno = 0; + if ((dp = readdir(dirp)) != NULL) { + if (dp->d_name[0] == '.') continue; + strncpy(file_name, "res/", 255); + strncat(file_name, dp->d_name, 255); + remove(file_name); + } else { + closedir(dirp); + return 1; + } + } + return 0; +} + +START_TEST(test_track3d_no_add) +{ + tracking_run *run; + int step; + Calibration *calib[3]; + control_par *cpar; + char cwd[PATH_MAX]; + + + if (getcwd(cwd, sizeof(cwd)) != NULL) { + printf("Current working directory: %s\n", cwd); + } else { + perror("getcwd() error"); + } + + + chdir("testing_fodder/track"); + // chdir("testing_fodder/track"); + copy_res_dir("res_orig/", "res/"); + copy_res_dir("img_orig/", "img/"); + + printf("----------------------------\n"); + printf("Test tracking multiple files 2 cameras, 1 particle\n"); + cpar = read_control_par("parameters/ptv.par"); + read_all_calibration(calib, cpar->num_cams); + + run = tr_new_legacy("parameters/sequence.par", + "parameters/track.par", "parameters/criteria.par", + "parameters/ptv.par", calib); + run->tpar->add = 0; + + + + track_forward_start(run); + track3d_loop(run, run->seq_par->first); + + for (step = run->seq_par->first + 1; step < run->seq_par->last; step++) { + track3d_loop(run, step); + } + trackcorr_c_finish(run, run->seq_par->last); + + empty_res_dir(); + + int range = run->seq_par->last - run->seq_par->first; + double npart, nlinks; + + /* average of all steps */ + npart = (double)run->npart / range; + nlinks = (double)run->nlinks / range; + + printf("npart: %d\n", run->npart); + printf("nlinks: %d\n", run->nlinks); + + ck_assert_msg(fabs(npart - 0.8)num_cams); + + run = tr_new_legacy("parameters/sequence.par", + "parameters/track.par", "parameters/criteria.par", + "parameters/ptv.par", calib); + + printf("num cams in run is %d\n",run->cpar->num_cams); + printf("add particle is %d\n",run->tpar->add); + + track_forward_start(run); + for (step = run->seq_par->first; step < run->seq_par->last; step++) { + track3d_loop(run, step); + } + trackcorr_c_finish(run, run->seq_par->last); + printf("total num parts is %d, num links is %d \n", run->npart, run->nlinks); + + ck_assert_msg(run->npart == 672+699+711, + "Was expecting npart == 2082 but found %d \n", run->npart); + ck_assert_msg(run->nlinks >= 132+176+144, + "Was expecting nlinks >= 452 found %ld \n", run->nlinks); + + + empty_res_dir(); +} +END_TEST + +START_TEST(track3d_test_burgers) +{ + tracking_run *run; + Calibration *calib[4]; + control_par *cpar; + int status, step; + struct stat st = {0}; + char cwd[PATH_MAX]; + + + + printf("----------------------------\n"); + printf("Test Burgers vortex case with track3d \n"); + + + if (getcwd(cwd, sizeof(cwd)) != NULL) { + printf("Current working directory: %s\n", cwd); + } else { + perror("getcwd() error"); + } + + + chdir("testing_fodder/burgers"); + + // chdir("testing_fodder/burgers"); + + if (stat("res", &st) == -1) { + mkdir("res", 0700); + } + copy_res_dir("res_orig/", "res/"); + + if (stat("img", &st) == -1) { + mkdir("img", 0700); + } + copy_res_dir("img_orig/", "img/"); + + fail_if((cpar = read_control_par("parameters/ptv.par"))== 0); + read_all_calibration(calib, cpar->num_cams); + + run = tr_new_legacy("parameters/sequence.par", + "parameters/track.par", "parameters/criteria.par", + "parameters/ptv.par", calib); + + printf("num cams in run is %d\n", run->cpar->num_cams); + printf("add particle is %d\n", run->tpar->add); + + track_forward_start(run); + for (step = run->seq_par->first; step < run->seq_par->last; step++) { + track3d_loop(run, step); + } + trackcorr_c_finish(run, run->seq_par->last); + printf("total num parts is %d, num links is %d \n", run->npart, run->nlinks); + + ck_assert_msg(run->npart == 19, + "Was expecting npart == 19 but found %d \n", run->npart); + ck_assert_msg(run->nlinks == 18, + "Was expecting nlinks == 18 found %ld \n", run->nlinks); + + + // run = tr_new_legacy("parameters/sequence.par", + // "parameters/track.par", "parameters/criteria.par", + // "parameters/ptv.par", calib); + + // run->tpar->add = 1; + // printf("changed add particle to %d\n", run->tpar->add); + + // track_forward_start(run); + // for (step = run->seq_par->first; step < run->seq_par->last; step++) { + // track3d_loop(run, step); + // } + // trackcorr_c_finish(run, run->seq_par->last); + // printf("total num parts is %d, num links is %d \n", run->npart, run->nlinks); + + // // ck_assert_msg(run->npart == 20, + // // "Was expecting npart == 20 but found %d \n", run->npart); + // // ck_assert_msg(run->nlinks ==20, + // // "Was expecting nlinks == 20 but found %d \n", run->nlinks); + + empty_res_dir(); + +} +END_TEST + + +Suite *track3d_suite(void) +{ + Suite *s; + TCase *tc_core; + + s = suite_create("Track3D"); + + /* Core test case */ + tc_core = tcase_create("core test"); + tcase_add_test(tc_core, test_track3d_no_add); + suite_add_tcase(s, tc_core); + + tc_core = tcase_create("test_cavity"); + tcase_add_test(tc_core, track3d_test_cavity); + suite_add_tcase(s, tc_core); + + tc_core = tcase_create("burgers test"); + tcase_add_test(tc_core, track3d_test_burgers); + suite_add_tcase(s, tc_core); + + return s; +} + +int main(void) +{ + int number_failed; + Suite *s; + SRunner *sr; + + s = track3d_suite(); + sr = srunner_create(s); + + // srunner_set_fork_status(sr, CK_NOFORK); + + srunner_run_all(sr, CK_VERBOSE); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/py_bind/optv/optv/calibration.h b/py_bind/optv/optv/calibration.h deleted file mode 120000 index 0e13603c..00000000 --- a/py_bind/optv/optv/calibration.h +++ /dev/null @@ -1 +0,0 @@ -../../liboptv/include/calibration.h \ No newline at end of file diff --git a/py_bind/optv/optv/correspondences.h b/py_bind/optv/optv/correspondences.h deleted file mode 120000 index 4288ba96..00000000 --- a/py_bind/optv/optv/correspondences.h +++ /dev/null @@ -1 +0,0 @@ -../../liboptv/include/correspondences.h \ No newline at end of file diff --git a/py_bind/optv/optv/epi.h b/py_bind/optv/optv/epi.h deleted file mode 120000 index 7e608474..00000000 --- a/py_bind/optv/optv/epi.h +++ /dev/null @@ -1 +0,0 @@ -../../liboptv/include/epi.h \ No newline at end of file diff --git a/py_bind/optv/optv/glass.h b/py_bind/optv/optv/glass.h deleted file mode 120000 index f29ce080..00000000 --- a/py_bind/optv/optv/glass.h +++ /dev/null @@ -1 +0,0 @@ -../../liboptv/include/glass.h \ No newline at end of file diff --git a/py_bind/optv/optv/image_processing.h b/py_bind/optv/optv/image_processing.h deleted file mode 120000 index 60a9621f..00000000 --- a/py_bind/optv/optv/image_processing.h +++ /dev/null @@ -1 +0,0 @@ -../../liboptv/include/image_processing.h \ No newline at end of file diff --git a/py_bind/optv/optv/imgcoord.h b/py_bind/optv/optv/imgcoord.h deleted file mode 120000 index c7285842..00000000 --- a/py_bind/optv/optv/imgcoord.h +++ /dev/null @@ -1 +0,0 @@ -../../liboptv/include/imgcoord.h \ No newline at end of file diff --git a/py_bind/optv/optv/lsqadj.h b/py_bind/optv/optv/lsqadj.h deleted file mode 120000 index 2e71b805..00000000 --- a/py_bind/optv/optv/lsqadj.h +++ /dev/null @@ -1 +0,0 @@ -../../liboptv/include/lsqadj.h \ No newline at end of file diff --git a/py_bind/optv/optv/multimed.h b/py_bind/optv/optv/multimed.h deleted file mode 120000 index b2c0fa1e..00000000 --- a/py_bind/optv/optv/multimed.h +++ /dev/null @@ -1 +0,0 @@ -../../liboptv/include/multimed.h \ No newline at end of file diff --git a/py_bind/optv/optv/orientation.h b/py_bind/optv/optv/orientation.h deleted file mode 120000 index 30359597..00000000 --- a/py_bind/optv/optv/orientation.h +++ /dev/null @@ -1 +0,0 @@ -../../liboptv/include/orientation.h \ No newline at end of file diff --git a/py_bind/optv/optv/parameters.h b/py_bind/optv/optv/parameters.h deleted file mode 120000 index c1055375..00000000 --- a/py_bind/optv/optv/parameters.h +++ /dev/null @@ -1 +0,0 @@ -../../liboptv/include/parameters.h \ No newline at end of file diff --git a/py_bind/optv/optv/ray_tracing.h b/py_bind/optv/optv/ray_tracing.h deleted file mode 120000 index 0e0748b4..00000000 --- a/py_bind/optv/optv/ray_tracing.h +++ /dev/null @@ -1 +0,0 @@ -../../liboptv/include/ray_tracing.h \ No newline at end of file diff --git a/py_bind/optv/optv/segmentation.h b/py_bind/optv/optv/segmentation.h deleted file mode 120000 index 2042850e..00000000 --- a/py_bind/optv/optv/segmentation.h +++ /dev/null @@ -1 +0,0 @@ -../../liboptv/include/segmentation.h \ No newline at end of file diff --git a/py_bind/optv/optv/sortgrid.h b/py_bind/optv/optv/sortgrid.h deleted file mode 120000 index 3b380de2..00000000 --- a/py_bind/optv/optv/sortgrid.h +++ /dev/null @@ -1 +0,0 @@ -../../liboptv/include/sortgrid.h \ No newline at end of file diff --git a/py_bind/optv/optv/track.h b/py_bind/optv/optv/track.h deleted file mode 120000 index 3fae937a..00000000 --- a/py_bind/optv/optv/track.h +++ /dev/null @@ -1 +0,0 @@ -../../liboptv/include/track.h \ No newline at end of file diff --git a/py_bind/optv/optv/tracking_frame_buf.h b/py_bind/optv/optv/tracking_frame_buf.h deleted file mode 120000 index c74c4398..00000000 --- a/py_bind/optv/optv/tracking_frame_buf.h +++ /dev/null @@ -1 +0,0 @@ -../../liboptv/include/tracking_frame_buf.h \ No newline at end of file diff --git a/py_bind/optv/optv/tracking_run.h b/py_bind/optv/optv/tracking_run.h deleted file mode 120000 index 71d80d51..00000000 --- a/py_bind/optv/optv/tracking_run.h +++ /dev/null @@ -1 +0,0 @@ -../../liboptv/include/tracking_run.h \ No newline at end of file diff --git a/py_bind/optv/optv/trafo.h b/py_bind/optv/optv/trafo.h deleted file mode 120000 index fc6ae143..00000000 --- a/py_bind/optv/optv/trafo.h +++ /dev/null @@ -1 +0,0 @@ -../../liboptv/include/trafo.h \ No newline at end of file diff --git a/py_bind/optv/optv/vec_utils.h b/py_bind/optv/optv/vec_utils.h deleted file mode 120000 index 7085986a..00000000 --- a/py_bind/optv/optv/vec_utils.h +++ /dev/null @@ -1 +0,0 @@ -../../liboptv/include/vec_utils.h \ No newline at end of file diff --git a/py_bind/optv/tracker.pxd b/py_bind/optv/tracker.pxd index 40ce24f7..5a1f50e4 100644 --- a/py_bind/optv/tracker.pxd +++ b/py_bind/optv/tracker.pxd @@ -25,6 +25,9 @@ cdef extern from "optv/track.h": void trackcorr_c_finish(tracking_run *run_info, int step) double trackback_c(tracking_run *run_info) +cdef extern from "optv/track3d.h": + void track3d_loop(tracking_run *run_info, int step) + cdef class Tracker: cdef tracking_run *run_info cdef int step diff --git a/py_bind/optv/tracker.pyx b/py_bind/optv/tracker.pyx index 85357ed2..31fbd48e 100644 --- a/py_bind/optv/tracker.pyx +++ b/py_bind/optv/tracker.pyx @@ -14,7 +14,8 @@ from optv.tracking_framebuf cimport fb_free # External C functions from tracking_run.h should be declared in tracker.pxd from optv.tracker cimport ( tr_new, track_forward_start, trackcorr_c_loop, - trackcorr_c_finish, trackback_c, TR_BUFSPACE, MAX_TARGETS + trackcorr_c_finish, trackback_c, TR_BUFSPACE, MAX_TARGETS, + track3d_loop ) def _encode_if_needed(s): @@ -92,7 +93,7 @@ cdef class Tracker: trackcorr_c_loop(self.run_info, self.step) self.step += 1 - return True + return True def finalize(self): """ @@ -109,7 +110,28 @@ cdef class Tracker: self.run_info.seq_par.first, self.run_info.seq_par.last): trackcorr_c_loop(self.run_info, step) trackcorr_c_finish(self.run_info, self.run_info.seq_par.last) - + + def step_forward_3d(self): + """ + Perform one tracking step for the current frame of iteration. + """ + if self.step >= self.run_info.seq_par.last: + return False + + track3d_loop(self.run_info, self.step) + self.step += 1 + return True + + def full_forward_3d(self): + """ + Do a full tracking run from restart to finalize. + """ + track_forward_start(self.run_info) + for step in range( + self.run_info.seq_par.first, self.run_info.seq_par.last): + track3d_loop(self.run_info, step) + trackcorr_c_finish(self.run_info, self.run_info.seq_par.last) + def full_backward(self): """ Does a full backward run on existing tracking results. so make sure diff --git a/py_bind/optv/version.py b/py_bind/optv/version.py index f2b3589f..5a271749 100644 --- a/py_bind/optv/version.py +++ b/py_bind/optv/version.py @@ -1 +1 @@ -__version__ = "0.3.0" \ No newline at end of file +__version__ = "0.3.1" \ No newline at end of file diff --git a/py_bind/test/test_burgers.py b/py_bind/test/test_burgers.py index 01f119e4..7e7c9dcf 100644 --- a/py_bind/test/test_burgers.py +++ b/py_bind/test/test_burgers.py @@ -80,6 +80,36 @@ def test_forward(self): last_step += 1 self.tracker.finalize() + def test_forward_3d(self): + """Manually running a full forward tracking run.""" + # path = 'testing_fodder/burgers/res' + # try: + # os.mkdir(path) + # except OSError: + # print("Creation of the directory %s failed" % path) + # else: + # print("Successfully created the directory %s " % path) + + shutil.copytree( + "testing_fodder/burgers/res_orig/", "testing_fodder/burgers/res/") + shutil.copytree( + "testing_fodder/burgers/img_orig/", "testing_fodder/burgers/img/") + + self.tracker.restart() + last_step = 10001 + while self.tracker.step_forward_3d(): + self.assertTrue(self.tracker.current_step() > last_step) + with open("testing_fodder/burgers/res/rt_is.%d" % last_step) as f: + lines = f.readlines() + # print(last_step,lines[0]) + # print(lines) + if last_step == 10003: + self.assertTrue(lines[0] == "4\n") + else: + self.assertTrue(lines[0] == "5\n") + last_step += 1 + self.tracker.finalize() + def test_full_forward(self): """Automatic full forward tracking run.""" # os.mkdir('testing_fodder/burgers/res') @@ -91,6 +121,17 @@ def test_full_forward(self): # if it passes without error, we assume it's ok. The actual test is in # the C code. + def test_full_forward_3d(self): + """Automatic full forward tracking run.""" + # os.mkdir('testing_fodder/burgers/res') + shutil.copytree( + "testing_fodder/burgers/res_orig/", "testing_fodder/burgers/res/") + shutil.copytree( + "testing_fodder/burgers/img_orig/", "testing_fodder/burgers/img/") + self.tracker.full_forward_3d() + # if it passes without error, we assume it's ok. The actual test is in + # the C code. + def test_full_backward(self): """Automatic full backward correction phase.""" shutil.copytree( diff --git a/py_bind/test/test_tracker.py b/py_bind/test/test_tracker.py index 1b32a8b3..22ecc785 100644 --- a/py_bind/test/test_tracker.py +++ b/py_bind/test/test_tracker.py @@ -71,6 +71,27 @@ def test_forward(self): last_step += 1 self.tracker.finalize() + def test_forward_3d(self): + """Manually running a full forward tracking run.""" + shutil.copytree( + "testing_fodder/track/res_orig/", "testing_fodder/track/res/") + + self.tracker.restart() + last_step = 10001 + while self.tracker.step_forward_3d(): + # print(f"step is {self.tracker.current_step()}\n") + # print(self.tracker.current_step() > last_step) + self.assertGreater(self.tracker.current_step(), last_step) + with open("testing_fodder/track/res/linkage.%d" % last_step) as f: + lines = f.readlines() + # print(last_step,lines[0]) + if last_step == 10003: + self.assertTrue(lines[0] == "-1\n") + else: + self.assertTrue(lines[0] == "1\n") + last_step += 1 + self.tracker.finalize() + def test_full_forward(self): """Automatic full forward tracking run.""" shutil.copytree( @@ -79,6 +100,14 @@ def test_full_forward(self): # if it passes without error, we assume it's ok. The actual test is in # the C code. + def test_full_forward_3d(self): + """Automatic full forward tracking run.""" + shutil.copytree( + "testing_fodder/track/res_orig/", "testing_fodder/track/res/") + self.tracker.full_forward_3d() + # if it passes without error, we assume it's ok. The actual test is in + # the C code. + def test_full_backward(self): """Automatic full backward correction phase.""" shutil.copytree(