diff --git a/constants/camera_constants.json b/constants/camera_constants.json deleted file mode 100644 index 8a8db67c..00000000 --- a/constants/camera_constants.json +++ /dev/null @@ -1,124 +0,0 @@ -{ - "_comments": [ - "exposure: Nullopt = auto exposure", - "DUMMY_CAMERA: For tests such as solver_test.cc" - ], - "cameras": [ - { - "pipeline": "nvarguscamerasrc sensor-id=0 aelock=true exposuretimerange=\"100000 200000\" gainrange=\"1 15\" ispdigitalgainrange=\"1 1\" ! video/x-raw(memory:NVMM), width=1456, height=1088, framerate=30/1, format=NV12 ! nvvidconv ! video/x-raw, format=BGRx ! queue ! appsink", - "intrinsics_path": "/bos/constants/imx296_camera0_intrinsics.json", - "extrinsics_path": "/bos/constants/imx296_camera0_extrinsics.json", - "name": "mipi0" - }, - { - "pipeline": "nvarguscamerasrc sensor-id=1 aelock=true exposuretimerange=\"100000 200000\" gainrange=\"1 15\" ispdigitalgainrange=\"1 1\" ! video/x-raw(memory:NVMM), width=1456, height=1088, framerate=30/1, format=NV12 ! nvvidconv ! video/x-raw, format=BGRx ! queue ! appsink", - "intrinsics_path": "/bos/constants/imx296_camera1_intrinsics.json", - "extrinsics_path": "/bos/constants/imx296_camera1_extrinsics.json", - "name": "mipi1" - }, - { - "pipeline": "/dev/v4l/by-path/platform-3610000.usb-usb-0:2.1:1.0-video-index0", - "intrinsics_path": "/bos/constants/misc/dev_orin_intrinsics.json", - "extrinsics_path": "/bos/constants/misc/dev_orin_extrinsics.json", - "name": "dev_orin" - }, - { - "pipeline": "", - "intrinsics_path": "/bos/constants/misc/dummy_camera_intrinsics.json", - "extrinsics_path": "/bos/constants/misc/dummy_camera_extrinsics.json", - "name": "default_usb0" - }, - { - "pipeline": "/dev/v4l/by-path/platform-3610000.usb-usb-0:2.1:1.0-video-index0", - "intrinsics_path": "/bos/constants/fiddler/usb_camera0_intrinsics.json", - "extrinsics_path": "/bos/constants/fiddler/usb_camera0_extrinsics.json", - "name": "fiddler_usb0" - }, - { - "pipeline": "/dev/v4l/by-path/platform-3610000.usb-usb-0:2.4:1.0-video-index0", - "intrinsics_path": "/bos/constants/fiddler/usb_camera1_intrinsics.json", - "extrinsics_path": "/bos/constants/fiddler/usb_camera1_extrinsics.json", - "name": "fiddler_usb1" - }, - { - "pipeline": "/dev/v4l/by-path/platform-3610000.usb-usb-0:1.3:1.0-video-index0", - "intrinsics_path": "/bos/constants/turret_bot/front_right_intrinsics.json", - "extrinsics_path": "/bos/constants/turret_bot/front_right_extrinsics.json", - "name": "turret_bot_front_right", - "backlight": 0.0 - }, - { - "pipeline": "/dev/v4l/by-path/platform-3610000.usb-usb-0:1.4:1.0-video-index0", - "intrinsics_path": "/bos/constants/turret_bot/front_left_intrinsics.json", - "extrinsics_path": "/bos/constants/turret_bot/front_left_extrinsics.json", - "name": "turret_bot_front_left", - "backlight": 0.0 - }, - { - "pipeline": "nvarguscamerasrc sensor-id=0 ! video/x-raw(memory:NVMM), width=1456, height=1088, framerate=30/1, format=NV12 ! nvvidconv ! video/x-raw, format=BGRx ! queue ! appsink", - "intrinsics_path": "/bos/constants/main_bot/front_intrinsics.json", - "extrinsics_path": "/bos/constants/main_bot/front_extrinsics.json", - "name": "main_bot_front", - "backlight": null, - "frame_width": null, - "frame_height": null, - "fps": null, - "exposure": null - }, - { - "pipeline": "/dev/v4l/by-path/platform-3610000.usb-usb-0:2.3:1.0-video-index0", - "intrinsics_path": "/bos/constants/main_bot/left_intrinsics.json", - "extrinsics_path": "/bos/constants/main_bot/left_extrinsics.json", - "name": "main_bot_left", - "backlight": null, - "frame_width": 1280, - "frame_height": 800, - "fps": 60.0, - "exposure": null - }, - { - "pipeline": "/dev/v4l/by-path/platform-3610000.usb-usb-0:2.1:1.0-video-index0", - "intrinsics_path": "/bos/constants/main_bot/right_intrinsics.json", - "extrinsics_path": "/bos/constants/main_bot/right_extrinsics.json", - "name": "main_bot_right", - "backlight": null, - "frame_width": 1280, - "frame_height": 800, - "fps": 60.0, - "exposure": null - }, - { - "pipeline": "nvarguscamerasrc sensor-id=0 aelock=true exposuretimerange=\"100000 200000\" gainrange=\"1 15\" ispdigitalgainrange=\"1 1\" ! video/x-raw(memory:NVMM), width=1456, height=1088, framerate=30/1, format=NV12 ! nvvidconv ! video/x-raw, format=BGRx ! queue ! appsink", - "intrinsics_path": "/bos/constants/second_bot/front_intrinsics.json", - "extrinsics_path": "/bos/constants/second_bot/front_extrinsics.json", - "name": "second_bot_front", - "backlight": null, - "frame_width": null, - "frame_height": null, - "fps": null, - "exposure": null - }, - { - "pipeline": "/dev/v4l/by-path/platform-3610000.usb-usb-0:2.3:1.0-video-index0", - "intrinsics_path": "/bos/constants/second_bot/right_intrinsics.json", - "extrinsics_path": "/bos/constants/second_bot/right_extrinsics.json", - "name": "second_bot_right", - "backlight": null, - "frame_width": 1280, - "frame_height": 800, - "fps": 60.0, - "exposure": null - }, - { - "pipeline": "/dev/v4l/by-path/platform-3610000.usb-usb-0:2.1:1.0-video-index0", - "intrinsics_path": "/bos/constants/second_bot/left_intrinsics.json", - "extrinsics_path": "/bos/constants/second_bot/left_extrinsics.json", - "name": "second_bot_left", - "backlight": null, - "frame_width": 1280, - "frame_height": 800, - "fps": 60.0, - "exposure": null - } - ] -} diff --git a/constants/fiddler/camera_constants.json b/constants/fiddler/camera_constants.json new file mode 100644 index 00000000..5d7a3992 --- /dev/null +++ b/constants/fiddler/camera_constants.json @@ -0,0 +1,23 @@ +{ + "_comments": [ + "Robot-specific camera configs for fiddler" + ], + "cameras": [ + { + "camera_backend": "uvc", + "detector_backend": "cpu", + "pipeline": "/dev/v4l/by-path/platform-3610000.usb-usb-0:2.1:1.0-video-index0", + "intrinsics_path": "/bos/constants/fiddler/usb_camera0_intrinsics.json", + "extrinsics_path": "/bos/constants/fiddler/usb_camera0_extrinsics.json", + "name": "fiddler_usb0" + }, + { + "camera_backend": "uvc", + "detector_backend": "cpu", + "pipeline": "/dev/v4l/by-path/platform-3610000.usb-usb-0:2.4:1.0-video-index0", + "intrinsics_path": "/bos/constants/fiddler/usb_camera1_intrinsics.json", + "extrinsics_path": "/bos/constants/fiddler/usb_camera1_extrinsics.json", + "name": "fiddler_usb1" + } + ] +} diff --git a/constants/main_bot/camera_constants.json b/constants/main_bot/camera_constants.json new file mode 100644 index 00000000..760b5fb7 --- /dev/null +++ b/constants/main_bot/camera_constants.json @@ -0,0 +1,46 @@ +{ + "_comments": [ + "Robot-specific camera configs for main_bot" + ], + "cameras": [ + { + "camera_backend": "opencv", + "detector_backend": "cpu", + "pipeline": "nvarguscamerasrc sensor-id=0 ! video/x-raw(memory:NVMM), width=1456, height=1088, framerate=30/1, format=NV12 ! nvvidconv ! video/x-raw, format=BGRx ! queue ! appsink", + "intrinsics_path": "/bos/constants/main_bot/front_intrinsics.json", + "extrinsics_path": "/bos/constants/main_bot/front_extrinsics.json", + "name": "front", + "backlight": null, + "frame_width": null, + "frame_height": null, + "fps": null, + "exposure": null + }, + { + "camera_backend": "uvc", + "detector_backend": "cpu", + "pipeline": "/dev/v4l/by-path/platform-3610000.usb-usb-0:2.3:1.0-video-index0", + "intrinsics_path": "/bos/constants/main_bot/left_intrinsics.json", + "extrinsics_path": "/bos/constants/main_bot/left_extrinsics.json", + "name": "left", + "backlight": null, + "frame_width": 1280, + "frame_height": 800, + "fps": 60.0, + "exposure": null + }, + { + "camera_backend": "uvc", + "detector_backend": "cpu", + "pipeline": "/dev/v4l/by-path/platform-3610000.usb-usb-0:2.1:1.0-video-index0", + "intrinsics_path": "/bos/constants/main_bot/right_intrinsics.json", + "extrinsics_path": "/bos/constants/main_bot/right_extrinsics.json", + "name": "right", + "backlight": null, + "frame_width": 1280, + "frame_height": 800, + "fps": 60.0, + "exposure": null + } + ] +} diff --git a/constants/second_bot/camera_constants.json b/constants/second_bot/camera_constants.json new file mode 100644 index 00000000..8aa94e1e --- /dev/null +++ b/constants/second_bot/camera_constants.json @@ -0,0 +1,46 @@ +{ + "_comments": [ + "Robot-specific camera configs for second_bot" + ], + "cameras": [ + { + "camera_backend": "opencv", + "detector_backend": "cpu", + "pipeline": "nvarguscamerasrc sensor-id=0 aelock=true exposuretimerange=\"100000 200000\" gainrange=\"1 15\" ispdigitalgainrange=\"1 1\" ! video/x-raw(memory:NVMM), width=1456, height=1088, framerate=30/1, format=NV12 ! nvvidconv ! video/x-raw, format=BGRx ! queue ! appsink", + "intrinsics_path": "/bos/constants/second_bot/front_intrinsics.json", + "extrinsics_path": "/bos/constants/second_bot/front_extrinsics.json", + "name": "front", + "backlight": null, + "frame_width": null, + "frame_height": null, + "fps": null, + "exposure": null + }, + { + "camera_backend": "uvc", + "detector_backend": "gpu", + "pipeline": "/dev/v4l/by-path/platform-3610000.usb-usb-0:2.3:1.0-video-index0", + "intrinsics_path": "/bos/constants/second_bot/right_intrinsics.json", + "extrinsics_path": "/bos/constants/second_bot/right_extrinsics.json", + "name": "right", + "backlight": null, + "frame_width": 1280, + "frame_height": 800, + "fps": 60.0, + "exposure": null + }, + { + "camera_backend": "uvc", + "detector_backend": "gpu", + "pipeline": "/dev/v4l/by-path/platform-3610000.usb-usb-0:2.1:1.0-video-index0", + "intrinsics_path": "/bos/constants/second_bot/left_intrinsics.json", + "extrinsics_path": "/bos/constants/second_bot/left_extrinsics.json", + "name": "left", + "backlight": null, + "frame_width": 1280, + "frame_height": 800, + "fps": 60.0, + "exposure": null + } + ] +} diff --git a/constants/shared_camera_constants.json b/constants/shared_camera_constants.json new file mode 100644 index 00000000..5e921eca --- /dev/null +++ b/constants/shared_camera_constants.json @@ -0,0 +1,32 @@ +{ + "_comments": [ + "exposure: Nullopt = auto exposure", + "Shared camera configs that are not robot-specific" + ], + "cameras": [ + { + "pipeline": "nvarguscamerasrc sensor-id=0 aelock=true exposuretimerange=\"100000 200000\" gainrange=\"1 15\" ispdigitalgainrange=\"1 1\" ! video/x-raw(memory:NVMM), width=1456, height=1088, framerate=30/1, format=NV12 ! nvvidconv ! video/x-raw, format=BGRx ! queue ! appsink", + "intrinsics_path": "/bos/constants/imx296_camera0_intrinsics.json", + "extrinsics_path": "/bos/constants/imx296_camera0_extrinsics.json", + "name": "mipi0" + }, + { + "pipeline": "nvarguscamerasrc sensor-id=1 aelock=true exposuretimerange=\"100000 200000\" gainrange=\"1 15\" ispdigitalgainrange=\"1 1\" ! video/x-raw(memory:NVMM), width=1456, height=1088, framerate=30/1, format=NV12 ! nvvidconv ! video/x-raw, format=BGRx ! queue ! appsink", + "intrinsics_path": "/bos/constants/imx296_camera1_intrinsics.json", + "extrinsics_path": "/bos/constants/imx296_camera1_extrinsics.json", + "name": "mipi1" + }, + { + "pipeline": "/dev/v4l/by-path/platform-3610000.usb-usb-0:2.1:1.0-video-index0", + "intrinsics_path": "/bos/constants/misc/dev_orin_intrinsics.json", + "extrinsics_path": "/bos/constants/misc/dev_orin_extrinsics.json", + "name": "dev_orin" + }, + { + "pipeline": "", + "intrinsics_path": "/bos/constants/misc/dummy_camera_intrinsics.json", + "extrinsics_path": "/bos/constants/misc/dummy_camera_extrinsics.json", + "name": "default_usb0" + } + ] +} diff --git a/constants/turret_bot/camera_constants.json b/constants/turret_bot/camera_constants.json new file mode 100644 index 00000000..92046908 --- /dev/null +++ b/constants/turret_bot/camera_constants.json @@ -0,0 +1,25 @@ +{ + "_comments": [ + "Robot-specific camera configs for turret_bot" + ], + "cameras": [ + { + "camera_backend": "uvc", + "detector_backend": "cpu", + "pipeline": "/dev/v4l/by-path/platform-3610000.usb-usb-0:1.3:1.0-video-index0", + "intrinsics_path": "/bos/constants/turret_bot/front_right_intrinsics.json", + "extrinsics_path": "/bos/constants/turret_bot/front_right_extrinsics.json", + "name": "front_right", + "backlight": 0.0 + }, + { + "camera_backend": "uvc", + "detector_backend": "cpu", + "pipeline": "/dev/v4l/by-path/platform-3610000.usb-usb-0:1.4:1.0-video-index0", + "intrinsics_path": "/bos/constants/turret_bot/front_left_intrinsics.json", + "extrinsics_path": "/bos/constants/turret_bot/front_left_extrinsics.json", + "name": "front_left", + "backlight": 0.0 + } + ] +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1f016da1..20327b41 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,16 +21,10 @@ add_subdirectory(yolo) add_subdirectory(test) add_subdirectory(pathing) -add_executable(main_bot_main main_bot_main.cc) -target_link_libraries(main_bot_main PRIVATE camera localization utils) +add_executable(robot_main robot_main.cc) +target_link_libraries(robot_main PRIVATE camera localization utils) -add_executable(second_bot_main second_bot_main.cc) -target_link_libraries(second_bot_main PRIVATE camera localization utils) - -add_executable(unambiguous_second unambiguous_second.cc) -target_link_libraries(unambiguous_second PRIVATE ${OpenCV_LIBS} apriltag ntcore camera nlohmann_json::nlohmann_json Eigen3::Eigen localization utils) - -add_executable(unambiguous_first unambiguous_first.cc) -target_link_libraries(unambiguous_first PRIVATE ${OpenCV_LIBS} apriltag ntcore camera nlohmann_json::nlohmann_json Eigen3::Eigen localization utils) +add_executable(unambiguous_main unambiguous_main.cc) +target_link_libraries(unambiguous_main PRIVATE ${OpenCV_LIBS} apriltag ntcore camera nlohmann_json::nlohmann_json Eigen3::Eigen localization utils) set(CMAKE_CXX_CLANG_TIDY "") diff --git a/src/camera/camera_constants.cc b/src/camera/camera_constants.cc index 0c9c1786..d94fa2ad 100644 --- a/src/camera/camera_constants.cc +++ b/src/camera/camera_constants.cc @@ -3,11 +3,39 @@ #include "absl/flags/flag.h" ABSL_FLAG(std::string, camera_constants_path, // NOLINT - "/bos/constants/camera_constants.json", // NOTLINT - "Path to the json file of camera constants"); //NOLINT + "/bos/constants/shared_camera_constants.json", // NOLINT + "Path to the json file of camera constants"); // NOLINT namespace camera { +namespace { + +auto ParseCameraBackend(std::string_view backend) -> CameraBackend { + if (backend == "uvc") { + return CameraBackend::kUvc; + } + if (backend == "opencv") { + return CameraBackend::kOpenCv; + } + LOG(WARNING) << "Unknown camera backend: " << backend + << ", defaulting to opencv"; + return CameraBackend::kOpenCv; +} + +auto ParseDetectorBackend(std::string_view backend) -> DetectorBackend { + if (backend == "gpu") { + return DetectorBackend::kGpu; + } + if (backend == "cpu") { + return DetectorBackend::kCpu; + } + LOG(WARNING) << "Unknown detector backend: " << backend + << ", defaulting to cpu"; + return DetectorBackend::kCpu; +} + +} // namespace + auto GetCameraConstants() -> camera_constants_t { return GetCameraConstants(absl::GetFlag(FLAGS_camera_constants_path)); } @@ -38,6 +66,17 @@ auto GetCameraConstants(const std::string& path) -> camera_constants_t { camera_constant_t camera_constant{ .name = camera_config.value("name", std::string{})}; + if (camera_config.contains("camera_backend") && + !camera_config["camera_backend"].is_null()) { + camera_constant.camera_backend = + ParseCameraBackend(camera_config["camera_backend"].get()); + } + if (camera_config.contains("detector_backend") && + !camera_config["detector_backend"].is_null()) { + camera_constant.detector_backend = + ParseDetectorBackend( + camera_config["detector_backend"].get()); + } if (camera_config.contains("pipeline") && !camera_config["pipeline"].is_null()) { camera_constant.pipeline = camera_config["pipeline"]; diff --git a/src/camera/camera_constants.h b/src/camera/camera_constants.h index 19cfb35f..415d7a91 100644 --- a/src/camera/camera_constants.h +++ b/src/camera/camera_constants.h @@ -5,11 +5,16 @@ #include "src/utils/pch.h" namespace camera { +enum class CameraBackend { kOpenCv, kUvc }; +enum class DetectorBackend { kCpu, kGpu }; + using camera_constant_t = struct CameraConstant { std::string name; std::optional pipeline = std::nullopt; std::optional intrinsics_path = std::nullopt; std::optional extrinsics_path = std::nullopt; + CameraBackend camera_backend = CameraBackend::kOpenCv; + DetectorBackend detector_backend = DetectorBackend::kCpu; std::optional backlight = std::nullopt; std::optional frame_width = std::nullopt; std::optional frame_height = std::nullopt; @@ -25,6 +30,11 @@ using camera_constant_t = struct CameraConstant { << "\textrinsics_path: " << c.extrinsics_path.value_or("NO EXTRINSICS") << "\tname: " << c.name << '\n'; + os << '\t' << "Camera Backend: " + << (c.camera_backend == CameraBackend::kUvc ? "uvc" : "opencv"); + os << '\t' << "Detector Backend: " + << (c.detector_backend == DetectorBackend::kGpu ? "gpu" : "cpu"); + const auto print = [&](std::string_view label, const auto& opt) { if (opt) os << '\t' << label << ": " << *opt; diff --git a/src/camera/cv_camera.cc b/src/camera/cv_camera.cc index c68374f2..82f579bf 100644 --- a/src/camera/cv_camera.cc +++ b/src/camera/cv_camera.cc @@ -19,16 +19,22 @@ auto FileSystemAlmostFull() { CVCamera::CVCamera(const CameraConstant& c, std::optional log_path) : camera_constant_(c), - cap_(cv::VideoCapture(c.pipeline.value())), - pipeline_(c.pipeline.value()), + cap_(cv::VideoCapture()), + pipeline_(c.pipeline.value_or("")), log_path_(std::move(log_path)) { + PCHECK(c.pipeline.has_value()) << "Pipeline needs value"; + const auto open_capture = [&]() { + if (c.camera_backend == CameraBackend::kUvc) { + return cv::VideoCapture(pipeline_, cv::CAP_V4L2); + } + return cv::VideoCapture(pipeline_); + }; cap_.release(); - cap_ = cv::VideoCapture(pipeline_); + cap_ = open_capture(); if (FileSystemAlmostFull()) { log_path_ = std::nullopt; LOG(WARNING) << "Filesystem almost full! Not logging any frames"; } - PCHECK(c.pipeline.has_value()) << "Pipeline needs value"; LOG(INFO) << c.pipeline.value(); @@ -92,11 +98,15 @@ auto CVCamera::Restart() -> void { cap_.release(); std::this_thread::sleep_for(std::chrono::seconds(3)); LOG(INFO) << "Restarting camera with pipeline: " << pipeline_; - cap_ = cv::VideoCapture(pipeline_); + if (camera_constant_.camera_backend == CameraBackend::kUvc) { + cap_ = cv::VideoCapture(pipeline_, cv::CAP_V4L2); + } else { + cap_ = cv::VideoCapture(pipeline_); + } std::this_thread::sleep_for(std::chrono::seconds(3)); } auto CVCamera::GetCameraConstant() const -> camera_constant_t { return camera_constant_; } -} // namespace camera \ No newline at end of file +} // namespace camera diff --git a/src/camera/select_camera.cc b/src/camera/select_camera.cc index f5facbb7..fa85655d 100644 --- a/src/camera/select_camera.cc +++ b/src/camera/select_camera.cc @@ -18,6 +18,32 @@ using camera::camera_constants_t; namespace camera { +namespace { + +auto ResolveCameraChoice(const std::string& choice, + const camera_constants_t& camera_constants) + -> std::optional { + if (camera_constants.contains(choice)) { + return choice; + } + + constexpr std::string_view kPrefixes[] = {"main_bot_", "second_bot_", + "turret_bot_"}; + for (std::string_view prefix : kPrefixes) { + if (choice.rfind(prefix, 0) != 0) { + continue; + } + const std::string stripped = choice.substr(prefix.size()); + if (camera_constants.contains(stripped)) { + return stripped; + } + } + + return std::nullopt; +} + +} // namespace + auto SelectCameraConfig(const camera_constants_t& camera_constants) -> std::unique_ptr { LOG(INFO) << "Available cameras: "; @@ -33,20 +59,27 @@ auto SelectCameraConfig(const camera_constants_t& camera_constants) auto SelectCameraConfig(const std::string& choice, const camera_constants_t& camera_constants) -> std::unique_ptr { + const std::optional resolved_choice = + ResolveCameraChoice(choice, camera_constants); if (absl::GetFlag(FLAGS_folder_path).has_value()) { if (std::filesystem::is_directory( absl::GetFlag(FLAGS_folder_path).value())) { - return std::make_unique( - absl::GetFlag(FLAGS_folder_path).value(), camera::GetCameraConstants()[choice]); + if (!resolved_choice.has_value()) { + LOG(WARNING) << "You entered in an invalid camera"; + } else { + return std::make_unique( + absl::GetFlag(FLAGS_folder_path).value(), + camera_constants.at(resolved_choice.value())); + } } else { LOG(WARNING) << "You entered in an invalid camera"; } } - if (camera_constants.contains(choice)) { + if (resolved_choice.has_value()) { return std::make_unique( - camera::GetCameraConstants()[choice]); + camera_constants.at(resolved_choice.value())); } else { return SelectCameraConfig(camera_constants); } diff --git a/src/localization/unambiguous_estimator.cc b/src/localization/unambiguous_estimator.cc index c203659a..cbfcba39 100644 --- a/src/localization/unambiguous_estimator.cc +++ b/src/localization/unambiguous_estimator.cc @@ -30,14 +30,13 @@ namespace localization { bool UnambiguousEstimator::log_interesting_timestamp_ = false; UnambiguousEstimator::UnambiguousEstimator( - std::vector>& cameras, + const std::vector& cameras, std::optional port_start, bool verbose, std::optional> img_dir_paths) : port_start_(port_start), prev_timestamps_(cameras.size()), sim_(img_dir_paths.has_value()) { std::string log_path = frc::DataLogManager::GetLogDir(); - auto camera_constants = camera::GetCameraConstants(); detectors_.reserve(cameras.size()); solvers_.reserve(cameras.size()); if (port_start.has_value()) { @@ -48,12 +47,12 @@ UnambiguousEstimator::UnambiguousEstimator( for (size_t i = 0; i < cameras.size(); i++) { if (sim_) { icameras.push_back(std::make_unique( - img_dir_paths.value()[i], cameras[i].first, 10, + img_dir_paths.value()[i], cameras[i], 10, interesting_timestamp_start_ - 1, interesting_timestamp_end_)); } else { const std::string camera_log_dest = - fmt::format("{}/{}", log_path, cameras[i].first.name); - icameras.push_back(std::make_unique(cameras[i].first, + fmt::format("{}/{}", log_path, cameras[i].name); + icameras.push_back(std::make_unique(cameras[i], camera_log_dest)); std::cout << "Logging to destination: " << camera_log_dest << std::endl; } @@ -64,24 +63,20 @@ UnambiguousEstimator::UnambiguousEstimator( std::cout << "Initializing estimators and streamers" << std::endl; std::vector first_frames = sources_->GetCVFrames(); for (size_t i = 0; i < cameras.size(); i++) { - switch (cameras[i].second) { - case OPENCV_CPU: + const auto intrinsics = utils::ReadIntrinsics(cameras[i].intrinsics_path.value()); + switch (cameras[i].detector_backend) { + case camera::DetectorBackend::kCpu: detectors_.push_back(std::make_unique( - first_frames[i].cols, first_frames[i].rows, - utils::ReadIntrinsics(cameras[i].first.intrinsics_path.value()))); + first_frames[i].cols, first_frames[i].rows, intrinsics)); break; - case AUSTIN_GPU: + case camera::DetectorBackend::kGpu: detectors_.push_back(std::make_unique( - first_frames[i].cols, first_frames[i].rows, - utils::ReadIntrinsics(cameras[i].first.intrinsics_path.value()))); + first_frames[i].cols, first_frames[i].rows, intrinsics)); break; - default: - LOG(FATAL) << "Invalid solver type"; - return; } - solvers_.emplace_back(cameras[i].first); + solvers_.emplace_back(cameras[i]); if (port_start.has_value()) { - streamers_.emplace_back(cameras[i].first.name, port_start.value() + i, 30, + streamers_.emplace_back(cameras[i].name, port_start.value() + i, 30, 1080, 1080); } } diff --git a/src/localization/unambiguous_estimator.h b/src/localization/unambiguous_estimator.h index 0bb51716..04306138 100644 --- a/src/localization/unambiguous_estimator.h +++ b/src/localization/unambiguous_estimator.h @@ -13,7 +13,6 @@ #include "src/utils/pch.h" namespace localization { -enum Detector { OPENCV_CPU, AUSTIN_GPU }; using latent_estimate_t = struct LatentEstimate { position_estimate_t pose_estimate; double latency; @@ -25,7 +24,7 @@ using latent_estimate_t = struct LatentEstimate { class UnambiguousEstimator { public: UnambiguousEstimator( - std::vector>& cameras, + const std::vector& cameras, std::optional port_start = std::nullopt, bool verbose = false, std::optional> img_dir_paths = std::nullopt); diff --git a/src/main_bot_main.cc b/src/main_bot_main.cc deleted file mode 100644 index 332846a0..00000000 --- a/src/main_bot_main.cc +++ /dev/null @@ -1,100 +0,0 @@ -#include "src/camera/camera_constants.h" -#include "src/camera/camera_source.h" -#include "src/camera/cv_camera.h" -#include "src/localization/multi_tag_solver.h" -#include "src/localization/networktable_sender.h" -#include "src/localization/nvidia_apriltag_detector.h" -#include "src/localization/opencv_apriltag_detector.h" -#include "src/localization/run_localization.h" -#include "src/localization/square_solver.h" -#include "src/utils/camera_utils.h" -#include "src/utils/nt_utils.h" - -using camera::camera_constants_t; -auto main() -> int { - utils::StartNetworktables(9971); - // TODO configure vision bot camera paths - - std::string log_path = frc::DataLogManager::GetLogDir(); - camera_constants_t camera_constants = camera::GetCameraConstants(); - - LOG(INFO) << "Starting estimators"; - - std::thread front_thread([&]() { - auto front_camera = std::make_unique( - "Front", std::make_unique( - camera_constants.at("main_bot_front"))); - cv::Mat front_camera_frame = front_camera->GetFrame(); - - std::vector> front_sender; - front_sender.emplace_back( - std::make_unique( - camera_constants.at("main_bot_front").name)); - - localization::RunLocalization( - std::move(front_camera), - std::make_unique( - front_camera_frame.cols, front_camera_frame.rows, - utils::ReadIntrinsics( - camera_constants.at("main_bot_front").intrinsics_path.value())), - std::make_unique( - camera_constants.at("main_bot_front")), - std::move(front_sender), - camera_constants.at("main_bot_front").extrinsics_path.value(), 5801, - false); - }); - - std::thread left_thread([&]() { - auto left_camera = std::make_unique( - "Left", - std::make_unique(camera_constants.at("main_bot_left"), - fmt::format("{}/left", log_path))); - cv::Mat left_camera_frame = left_camera->GetFrame(); - - std::vector> left_sender; - left_sender.emplace_back(std::make_unique( - camera_constants.at("main_bot_left").name)); - - localization::RunLocalization( - std::move(left_camera), - std::make_unique( - left_camera_frame.cols, left_camera_frame.rows, - utils::ReadIntrinsics( - camera_constants.at("main_bot_left").intrinsics_path.value())), - std::make_unique( - camera_constants.at("main_bot_left")), - std::move(left_sender), - camera_constants.at("main_bot_left").extrinsics_path.value(), 5802, - false); - }); - - std::thread right_thread([&]() { - auto right_camera = std::make_unique( - "Right", std::make_unique( - camera_constants.at("main_bot_right"), - fmt::format("{}/right", log_path))); - cv::Mat right_camera_frame = right_camera->GetFrame(); - - std::vector> right_sender; - right_sender.emplace_back( - std::make_unique( - camera_constants.at("main_bot_right").name)); - - localization::RunLocalization( - std::move(right_camera), - std::make_unique( - right_camera_frame.cols, right_camera_frame.rows, - utils::ReadIntrinsics( - camera_constants.at("main_bot_right").intrinsics_path.value())), - std::make_unique( - camera_constants.at("main_bot_right")), - std::move(right_sender), - camera_constants.at("main_bot_right").extrinsics_path.value(), 5803, - false); - }); - - LOG(INFO) << "Started estimators"; - - // TODO find better way - left_thread.join(); -} diff --git a/src/robot_main.cc b/src/robot_main.cc new file mode 100644 index 00000000..6203068e --- /dev/null +++ b/src/robot_main.cc @@ -0,0 +1,135 @@ +#include "src/camera/camera_constants.h" +#include "src/camera/camera_source.h" +#include "src/camera/cv_camera.h" +#include "src/localization/gpu_apriltag_detector.h" +#include "src/localization/multi_tag_solver.h" +#include "src/localization/networktable_sender.h" +#include "src/localization/opencv_apriltag_detector.h" +#include "src/localization/run_localization.h" +#include "src/localization/square_solver.h" +#include "src/utils/camera_utils.h" +#include "src/utils/nt_utils.h" +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" + +using camera::camera_constants_t; + +ABSL_FLAG(std::string, robot, "main_bot", // NOLINT + "Robot name used to select robot-specific camera constants"); + +namespace { + +auto GetRobotCameraConstantsPath(const std::string& robot) -> std::string { + return "/bos/constants/" + robot + "/camera_constants.json"; +} + +auto MakeDetector(const camera::camera_constant_t& camera_constant, + int image_width, int image_height) + -> std::unique_ptr { + const auto intrinsics = + utils::ReadIntrinsics(camera_constant.intrinsics_path.value()); + if (camera_constant.detector_backend == camera::DetectorBackend::kGpu) { + return std::make_unique( + image_width, image_height, intrinsics); + } + return std::make_unique( + image_width, image_height, intrinsics); +} + +auto IsMainBot(const std::string& robot) -> bool { return robot == "main_bot"; } + +auto IsSecondBot(const std::string& robot) -> bool { + return robot == "second_bot"; +} + +} // namespace + +auto main(int argc, char** argv) -> int { + absl::ParseCommandLine(argc, argv); + + const std::string robot = absl::GetFlag(FLAGS_robot); + if (IsMainBot(robot)) { + utils::StartNetworktables(9971); + } else if (IsSecondBot(robot)) { + utils::StartNetworktables(); + } else { + LOG(FATAL) << "Unsupported robot for robot_main: " << robot; + } + + std::string log_path = frc::DataLogManager::GetLogDir(); + camera_constants_t camera_constants = + camera::GetCameraConstants(GetRobotCameraConstantsPath(robot)); + + LOG(INFO) << "Starting estimators"; + + std::thread left_thread([&]() { + auto left_camera = std::make_unique( + "Left", std::make_unique(camera_constants.at("left"), + fmt::format("{}/left", log_path))); + cv::Mat left_camera_frame = left_camera->GetFrame(); + + std::vector> left_sender; + left_sender.emplace_back(std::make_unique( + camera_constants.at("left").name)); + + localization::RunLocalization( + std::move(left_camera), + MakeDetector(camera_constants.at("left"), left_camera_frame.cols, + left_camera_frame.rows), + std::make_unique( + camera_constants.at("left")), + std::move(left_sender), + camera_constants.at("left").extrinsics_path.value(), 5802, false); + }); + + std::thread right_thread([&]() { + auto right_camera = std::make_unique( + "Right", std::make_unique( + camera_constants.at("right"), + fmt::format("{}/right", log_path))); + cv::Mat right_camera_frame = right_camera->GetFrame(); + + std::vector> right_sender; + right_sender.emplace_back( + std::make_unique( + camera_constants.at("right").name)); + + localization::RunLocalization( + std::move(right_camera), + MakeDetector(camera_constants.at("right"), right_camera_frame.cols, + right_camera_frame.rows), + std::make_unique( + camera_constants.at("right")), + std::move(right_sender), + camera_constants.at("right").extrinsics_path.value(), 5803, false); + }); + + std::optional front_thread = std::nullopt; + if (IsMainBot(robot)) { + front_thread.emplace([&]() { + auto front_camera = std::make_unique( + "Front", + std::make_unique(camera_constants.at("front"))); + cv::Mat front_camera_frame = front_camera->GetFrame(); + + std::vector> front_sender; + front_sender.emplace_back( + std::make_unique( + camera_constants.at("front").name)); + + localization::RunLocalization( + std::move(front_camera), + MakeDetector(camera_constants.at("front"), front_camera_frame.cols, + front_camera_frame.rows), + std::make_unique( + camera_constants.at("front")), + std::move(front_sender), + camera_constants.at("front").extrinsics_path.value(), 5801, false); + }); + } + + LOG(INFO) << "Started estimators"; + + left_thread.join(); + return 0; +} diff --git a/src/second_bot_main.cc b/src/second_bot_main.cc deleted file mode 100644 index a2e305df..00000000 --- a/src/second_bot_main.cc +++ /dev/null @@ -1,75 +0,0 @@ -#include "src/camera/camera_constants.h" -#include "src/camera/camera_source.h" -#include "src/camera/cv_camera.h" -#include "src/localization/multi_tag_solver.h" -#include "src/localization/networktable_sender.h" -#include "src/localization/opencv_apriltag_detector.h" -#include "src/localization/run_localization.h" -#include "src/localization/square_solver.h" -#include "src/utils/camera_utils.h" -#include "src/utils/nt_utils.h" - -using camera::camera_constants_t; -auto main() -> int { - utils::StartNetworktables(); - - std::string log_path = frc::DataLogManager::GetLogDir(); - camera_constants_t camera_constants = camera::GetCameraConstants(); - - LOG(INFO) << "Starting estimators"; - - std::thread left_thread([&]() { - auto left_camera = std::make_unique( - "Left", std::make_unique( - camera_constants.at("second_bot_left"), - fmt::format("{}/left", log_path))); - cv::Mat left_camera_frame = left_camera->GetFrame(); - - std::vector> left_sender; - left_sender.emplace_back(std::make_unique( - camera_constants.at("second_bot_left").name)); - - localization::RunLocalization( - std::move(left_camera), - std::make_unique( - left_camera_frame.cols, left_camera_frame.rows, - utils::ReadIntrinsics(camera_constants.at("second_bot_left") - .intrinsics_path.value())), - std::make_unique( - camera_constants.at("second_bot_left")), - std::move(left_sender), - camera_constants.at("second_bot_left").extrinsics_path.value(), 5802, - false); - }); - - std::thread right_thread([&]() { - auto right_camera = std::make_unique( - "Right", std::make_unique( - camera_constants.at("second_bot_right"), - fmt::format("{}/right", log_path))); - cv::Mat right_camera_frame = right_camera->GetFrame(); - - std::vector> right_sender; - right_sender.emplace_back( - std::make_unique( - camera_constants.at("second_bot_right").name)); - - localization::RunLocalization( - std::move(right_camera), - std::make_unique( - right_camera_frame.cols, right_camera_frame.rows, - utils::ReadIntrinsics(camera_constants.at("second_bot_right") - .intrinsics_path.value())), - std::make_unique( - camera_constants.at("second_bot_right")), - std::move(right_sender), - camera_constants.at("second_bot_right").extrinsics_path.value(), 5803, - false); - }); - - // TODO front camera - - LOG(INFO) << "Started estimators"; - - left_thread.join(); -} diff --git a/src/test/integration_test/localization_test.cc b/src/test/integration_test/localization_test.cc index 80555fcc..3f8822a4 100644 --- a/src/test/integration_test/localization_test.cc +++ b/src/test/integration_test/localization_test.cc @@ -14,6 +14,7 @@ #include "src/camera/camera_constants.h" #include "src/camera/camera_source.h" #include "src/camera/disk_camera.h" +#include "src/localization/gpu_apriltag_detector.h" #include "src/localization/multi_tag_solver.h" #include "src/localization/networktable_sender.h" #include "src/localization/opencv_apriltag_detector.h" @@ -23,16 +24,39 @@ #include "src/utils/log.h" // Example command: -// ./build/src/test/integration_test/localization_test --camera_name=main_bot_right --image_folder=logs/log181/right --speed=0.5 +// ./build/src/test/integration_test/localization_test --robot=main_bot --camera_name=right --image_folder=logs/log181/right --speed=0.5 // To each camera has two streams. 1. Raw video stream, 2. Position estimate stream. The port for raw video stream is 580x and the port for position estimate stream is 480x. x is different for each camera. It starts at 1 and counts up. For example, to view the third camera's position estimate stream, go to localhost:4803 ABSL_FLAG(std::string, image_folder, "", //NOLINT "Path to folder of test images"); +ABSL_FLAG(std::string, robot, "main_bot", // NOLINT + "Robot name used to select robot-specific camera constants"); ABSL_FLAG(std::optional, camera_name, std::nullopt, //NOLINT "Camera name"); ABSL_FLAG(int, port, 5801, "Port"); //NOLINT ABSL_FLAG(double, speed, 1, "Delay between frames"); //NOLINT +namespace { + +auto GetRobotCameraConstantsPath(const std::string& robot) -> std::string { + return "/bos/constants/" + robot + "/camera_constants.json"; +} + +auto MakeDetector(const camera::camera_constant_t& camera_constant, + int image_width, int image_height) + -> std::unique_ptr { + const auto intrinsics = + utils::ReadIntrinsics(camera_constant.intrinsics_path.value()); + if (camera_constant.detector_backend == camera::DetectorBackend::kGpu) { + return std::make_unique( + image_width, image_height, intrinsics); + } + return std::make_unique( + image_width, image_height, intrinsics); +} + +} // namespace + auto HasRegularFiles(const std::filesystem::path& path) -> bool { for (const auto& entry : std::filesystem::directory_iterator(path)) { if (!entry.is_regular_file()) { @@ -69,17 +93,28 @@ auto FindCameraFolders(const std::filesystem::path& path) auto ResolveCameraName(const std::string& directory_name, const camera::camera_constants_t& constants) -> std::string { - std::string resolved_name = directory_name.rfind("main_bot_", 0) == 0 - ? directory_name - : "main_bot_" + directory_name; + if (constants.contains(directory_name)) { + return directory_name; + } + + constexpr std::string_view kPrefixes[] = {"main_bot_", "second_bot_", + "turret_bot_"}; + for (std::string_view prefix : kPrefixes) { + if (directory_name.rfind(prefix, 0) != 0) { + continue; + } + const std::string stripped = directory_name.substr(prefix.size()); + if (constants.contains(stripped)) { + return stripped; + } + } - if (!constants.contains(resolved_name)) { + if (!constants.contains(directory_name)) { LOG(FATAL) << "Could not resolve camera constants name for directory: " - << directory_name - << ", expected constants entry: " << resolved_name; + << directory_name; } - return resolved_name; + return directory_name; } auto main(int argc, char** argv) -> int { @@ -96,7 +131,8 @@ auto main(int argc, char** argv) -> int { } auto camera_folders = FindCameraFolders(image_root_path); - auto constants = camera::GetCameraConstants(); + auto constants = camera::GetCameraConstants( + GetRobotCameraConstantsPath(absl::GetFlag(FLAGS_robot))); std::optional camera_name_override = absl::GetFlag(FLAGS_camera_name); if (camera_name_override.has_value() && camera_folders.size() > 1) { @@ -140,10 +176,8 @@ auto main(int argc, char** argv) -> int { senders.emplace_back(std::make_unique( camera_name, base_port + i - 1000)); localization::RunLocalization( - std::move(camera_source), - std::make_unique( - frame.cols, frame.rows, - utils::ReadIntrinsics(camera_constant.intrinsics_path.value())), + std::move(camera_source), MakeDetector(camera_constant, frame.cols, + frame.rows), std::make_unique(camera_constant), std::move(senders), camera_constant.extrinsics_path.value(), base_port + i, true); diff --git a/src/test/integration_test/localization_test2.cc b/src/test/integration_test/localization_test2.cc index 421be66f..54388b96 100644 --- a/src/test/integration_test/localization_test2.cc +++ b/src/test/integration_test/localization_test2.cc @@ -16,15 +16,25 @@ #include "src/utils/log.h" // for reference, example command: -// ./build/src/test/integration_test/l2calization_test --camera_name=main_bot_right --image_folder=logs/log181/right --speed=0.5 +// ./build/src/test/integration_test/l2calization_test --robot=main_bot --camera_name=right --image_folder=logs/log181/right --speed=0.5 ABSL_FLAG(std::string, image_folder, "", //NOLINT "Path to folder of test images"); +ABSL_FLAG(std::string, robot, "main_bot", // NOLINT + "Robot name used to select robot-specific camera constants"); ABSL_FLAG(std::optional, camera_name, std::nullopt, //NOLINT "Camera name"); ABSL_FLAG(int, port, 5801, "Port"); //NOLINT ABSL_FLAG(double, speed, 0.01, "Delay between frames"); //NOLINT +namespace { + +auto GetRobotCameraConstantsPath(const std::string& robot) -> std::string { + return "/bos/constants/" + robot + "/camera_constants.json"; +} + +} // namespace + auto main(int argc, char** argv) -> int { absl::ParseCommandLine(argc, argv); @@ -36,16 +46,15 @@ auto main(int argc, char** argv) -> int { LOG(FATAL) << "Folder empty or doesn't exist"; } - auto constants = camera::GetCameraConstants(); + auto constants = camera::GetCameraConstants( + GetRobotCameraConstantsPath(absl::GetFlag(FLAGS_robot))); - std::vector> - cameras{{constants.at("main_bot_left"), localization::OPENCV_CPU}, - {constants.at("main_bot_right"), localization::OPENCV_CPU}}; + std::vector cameras{constants.at("left"), + constants.at("right")}; auto paths = std::make_optional>( {image_folder + "/left", image_folder + "/right"}); - // std::vector> - // cameras{{constants.at("main_bot_left"), localization::OPENCV_CPU}}; + // std::vector cameras{constants.at("left")}; // auto paths = std::make_optional>( // {image_folder + "/left"}); diff --git a/src/unambiguous_first.cc b/src/unambiguous_first.cc deleted file mode 100644 index 02260610..00000000 --- a/src/unambiguous_first.cc +++ /dev/null @@ -1,34 +0,0 @@ -#include "src/camera/camera_constants.h" -#include "src/camera/camera_source.h" -#include "src/camera/cv_camera.h" -#include "src/localization/multi_tag_solver.h" -#include "src/localization/opencv_apriltag_detector.h" -#include "src/localization/run_localization.h" -#include "src/localization/square_solver.h" -#include "src/localization/unambiguous_estimator.h" -#include "src/utils/camera_utils.h" -#include "src/utils/nt_utils.h" - -using camera::camera_constants_t; -auto main() -> int { - utils::StartNetworktables(9971); - - std::string log_path = frc::DataLogManager::GetLogDir(); - camera_constants_t camera_constants = camera::GetCameraConstants(); - - LOG(INFO) << "Starting cameras"; - - std::vector> - cameras{ - {camera_constants.at("main_bot_left"), - localization::Detector::AUSTIN_GPU}, - {camera_constants.at("main_bot_right"), - localization::Detector::AUSTIN_GPU}, - }; - - LOG(INFO) << "Started cameras"; - - localization::UnambiguousEstimator localizer(cameras, - std::make_optional(5801)); - localizer.Run(); -} diff --git a/src/unambiguous_main.cc b/src/unambiguous_main.cc new file mode 100644 index 00000000..7aa6b20e --- /dev/null +++ b/src/unambiguous_main.cc @@ -0,0 +1,61 @@ +#include "src/camera/camera_constants.h" +#include "src/camera/camera_source.h" +#include "src/camera/cv_camera.h" +#include "src/localization/multi_tag_solver.h" +#include "src/localization/opencv_apriltag_detector.h" +#include "src/localization/run_localization.h" +#include "src/localization/square_solver.h" +#include "src/localization/unambiguous_estimator.h" +#include "src/utils/camera_utils.h" +#include "src/utils/nt_utils.h" +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" + +using camera::camera_constants_t; + +ABSL_FLAG(std::string, robot, "main_bot", // NOLINT + "Robot name used to select robot-specific camera constants"); + +namespace { + +auto GetRobotCameraConstantsPath(const std::string& robot) -> std::string { + return "/bos/constants/" + robot + "/camera_constants.json"; +} + +auto IsMainBot(const std::string& robot) -> bool { return robot == "main_bot"; } + +auto IsSecondBot(const std::string& robot) -> bool { + return robot == "second_bot"; +} + +} // namespace + +auto main(int argc, char** argv) -> int { + absl::ParseCommandLine(argc, argv); + + const std::string robot = absl::GetFlag(FLAGS_robot); + if (IsMainBot(robot)) { + utils::StartNetworktables(9971); + } else if (IsSecondBot(robot)) { + utils::StartNetworktables(); + } else { + LOG(FATAL) << "Unsupported robot for unambiguous_main: " << robot; + } + + camera_constants_t camera_constants = + camera::GetCameraConstants(GetRobotCameraConstantsPath(robot)); + + LOG(INFO) << "Starting cameras"; + + std::vector cameras{camera_constants.at("left"), + camera_constants.at("right")}; + if (IsSecondBot(robot)) { + cameras.emplace_back(camera_constants.at("front")); + } + + LOG(INFO) << "Started cameras"; + + localization::UnambiguousEstimator localizer(cameras, + std::make_optional(5801)); + localizer.Run(); +} diff --git a/src/unambiguous_second.cc b/src/unambiguous_second.cc deleted file mode 100644 index 668b868f..00000000 --- a/src/unambiguous_second.cc +++ /dev/null @@ -1,36 +0,0 @@ -#include "src/camera/camera_constants.h" -#include "src/camera/camera_source.h" -#include "src/camera/cv_camera.h" -#include "src/localization/multi_tag_solver.h" -#include "src/localization/opencv_apriltag_detector.h" -#include "src/localization/run_localization.h" -#include "src/localization/square_solver.h" -#include "src/localization/unambiguous_estimator.h" -#include "src/utils/camera_utils.h" -#include "src/utils/nt_utils.h" - -using camera::camera_constants_t; -auto main() -> int { - utils::StartNetworktables(); - - std::string log_path = frc::DataLogManager::GetLogDir(); - camera_constants_t camera_constants = camera::GetCameraConstants(); - - LOG(INFO) << "Starting cameras"; - - std::vector> - cameras{ - {camera_constants.at("second_bot_left"), - localization::Detector::AUSTIN_GPU}, - {camera_constants.at("second_bot_right"), - localization::Detector::AUSTIN_GPU}, - {camera_constants.at("second_bot_front"), - localization::Detector::OPENCV_CPU}, - }; - - LOG(INFO) << "Started cameras"; - - localization::UnambiguousEstimator localizer(cameras, - std::make_optional(5801)); - localizer.Run(); -}