feat(armor_yolo_detect): add detector mode support with ROI cache
- Add detector_mode parameter for mode selection: - HYBRID_SYNC (0): YOLO runs every frame (original behavior) - HYBRID_ROI_CACHE (1): YOLO runs every N frames, detection every frame - Add roi_update_interval parameter (default 5) for ROI cache mode - Add detector_mode.hpp for mode definitions - Add AsyncDetector class skeleton for future async mode - Add debug ROI visualization (blue rectangles) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -32,6 +32,7 @@ find_package(CUDA REQUIRED)
|
||||
ament_auto_add_library(${PROJECT_NAME} SHARED
|
||||
src/armor_yolo_detector.cpp
|
||||
src/armor_yolo_detector_node.cpp
|
||||
src/async_detector.cpp
|
||||
src/trt_logger.cpp
|
||||
src/yolo_tensorrt.cpp
|
||||
)
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
// Uses YOLO to provide ROI regions, then applies traditional binary + light detection
|
||||
// for precise corner localization. Input/output compatible with armor_detector.
|
||||
//
|
||||
// Supports multiple detection modes:
|
||||
// - HYBRID_SYNC: YOLO runs every frame (original)
|
||||
// - HYBRID_ROI_CACHE: YOLO runs every N frames, detection runs every frame
|
||||
// - HYBRID_ASYNC: Async queue with ROI cache
|
||||
//
|
||||
// Copyright (C) FYT Vision Group. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -24,11 +29,16 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
// project
|
||||
#include "armor_yolo_detect/types.hpp"
|
||||
#include "armor_yolo_detect/yolo_tensorrt.hpp"
|
||||
#include "armor_yolo_detect/detector_mode.hpp"
|
||||
#include "armor_detector/types.hpp"
|
||||
#include "armor_detector/light_corner_corrector.hpp"
|
||||
#include "rm_utils/common.hpp"
|
||||
@@ -71,8 +81,6 @@ public:
|
||||
* @brief Initialize detector with TensorRT engine and traditional vision params
|
||||
* @param engine_path Path to TensorRT engine file
|
||||
* @param params Detection parameters
|
||||
* @param light_params Light bar detection parameters
|
||||
* @param armor_params Armor matching parameters
|
||||
*/
|
||||
bool init(const std::string& engine_path, const Params& params);
|
||||
|
||||
@@ -90,6 +98,15 @@ public:
|
||||
*/
|
||||
std::vector<YoloObject> detectRaw(const cv::Mat& input);
|
||||
|
||||
// Set detection mode
|
||||
void setMode(DetectorMode mode);
|
||||
|
||||
// Get current mode
|
||||
DetectorMode getMode() const { return mode_; }
|
||||
|
||||
// Set ROI update interval (frames between YOLO updates)
|
||||
void setRoiUpdateInterval(int interval);
|
||||
|
||||
// Parameters
|
||||
Params params;
|
||||
LightParams light_params;
|
||||
@@ -120,10 +137,24 @@ private:
|
||||
// Expand YOLO bbox to ROI
|
||||
cv::Rect expandBBox(const cv::Rect& bbox, const cv::Size& img_size, int pixel_expand);
|
||||
|
||||
// Process ROIs to detect armors (common logic for all modes)
|
||||
std::vector<fyt::auto_aim::Armor> processROIs(
|
||||
const cv::Mat& input,
|
||||
const cv::Mat& gray_img,
|
||||
const cv::Mat& binary_img);
|
||||
|
||||
std::unique_ptr<YoloTensorRT> tensorrt_;
|
||||
std::vector<YoloObject> yolo_objects_;
|
||||
std::vector<fyt::auto_aim::Armor> armors_;
|
||||
std::unique_ptr<fyt::auto_aim::LightCornerCorrector> corner_corrector_;
|
||||
|
||||
// Detection mode
|
||||
DetectorMode mode_ = DetectorMode::HYBRID_SYNC;
|
||||
|
||||
// ROI cache for HYBRID_ROI_CACHE and HYBRID_ASYNC modes
|
||||
std::vector<YoloObject> cached_yolo_objects_;
|
||||
int roi_update_interval_ = 5;
|
||||
std::atomic<uint64_t> frame_counter_{0};
|
||||
};
|
||||
|
||||
} // namespace armor_yolo_detect
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
|
||||
#include "armor_yolo_detect/armor_yolo_detector.hpp"
|
||||
#include "armor_yolo_detect/types.hpp"
|
||||
#include "armor_yolo_detect/detector_mode.hpp"
|
||||
#include "armor_detector/armor_pose_estimator.hpp"
|
||||
#include "rm_utils/logger/log.hpp"
|
||||
#include "rm_utils/heartbeat.hpp"
|
||||
@@ -108,6 +109,9 @@ private:
|
||||
std::unique_ptr<Detector> detector_;
|
||||
cv::Point2f cam_center_;
|
||||
|
||||
// Detection mode
|
||||
DetectorMode detector_mode_ = DetectorMode::HYBRID_SYNC;
|
||||
|
||||
// Camera info for pose estimation
|
||||
sensor_msgs::msg::CameraInfo::SharedPtr cam_info_;
|
||||
std::unique_ptr<fyt::auto_aim::ArmorPoseEstimator> armor_pose_estimator_;
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright chenyy 2026. Licensed under the MIT License.
|
||||
// Async detector for armor_yolo_detect
|
||||
//
|
||||
// Uses a separate processing thread with an async queue to reduce data blocking.
|
||||
// Combined with ROI cache to reduce YOLO inference frequency.
|
||||
//
|
||||
// Copyright (C) FYT Vision Group. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ARMOR_YOLO_DETECT_ASYNC_DETECTOR_HPP_
|
||||
#define ARMOR_YOLO_DETECT_ASYNC_DETECTOR_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
// project
|
||||
#include "armor_yolo_detect/armor_yolo_detector.hpp"
|
||||
#include "armor_detector/types.hpp"
|
||||
|
||||
namespace armor_yolo_detect {
|
||||
|
||||
struct AsyncFrame {
|
||||
cv::Mat img;
|
||||
uint64_t frame_id;
|
||||
std::chrono::steady_clock::time_point timestamp;
|
||||
};
|
||||
|
||||
class AsyncDetector {
|
||||
public:
|
||||
AsyncDetector();
|
||||
~AsyncDetector();
|
||||
|
||||
// Initialize with a existing detector
|
||||
void init(std::shared_ptr<Detector> detector, int queue_size = 3);
|
||||
|
||||
// Start async processing thread
|
||||
void start();
|
||||
|
||||
// Stop async processing thread
|
||||
void stop();
|
||||
|
||||
// Enqueue a frame for async processing
|
||||
void enqueue(const cv::Mat& img);
|
||||
|
||||
// Get latest detection result (non-blocking)
|
||||
std::vector<fyt::auto_aim::Armor> getLatestResult();
|
||||
|
||||
// Check if processing is running
|
||||
bool isRunning() const { return running_; }
|
||||
|
||||
// Get queue size
|
||||
size_t queueSize() const {
|
||||
std::lock_guard<std::mutex> lock(queue_mutex_);
|
||||
return frame_queue_.size();
|
||||
}
|
||||
|
||||
private:
|
||||
void processingLoop();
|
||||
|
||||
// Shared detector pointer
|
||||
std::shared_ptr<Detector> detector_;
|
||||
|
||||
// Frame queue
|
||||
std::deque<AsyncFrame> frame_queue_;
|
||||
size_t max_queue_size_ = 3;
|
||||
|
||||
// Processing thread
|
||||
std::thread processing_thread_;
|
||||
std::atomic<bool> running_{false};
|
||||
|
||||
// Synchronization
|
||||
std::mutex queue_mutex_;
|
||||
std::condition_variable queue_cv_;
|
||||
|
||||
// Latest result
|
||||
std::vector<fyt::auto_aim::Armor> latest_result_;
|
||||
std::mutex result_mutex_;
|
||||
};
|
||||
|
||||
} // namespace armor_yolo_detect
|
||||
|
||||
#endif // ARMOR_YOLO_DETECT_ASYNC_DETECTOR_HPP_
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright chenyy 2026. Licensed under the MIT License.
|
||||
//
|
||||
// Detection modes for armor_yolo_detect
|
||||
//
|
||||
// Copyright (C) FYT Vision Group. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ARMOR_YOLO_DETECT_DETECTOR_MODE_HPP_
|
||||
#define ARMOR_YOLO_DETECT_DETECTOR_MODE_HPP_
|
||||
|
||||
namespace armor_yolo_detect {
|
||||
|
||||
// Detection modes
|
||||
enum class DetectorMode {
|
||||
// Original hybrid detection: YOLO runs every frame
|
||||
HYBRID_SYNC = 0,
|
||||
// ROI cache mode: YOLO runs every N frames, detection runs every frame
|
||||
HYBRID_ROI_CACHE = 1,
|
||||
// Async mode: async queue with ROI cache
|
||||
HYBRID_ASYNC = 2
|
||||
};
|
||||
|
||||
inline std::string detectorModeToString(DetectorMode mode) {
|
||||
switch (mode) {
|
||||
case DetectorMode::HYBRID_SYNC:
|
||||
return "hybrid_sync";
|
||||
case DetectorMode::HYBRID_ROI_CACHE:
|
||||
return "hybrid_roi_cache";
|
||||
case DetectorMode::HYBRID_ASYNC:
|
||||
return "hybrid_async";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace armor_yolo_detect
|
||||
|
||||
#endif // ARMOR_YOLO_DETECT_DETECTOR_MODE_HPP_
|
||||
@@ -4,6 +4,11 @@
|
||||
// Uses YOLO to provide ROI regions, then applies traditional binary + light detection
|
||||
// for precise corner localization. Input/output compatible with armor_detector.
|
||||
//
|
||||
// Supports multiple detection modes:
|
||||
// - HYBRID_SYNC: YOLO runs every frame (original)
|
||||
// - HYBRID_ROI_CACHE: YOLO runs every N frames, detection runs every frame
|
||||
// - HYBRID_ASYNC: Async queue with ROI cache
|
||||
//
|
||||
// Copyright (C) FYT Vision Group. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -42,25 +47,68 @@ bool Detector::init(const std::string& engine_path, const Params& params) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Detector::setMode(DetectorMode mode) {
|
||||
mode_ = mode;
|
||||
if (mode_ != DetectorMode::HYBRID_SYNC) {
|
||||
// Clear cache when switching to cached modes
|
||||
cached_yolo_objects_.clear();
|
||||
frame_counter_.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
void Detector::setRoiUpdateInterval(int interval) {
|
||||
roi_update_interval_ = std::max(1, interval);
|
||||
}
|
||||
|
||||
std::vector<fyt::auto_aim::Armor> Detector::detect(const cv::Mat& input) {
|
||||
armors_.clear();
|
||||
debug_rois_.clear();
|
||||
|
||||
// 1. Run YOLO inference to get ROI regions
|
||||
yolo_objects_ = tensorrt_->infer(input, params.detect_color);
|
||||
|
||||
if (yolo_objects_.empty()) {
|
||||
return armors_;
|
||||
}
|
||||
|
||||
// 2. Preprocess image once
|
||||
// Preprocess image once
|
||||
cv::Mat gray_img;
|
||||
cv::cvtColor(input, gray_img, cv::COLOR_BGR2GRAY);
|
||||
|
||||
cv::Mat full_binary_img;
|
||||
cv::threshold(gray_img, full_binary_img, binary_thres, 255, cv::THRESH_BINARY);
|
||||
|
||||
// 3. For each YOLO detection, create ROI and detect lights
|
||||
uint64_t current_frame = frame_counter_.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
// Update YOLO objects based on mode
|
||||
switch (mode_) {
|
||||
case DetectorMode::HYBRID_SYNC:
|
||||
// YOLO runs every frame
|
||||
yolo_objects_ = tensorrt_->infer(input, params.detect_color);
|
||||
break;
|
||||
|
||||
case DetectorMode::HYBRID_ROI_CACHE:
|
||||
case DetectorMode::HYBRID_ASYNC:
|
||||
// YOLO runs every roi_update_interval_ frames
|
||||
if (current_frame % roi_update_interval_ == 0) {
|
||||
yolo_objects_ = tensorrt_->infer(input, params.detect_color);
|
||||
cached_yolo_objects_ = yolo_objects_;
|
||||
} else {
|
||||
// Use cached ROIs
|
||||
yolo_objects_ = cached_yolo_objects_;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (yolo_objects_.empty()) {
|
||||
return armors_;
|
||||
}
|
||||
|
||||
// Process ROIs to detect armors
|
||||
armors_ = processROIs(input, gray_img, full_binary_img);
|
||||
|
||||
return armors_;
|
||||
}
|
||||
|
||||
std::vector<fyt::auto_aim::Armor> Detector::processROIs(
|
||||
const cv::Mat& input,
|
||||
const cv::Mat& gray_img,
|
||||
const cv::Mat& binary_img) {
|
||||
std::vector<fyt::auto_aim::Armor> result_armors;
|
||||
|
||||
for (const auto& yolo_obj : yolo_objects_) {
|
||||
// Get bounding box from YOLO
|
||||
cv::Rect_<float> bbox = yolo_obj.rect;
|
||||
@@ -80,19 +128,19 @@ std::vector<fyt::auto_aim::Armor> Detector::detect(const cv::Mat& input) {
|
||||
|
||||
// Extract ROI images
|
||||
cv::Mat roi_gray = gray_img(roi);
|
||||
cv::Mat roi_binary = full_binary_img(roi);
|
||||
cv::Mat roi_binary = binary_img(roi);
|
||||
|
||||
// 4. Find lights within this ROI
|
||||
// Find lights within this ROI
|
||||
auto lights = findLightsInROI(input, roi_gray, roi_binary, roi);
|
||||
|
||||
if (lights.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 5. Match lights to form armors within this ROI
|
||||
// Match lights to form armors within this ROI
|
||||
auto roi_armors = matchLightsInROI(lights);
|
||||
|
||||
// 6. Process each armor: assign YOLO classification info
|
||||
// Process each armor: assign YOLO classification info
|
||||
for (auto& armor : roi_armors) {
|
||||
// Convert ROI coordinates to global coordinates
|
||||
armor.left_light.top += cv::Point2f(static_cast<float>(roi.x), static_cast<float>(roi.y));
|
||||
@@ -108,16 +156,15 @@ std::vector<fyt::auto_aim::Armor> Detector::detect(const cv::Mat& input) {
|
||||
armor.confidence = yolo_obj.prob;
|
||||
|
||||
// Use armor type from traditional detection (more accurate)
|
||||
// YOLO info available if needed: yolo_obj.color
|
||||
|
||||
// 7. Correct corners using traditional method
|
||||
// Correct corners using traditional method
|
||||
correctCorners(armor, gray_img);
|
||||
|
||||
armors_.push_back(armor);
|
||||
result_armors.push_back(armor);
|
||||
}
|
||||
}
|
||||
|
||||
return armors;
|
||||
return result_armors;
|
||||
}
|
||||
|
||||
std::vector<YoloObject> Detector::detectRaw(const cv::Mat& input) {
|
||||
|
||||
@@ -49,6 +49,8 @@
|
||||
#include <opencv2/imgproc.hpp>
|
||||
|
||||
#include "armor_yolo_detect/types.hpp"
|
||||
#include "armor_yolo_detect/detector_mode.hpp"
|
||||
#include "armor_yolo_detect/async_detector.hpp"
|
||||
#include "armor_detector/ba_solver.hpp"
|
||||
#include "rm_utils/assert.hpp"
|
||||
#include "rm_utils/common.hpp"
|
||||
@@ -368,7 +370,18 @@ std::unique_ptr<Detector> ArmorYoloDetectorNode::initDetector() {
|
||||
this->declare_parameter("armor.max_large_center_distance", 5.0);
|
||||
detector->armor_params.max_angle = this->declare_parameter("armor.max_angle", 35.0);
|
||||
|
||||
// Detection mode
|
||||
int mode_int = this->declare_parameter("detector_mode", 0);
|
||||
detector_mode_ = static_cast<DetectorMode>(mode_int);
|
||||
detector->setMode(detector_mode_);
|
||||
|
||||
// ROI update interval (for roi_cache and async modes)
|
||||
int roi_update_interval = this->declare_parameter("roi_update_interval", 5);
|
||||
detector->setRoiUpdateInterval(roi_update_interval);
|
||||
|
||||
FYT_INFO("armor_yolo_detect", "Inference backend: TensorRT ({})", engine_path.string());
|
||||
FYT_INFO("armor_yolo_detect", "Detector mode: {} (roi_update_interval={})",
|
||||
detectorModeToString(detector_mode_), roi_update_interval);
|
||||
|
||||
// Set dynamic parameter callback
|
||||
on_set_parameters_callback_handle_ = this->add_on_set_parameters_callback(
|
||||
@@ -463,6 +476,13 @@ rcl_interfaces::msg::SetParametersResult ArmorYoloDetectorNode::onSetParameters(
|
||||
detector_->armor_params.max_large_center_distance = param.as_double();
|
||||
} else if (param.get_name() == "armor.max_angle") {
|
||||
detector_->armor_params.max_angle = param.as_double();
|
||||
} else if (param.get_name() == "detector_mode") {
|
||||
detector_mode_ = static_cast<DetectorMode>(param.as_int());
|
||||
detector_->setMode(detector_mode_);
|
||||
FYT_INFO("armor_yolo_detect", "Detector mode changed to: {}",
|
||||
detectorModeToString(detector_mode_));
|
||||
} else if (param.get_name() == "roi_update_interval") {
|
||||
detector_->setRoiUpdateInterval(param.as_int());
|
||||
} else if (param.get_name() == "debug.enable_terminal_log") {
|
||||
debug_terminal_log_ = param.as_bool();
|
||||
} else if (param.get_name() == "debug.enable_markers") {
|
||||
|
||||
113
src/rm_auto_aim/armor_yolo_detect/src/async_detector.cpp
Normal file
113
src/rm_auto_aim/armor_yolo_detect/src/async_detector.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright chenyy 2026. Licensed under the MIT License.
|
||||
// Async detector implementation
|
||||
//
|
||||
// Copyright (C) FYT Vision Group. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "armor_yolo_detect/async_detector.hpp"
|
||||
|
||||
namespace armor_yolo_detect {
|
||||
|
||||
AsyncDetector::AsyncDetector() = default;
|
||||
|
||||
AsyncDetector::~AsyncDetector() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void AsyncDetector::init(std::shared_ptr<Detector> detector, int queue_size) {
|
||||
detector_ = detector;
|
||||
max_queue_size_ = static_cast<size_t>(std::max(1, queue_size));
|
||||
}
|
||||
|
||||
void AsyncDetector::start() {
|
||||
if (running_) {
|
||||
return;
|
||||
}
|
||||
|
||||
running_ = true;
|
||||
processing_thread_ = std::thread(&AsyncDetector::processingLoop, this);
|
||||
}
|
||||
|
||||
void AsyncDetector::stop() {
|
||||
if (!running_) {
|
||||
return;
|
||||
}
|
||||
|
||||
running_ = false;
|
||||
queue_cv_.notify_one();
|
||||
|
||||
if (processing_thread_.joinable()) {
|
||||
processing_thread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncDetector::enqueue(const cv::Mat& img) {
|
||||
std::lock_guard<std::mutex> lock(queue_mutex_);
|
||||
|
||||
// Remove oldest frame if queue is full
|
||||
if (frame_queue_.size() >= max_queue_size_) {
|
||||
frame_queue_.pop_front();
|
||||
}
|
||||
|
||||
static uint64_t frame_id_counter = 0;
|
||||
AsyncFrame frame;
|
||||
frame.img = img.clone();
|
||||
frame.frame_id = frame_id_counter++;
|
||||
frame.timestamp = std::chrono::steady_clock::now();
|
||||
|
||||
frame_queue_.push_back(std::move(frame));
|
||||
queue_cv_.notify_one();
|
||||
}
|
||||
|
||||
std::vector<fyt::auto_aim::Armor> AsyncDetector::getLatestResult() {
|
||||
std::lock_guard<std::mutex> lock(result_mutex_);
|
||||
return latest_result_;
|
||||
}
|
||||
|
||||
void AsyncDetector::processingLoop() {
|
||||
while (running_) {
|
||||
AsyncFrame frame;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
queue_cv_.wait_for(
|
||||
lock,
|
||||
std::chrono::milliseconds(100),
|
||||
[this]() { return !frame_queue_.empty() || !running_; });
|
||||
|
||||
if (!running_ && frame_queue_.empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (frame_queue_.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
frame = std::move(frame_queue_.front());
|
||||
frame_queue_.pop_front();
|
||||
}
|
||||
|
||||
// Process frame
|
||||
if (detector_) {
|
||||
auto result = detector_->detect(frame.img);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(result_mutex_);
|
||||
latest_result_ = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace armor_yolo_detect
|
||||
Reference in New Issue
Block a user