// fh-tracker: hands in 3D from fh-camd's ring, published for Frametop's ft-screens. // The C++ version of tracker/live.py: the same scheduling, with the models on a few // threads and no Python in the loop. // // fh-tracker [--seconds N] [--threads N] [--int8] [--status S] [--models DIR] [--nice N] // [--no-publish] [--record DIR] [--swap-sides] ... (--help lists them all) #include "io.h" #include "record.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace { volatile std::sig_atomic_t g_stop = 0, g_record = 0; // which calibrated camera each capture pipe carries (XRService's fixed routing) const char *camera_for_pipe(int node) { char path[64], name[64] = ""; std::snprintf(path, sizeof path, "/sys/class/video4linux/video%d/name", node); std::ifstream f(path); f.getline(name, sizeof name); if (!std::strcmp(name, "msm_vfe3_video0")) return "slam_left"; if (!std::strcmp(name, "msm_vfe4_video0")) return "slam_right"; if (!std::strcmp(name, "msm_vfe2_video0")) return "upper_left"; if (!std::strcmp(name, "msm_vfe2_video1")) return "upper_right"; return nullptr; } double cpu_seconds() { rusage r; getrusage(RUSAGE_SELF, &r); return r.ru_utime.tv_sec + r.ru_stime.tv_sec + (r.ru_utime.tv_usec + r.ru_stime.tv_usec) / 1e6; } } // namespace int main(int argc, char **argv) { double seconds = 0, status = 5; int threads = 3, niceness = 5; bool int8 = false, publish = true, track = true, swap_sides = false; std::string models = std::string(argv[0]).substr(0, std::string(argv[0]).rfind('/') + 1) + "../models/ncnn"; std::string record; // SteamOS starts user processes on CPUs 0-4 (2-4 are the big A720s) and keeps 5-7 (two // A720s and the X4) for SteamVR's compositor, whose threads there run at real-time // priority. XRService pins its head tracking to 2-3. std::vector cpus = {2, 3, 4}; // How crops are equalized. CLAHE helps the palm search find hands (about 10% more in the // dim recording), but makes the landmarks jitter, so they get plain crops. Contrast palm_contrast, hand_contrast{Contrast::None}; double keep_presence = 0.5; // landmark presence a tracked view needs to stay double record_for = 120; for (int i = 1; i < argc; ++i) { const std::string a = argv[i]; const bool more = i + 1 < argc; if (a == "--seconds" && more) seconds = std::atof(argv[++i]); else if (a == "--threads" && more) threads = std::max(1, std::atoi(argv[++i])); else if (a == "--status" && more) status = std::atof(argv[++i]); else if (a == "--models" && more) models = argv[++i]; else if (a == "--nice" && more) niceness = std::atoi(argv[++i]); else if (a == "--int8") int8 = true; else if (a == "--no-publish") publish = false; else if (a == "--swap-sides") swap_sides = true; else if (a == "--record-only") track = publish = false; else if (a == "--record" && more) record = argv[++i]; else if (a == "--record-for" && more) record_for = std::atof(argv[++i]); else if (a == "--keep-presence" && more) keep_presence = std::atof(argv[++i]); else if (a == "--contrast" && more) { if (!Contrast::parse_pair(argv[++i], palm_contrast, hand_contrast)) return std::fprintf(stderr, "--contrast MODE or PALM/HAND, each clahe[:CLIP]|none|stretch\n"), 1; } else if (a == "--cpus" && more) { cpus.clear(); for (char *p = argv[++i]; *p;) { cpus.push_back(int(std::strtol(p, &p, 10))); if (*p == ',') ++p; else if (*p) break; } if (cpus.empty()) cpus = {2, 3, 4}; } else { std::printf("usage: %s [--seconds N] [--threads N] [--int8] [--status S] [--models DIR] [--nice N] [--no-publish]\n" " [--record DIR] [--record-for S] [--record-only] [--cpus 2,3,4] [--swap-sides]\n" " [--keep-presence P] (0.5)\n" " [--contrast MODE|PALM/HAND] (clahe[:CLIP], none, stretch; default clahe:2/none)\n" "Recording saves every frame set for S seconds (120) to DIR/sets.bin, for fh-replay; SIGUSR1\n" "starts one in captures/rec-