From 32221917d160b1c4db66a6c45f00ebaf04fa88d3 Mon Sep 17 00:00:00 2001 From: kaatrasa Date: Fri, 19 Jan 2024 16:04:58 +0200 Subject: [PATCH 1/2] Add default recording directory to recording tools --- cpp/k4a/CMakeLists.txt | 16 +++++----------- cpp/k4a/record.cpp | 33 +++++++++++++++++++++++++++------ cpp/orbbec/CMakeLists.txt | 15 ++++----------- cpp/orbbec/record.cpp | 35 ++++++++++++++++++++++++++++------- cpp/realsense/CMakeLists.txt | 6 ++++-- cpp/realsense/record.cpp | 32 +++++++++++++++++++++++++++----- python/cli/record/oak.py | 4 ++-- 7 files changed, 97 insertions(+), 44 deletions(-) diff --git a/cpp/k4a/CMakeLists.txt b/cpp/k4a/CMakeLists.txt index 2678e21..1599c1d 100644 --- a/cpp/k4a/CMakeLists.txt +++ b/cpp/k4a/CMakeLists.txt @@ -2,31 +2,25 @@ if(MSVC) # Windows build uses newer features cmake_minimum_required(VERSION 3.21) else() - cmake_minimum_required(VERSION 3.3) + cmake_minimum_required(VERSION 3.12) endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") project(spectacularAI_k4a_tools) -if(MSVC) - set(CMAKE_CXX_STANDARD 20) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") -else() - set(CMAKE_CXX_STANDARD 14) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") -endif() - find_package(Threads REQUIRED) find_package(spectacularAI_k4aPlugin REQUIRED) -if(MSVC) # Must be after project() is called +if(MSVC) set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /Gy") # ./cmake/Findk4a.cmake is only tested on Windows and ideally we would rely on system dependency find_package(k4a MODULE REQUIRED) else() - set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") find_package(k4a REQUIRED PATHS "${k4a_DIR}") SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--exclude-libs=ALL") diff --git a/cpp/k4a/record.cpp b/cpp/k4a/record.cpp index 001919c..799af06 100644 --- a/cpp/k4a/record.cpp +++ b/cpp/k4a/record.cpp @@ -2,6 +2,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -9,7 +13,8 @@ void showUsage() { std::cout << "Supported arguments:" << std::endl << " -h, --help Help" << std::endl - << " --output , recorded output" << std::endl + << " --output , otherwise recording is saved to current working directory" << std::endl + << " --auto_subfolders, create timestamp-named subfolders for each recording" << std::endl << " --recording_only, disables Vio" << std::endl << " --color_res <720p, 1080p, 1440p, 1536p, 2160p, 3070p>" << std::endl << " --depth_mode <1 (NVOF_2X2BINNED), 2 (NVOF_UNBINNED), 3 (WFOV_2X2BINNED), 4 (WFOV_UNBINNED)>" << std::endl @@ -26,6 +31,18 @@ void showUsage() { << std::endl; } +void setAutoSubfolder(std::string &recordingFolder) { + auto now = std::chrono::system_clock::now(); + auto timePoint = std::chrono::system_clock::to_time_t(now); + std::tm localTime = *std::localtime(&timePoint); + std::ostringstream oss; + oss << std::put_time(&localTime, "%Y-%m-%d_%H-%M-%S"); + std::filesystem::path basePath = recordingFolder; + std::filesystem::path filename = oss.str(); + std::filesystem::path combinedPath = basePath / filename; + recordingFolder = combinedPath.string(); +} + int main(int argc, char *argv[]) { std::vector arguments(argv, argv + argc); std::string colorResolution = "720p"; @@ -37,11 +54,14 @@ int main(int argc, char *argv[]) { int32_t brightness = -1; spectacularAI::k4aPlugin::Configuration config; bool print = false; + bool autoSubfolders = false; for (size_t i = 1; i < arguments.size(); ++i) { const std::string &argument = arguments.at(i); if (argument == "--output") config.recordingFolder = arguments.at(++i); + else if (argument == "--auto_subfolders") + autoSubfolders = true; else if (argument == "--recording_only") config.recordingOnly = true; else if (argument == "--color_res") @@ -78,11 +98,11 @@ int main(int argc, char *argv[]) { } } - // Require recording folder when using recording only mode. - if (config.recordingOnly && config.recordingFolder.empty()) { - std::cerr << "Record only but recording folder is not set!" << std::endl; - return EXIT_FAILURE; - } + // Set default recording folder if user didn't specify output + if (config.recordingFolder.empty()) config.recordingFolder = "data"; + + // Create timestamp-named subfolders for each recording + if (autoSubfolders) setAutoSubfolder(config.recordingFolder); // In monocular mode, disable depth camera. if (!config.useStereo) depthMode = K4A_DEPTH_MODE_OFF; @@ -171,6 +191,7 @@ int main(int argc, char *argv[]) { std::atomic shouldQuit(false); std::thread inputThread([&]() { + std::cout << "Recording to '" << config.recordingFolder << "'" << std::endl; std::cout << "Press Enter to quit." << std::endl << std::endl; getchar(); shouldQuit = true; diff --git a/cpp/orbbec/CMakeLists.txt b/cpp/orbbec/CMakeLists.txt index 640e28e..8ff7f7d 100644 --- a/cpp/orbbec/CMakeLists.txt +++ b/cpp/orbbec/CMakeLists.txt @@ -2,7 +2,7 @@ if(MSVC) # Windows build uses newer features cmake_minimum_required(VERSION 3.21) else() - cmake_minimum_required(VERSION 3.3) + cmake_minimum_required(VERSION 3.12) endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") @@ -11,9 +11,11 @@ project(spectacularAI_orbbec_tools) if(MSVC) set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") else() - set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") endif() @@ -21,15 +23,6 @@ find_package(Threads REQUIRED) find_package(spectacularAI_orbbecPlugin REQUIRED) find_package(OrbbecSDK REQUIRED PATHS "${OrbbecSDK_DIR}") -if(MSVC) # Must be after project() is called - set(CMAKE_CXX_STANDARD 20) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /Gy") -else() - set(CMAKE_CXX_STANDARD 14) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") - SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--exclude-libs=ALL") -endif() - set(TOOL_LIBS Threads::Threads spectacularAI::orbbecPlugin diff --git a/cpp/orbbec/record.cpp b/cpp/orbbec/record.cpp index 6158021..bde1249 100644 --- a/cpp/orbbec/record.cpp +++ b/cpp/orbbec/record.cpp @@ -1,16 +1,20 @@ #include #include -#include #include #include #include +#include +#include +#include +#include #include #include void showUsage() { std::cout << "Supported arguments:" << std::endl << " -h, --help Help" << std::endl - << " --output , recorded output" << std::endl + << " --output , otherwise recording is saved to current working directory" << std::endl + << " --auto_subfolders, create timestamp-named subfolders for each recording" << std::endl << " --recording_only, disables Vio" << std::endl << " --color_res " << std::endl << " --depth_res " << std::endl @@ -81,6 +85,18 @@ std::pair tryParseResolution(const std::string &s) { } } +void setAutoSubfolder(std::string &recordingFolder) { + auto now = std::chrono::system_clock::now(); + auto timePoint = std::chrono::system_clock::to_time_t(now); + std::tm localTime = *std::localtime(&timePoint); + std::ostringstream oss; + oss << std::put_time(&localTime, "%Y-%m-%d_%H-%M-%S"); + std::filesystem::path basePath = recordingFolder; + std::filesystem::path filename = oss.str(); + std::filesystem::path combinedPath = basePath / filename; + recordingFolder = combinedPath.string(); +} + int main(int argc, char *argv[]) { std::vector arguments(argv, argv + argc); ob::Context::setLoggerSeverity(OB_LOG_SEVERITY_OFF); @@ -102,10 +118,14 @@ int main(int argc, char *argv[]) { int gain = -1; int brightness = -1; bool print = false; + bool autoSubfolders = false; + for (size_t i = 1; i < arguments.size(); ++i) { const std::string &argument = arguments.at(i); if (argument == "--output") config.recordingFolder = arguments.at(++i); + else if (argument == "--auto_subfolders") + autoSubfolders = true; else if (argument == "--recording_only") config.recordingOnly = true; else if (argument == "--color_res") @@ -136,11 +156,11 @@ int main(int argc, char *argv[]) { } } - // Require recording folder when using recording only mode. - if (config.recordingOnly && config.recordingFolder.empty()) { - std::cerr << "Record only but recording folder is not set!" << std::endl; - return EXIT_FAILURE; - } + // Set default recording folder if user didn't specify output + if (config.recordingFolder.empty()) config.recordingFolder = "data"; + + // Create timestamp-named subfolders for each recording + if (autoSubfolders) setAutoSubfolder(config.recordingFolder); // Create vio pipeline using the config & setup orbbec pipeline spectacularAI::orbbecPlugin::Pipeline vioPipeline(*obPipeline, config); @@ -170,6 +190,7 @@ int main(int argc, char *argv[]) { std::atomic shouldQuit(false); std::thread inputThread([&]() { + std::cout << "Recording to '" << config.recordingFolder << "'" << std::endl; std::cout << "Press Enter to quit." << std::endl << std::endl; getchar(); shouldQuit = true; diff --git a/cpp/realsense/CMakeLists.txt b/cpp/realsense/CMakeLists.txt index f3dc7d7..154db52 100644 --- a/cpp/realsense/CMakeLists.txt +++ b/cpp/realsense/CMakeLists.txt @@ -2,16 +2,18 @@ if(MSVC) # Windows build uses newer features cmake_minimum_required(VERSION 3.21) else() - cmake_minimum_required(VERSION 3.3) + cmake_minimum_required(VERSION 3.12) endif() project(spectacularAI_realsense_tools) if(MSVC) set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") else() - set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") endif() diff --git a/cpp/realsense/record.cpp b/cpp/realsense/record.cpp index 7b577b5..8cb857f 100644 --- a/cpp/realsense/record.cpp +++ b/cpp/realsense/record.cpp @@ -2,13 +2,18 @@ #include #include #include +#include +#include +#include +#include #include #include void showUsage() { std::cout << "Supported arguments:" << std::endl << " -h, --help Help" << std::endl - << " --output , recorded output" << std::endl + << " --output , otherwise recording is saved to current working directory" << std::endl + << " --auto_subfolders, create timestamp-named subfolders for each recording" << std::endl << " --recording_only, disables Vio" << std::endl << " --resolution , 400p or 800p" << std::endl << " --brightness " << std::endl @@ -24,6 +29,18 @@ void showUsage() { << std::endl; } +void setAutoSubfolder(std::string &recordingFolder) { + auto now = std::chrono::system_clock::now(); + auto timePoint = std::chrono::system_clock::to_time_t(now); + std::tm localTime = *std::localtime(&timePoint); + std::ostringstream oss; + oss << std::put_time(&localTime, "%Y-%m-%d_%H-%M-%S"); + std::filesystem::path basePath = recordingFolder; + std::filesystem::path filename = oss.str(); + std::filesystem::path combinedPath = basePath / filename; + recordingFolder = combinedPath.string(); +} + struct ColorCameraConfig { int brightness = -1; int contrast = -1; @@ -40,12 +57,15 @@ int main(int argc, char** argv) { spectacularAI::rsPlugin::Configuration config; ColorCameraConfig colorConfig; bool print = false; + bool autoSubfolders = false; std::vector arguments(argv, argv + argc); for (size_t i = 1; i < arguments.size(); ++i) { const std::string &argument = arguments.at(i); if (argument == "--output") config.recordingFolder = arguments.at(++i); + else if (argument == "--auto_subfolders") + autoSubfolders = true; else if (argument == "--recording_only") config.recordingOnly = true; else if (argument == "--resolution") @@ -80,10 +100,11 @@ int main(int argc, char** argv) { } } - if (config.recordingFolder.empty()) { - std::cerr << " You must provide output folder with --output argument." << std::endl;; - return EXIT_FAILURE; - } + // Set default recording folder if user didn't specify output + if (config.recordingFolder.empty()) config.recordingFolder = "data"; + + // Create timestamp-named subfolders for each recording + if (autoSubfolders) setAutoSubfolder(config.recordingFolder); spectacularAI::rsPlugin::Pipeline vioPipeline(config); @@ -123,6 +144,7 @@ int main(int argc, char** argv) { std::atomic shouldQuit(false); std::thread inputThread([&]() { + std::cout << "Recording to '" << config.recordingFolder << "'" << std::endl; std::cout << "Press Enter to quit." << std::endl << std::endl; getchar(); shouldQuit = true; diff --git a/python/cli/record/oak.py b/python/cli/record/oak.py index b07a643..08feb99 100644 --- a/python/cli/record/oak.py +++ b/python/cli/record/oak.py @@ -90,7 +90,7 @@ def record(args): outputFolder = args.output if args.auto_subfolders: import datetime - autoFolderName = datetime.datetime.now().strftime("%Y%m%dT%H%M%S") + autoFolderName = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S") outputFolder = os.path.join(outputFolder, autoFolderName) internalParameters = {} @@ -189,7 +189,7 @@ def open_gray_video(name): videoFile = open(outputFolder + "/rgb_video.h265", "wb") rgbQueue = device.getOutputQueue(name="h265-rgb", maxSize=30, blocking=False) - print("Recording!") + print("Recording to '{0}'".format(config.recordingFolder)) print("") if plotter is not None: print("Close the visualization window to stop recording") From 329e1752ad7b7fae60536c1fdabed35fc1783e1e Mon Sep 17 00:00:00 2001 From: kaatrasa Date: Mon, 22 Jan 2024 10:24:00 +0200 Subject: [PATCH 2/2] Create timestamp based subfolders automatically if output is not set --- cpp/k4a/record.cpp | 5 ++++- cpp/orbbec/record.cpp | 5 ++++- cpp/realsense/record.cpp | 5 ++++- python/cli/record/oak.py | 22 ++++++++++++++-------- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/cpp/k4a/record.cpp b/cpp/k4a/record.cpp index 799af06..6735791 100644 --- a/cpp/k4a/record.cpp +++ b/cpp/k4a/record.cpp @@ -99,7 +99,10 @@ int main(int argc, char *argv[]) { } // Set default recording folder if user didn't specify output - if (config.recordingFolder.empty()) config.recordingFolder = "data"; + if (config.recordingFolder.empty()) { + autoSubfolders = true; + config.recordingFolder = "data"; + } // Create timestamp-named subfolders for each recording if (autoSubfolders) setAutoSubfolder(config.recordingFolder); diff --git a/cpp/orbbec/record.cpp b/cpp/orbbec/record.cpp index bde1249..211c496 100644 --- a/cpp/orbbec/record.cpp +++ b/cpp/orbbec/record.cpp @@ -157,7 +157,10 @@ int main(int argc, char *argv[]) { } // Set default recording folder if user didn't specify output - if (config.recordingFolder.empty()) config.recordingFolder = "data"; + if (config.recordingFolder.empty()) { + autoSubfolders = true; + config.recordingFolder = "data"; + } // Create timestamp-named subfolders for each recording if (autoSubfolders) setAutoSubfolder(config.recordingFolder); diff --git a/cpp/realsense/record.cpp b/cpp/realsense/record.cpp index 8cb857f..f162937 100644 --- a/cpp/realsense/record.cpp +++ b/cpp/realsense/record.cpp @@ -101,7 +101,10 @@ int main(int argc, char** argv) { } // Set default recording folder if user didn't specify output - if (config.recordingFolder.empty()) config.recordingFolder = "data"; + if (config.recordingFolder.empty()) { + autoSubfolders = true; + config.recordingFolder = "data"; + } // Create timestamp-named subfolders for each recording if (autoSubfolders) setAutoSubfolder(config.recordingFolder); diff --git a/python/cli/record/oak.py b/python/cli/record/oak.py index 08feb99..d75eaae 100644 --- a/python/cli/record/oak.py +++ b/python/cli/record/oak.py @@ -31,9 +31,8 @@ # script and as a subcommand in sai-cli. def define_args(p): - p.add_argument("--output", help="Recording output folder", default="data") - p.add_argument('--auto_subfolders', action='store_true', - help='Create timestamp-named subfolders for each recording') + p.add_argument("--output", help="Recording output folder, otherwise recording is saved to current working directory") + p.add_argument('--auto_subfolders', action='store_true', help='Create timestamp-named subfolders for each recording') p.add_argument("--use_rgb", help="Use RGB data for tracking (OAK-D S2)", action="store_true") p.add_argument("--mono", help="Use a single camera (not stereo)", action="store_true") p.add_argument("--no_rgb", help="Disable recording RGB video feed", action="store_true") @@ -73,6 +72,13 @@ def define_subparser(subparsers): sub.set_defaults(func=record) return define_args(sub) +def auto_subfolder(outputFolder): + import datetime + import os + autoFolderName = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + outputFolder = os.path.join(outputFolder, autoFolderName) + return outputFolder + def record(args): import depthai import spectacularAI @@ -87,11 +93,11 @@ def record(args): config.useSlam = True config.inputResolution = args.resolution - outputFolder = args.output - if args.auto_subfolders: - import datetime - autoFolderName = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S") - outputFolder = os.path.join(outputFolder, autoFolderName) + if args.output: + outputFolder = args.output + if args.auto_subfolders: outputFolder = auto_subfolder(outputFolder) + else: + outputFolder = auto_subfolder("data") internalParameters = {}