forked from zakaria/chicken-counting-sukawarna-det
To move to main
This commit is contained in:
1 parent
f34eaa3af4
commit
be204b91cc
11 files changed
+187
-63
No files matched your search
Binary file not shown.
Binary file not shown.
@@ -11,11 +11,11 @@ defaults:
|
|||||||
model_path: /media/jetson/DATA/.Codes/chicken-counting-sukawarna-det/chicken-detection-model-v26n-300e-best-2026-05-02-NEW.engine
|
model_path: /media/jetson/DATA/.Codes/chicken-counting-sukawarna-det/chicken-detection-model-v26n-300e-best-2026-05-02-NEW.engine
|
||||||
classes: [0]
|
classes: [0]
|
||||||
ignored_classes: [1, 2]
|
ignored_classes: [1, 2]
|
||||||
conf: 0.45
|
conf: 0.55
|
||||||
iou: 0.55
|
iou: 0.55
|
||||||
imgsz: 640
|
imgsz: 640
|
||||||
device: "0"
|
device: "0"
|
||||||
min_box_area_px: 3000
|
min_box_area_px: 5000
|
||||||
validate_while_inside: true
|
validate_while_inside: true
|
||||||
detection_zone:
|
detection_zone:
|
||||||
enabled: true
|
enabled: true
|
||||||
@@ -53,6 +53,7 @@ defaults:
|
|||||||
show_track_ring: false
|
show_track_ring: false
|
||||||
count_anchor: [780, 120]
|
count_anchor: [780, 120]
|
||||||
inside_box_only: true
|
inside_box_only: true
|
||||||
|
validated_only: true
|
||||||
pending_blink: true
|
pending_blink: true
|
||||||
pending_colors:
|
pending_colors:
|
||||||
- [255, 255, 0]
|
- [255, 255, 0]
|
||||||
@@ -67,7 +68,7 @@ defaults:
|
|||||||
overlay_buffer_reuse: true
|
overlay_buffer_reuse: true
|
||||||
inference_stride: 2
|
inference_stride: 2
|
||||||
stream:
|
stream:
|
||||||
enabled: false
|
enabled: true
|
||||||
shm_dir: /dev/shm
|
shm_dir: /dev/shm
|
||||||
interval_frames: 5
|
interval_frames: 5
|
||||||
feedback:
|
feedback:
|
||||||
@@ -80,7 +81,7 @@ defaults:
|
|||||||
inset_right_px: 60
|
inset_right_px: 60
|
||||||
inset_top_px: 0
|
inset_top_px: 0
|
||||||
inset_bottom_px: 0
|
inset_bottom_px: 0
|
||||||
min_overlap_ratio: 0.30
|
min_overlap_ratio: 0.35
|
||||||
|
|
||||||
cameras:
|
cameras:
|
||||||
CC1:
|
CC1:
|
||||||
@@ -95,6 +96,8 @@ cameras:
|
|||||||
CC2:
|
CC2:
|
||||||
camera_num: 2
|
camera_num: 2
|
||||||
count_anchor: [900, 120]
|
count_anchor: [900, 120]
|
||||||
|
detection:
|
||||||
|
min_box_area_px: 3000
|
||||||
roi:
|
roi:
|
||||||
points:
|
points:
|
||||||
- [20, 380]
|
- [20, 380]
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
batch:
|
||||||
|
root_dir: /media/jetson/DATA/.Codes/VIDEOS/cycle7/kandang-atas
|
||||||
|
camera_glob: "kandang_*_camera_{num}_*.mp4"
|
||||||
|
output_subdir: output
|
||||||
|
compress_max_mb: 200
|
||||||
|
delete_intermediate: false
|
||||||
|
checkpoint_every_n_frames: 3000
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
detection:
|
||||||
|
model_path: /media/jetson/DATA/.Codes/chicken-counting-sukawarna-det/chicken-detection-model-v26n-300e-best-2026-05-02-NEW.engine
|
||||||
|
classes: [0]
|
||||||
|
ignored_classes: [1, 2]
|
||||||
|
conf: 0.35
|
||||||
|
iou: 0.55
|
||||||
|
imgsz: 640
|
||||||
|
device: "0"
|
||||||
|
min_box_area_px: 3000
|
||||||
|
validate_while_inside: true
|
||||||
|
detection_zone:
|
||||||
|
enabled: true
|
||||||
|
buffer_above_px: 250
|
||||||
|
buffer_below_px: 250
|
||||||
|
show_in_overlay: true
|
||||||
|
tracker:
|
||||||
|
tracker_config_path: /media/jetson/DATA/.Codes/chicken-counting-sukawarna-det/configs/trackers/botsort_chicken.yaml
|
||||||
|
persist: true
|
||||||
|
track_buffer: 75
|
||||||
|
gate:
|
||||||
|
mode: two_line
|
||||||
|
lines_y: [420, 730]
|
||||||
|
direction: bottom_to_up
|
||||||
|
motion:
|
||||||
|
enabled: true
|
||||||
|
axis: vertical
|
||||||
|
forward_sign: 1.0
|
||||||
|
ema_alpha: 0.2
|
||||||
|
reverse_enter_threshold: -1.5
|
||||||
|
reverse_exit_threshold: -0.5
|
||||||
|
debounce_frames: 12
|
||||||
|
min_features: 60
|
||||||
|
max_corners: 80
|
||||||
|
stride_frames: 2
|
||||||
|
flow_scale: 0.5
|
||||||
|
quality_level: 0.01
|
||||||
|
min_distance: 8
|
||||||
|
block_radius: 6
|
||||||
|
overlay:
|
||||||
|
show_boxes: true
|
||||||
|
show_track_trails: false
|
||||||
|
trail_length: 20
|
||||||
|
show_center_marker: true
|
||||||
|
show_track_ring: false
|
||||||
|
count_anchor: [780, 120]
|
||||||
|
inside_box_only: true
|
||||||
|
pending_blink: true
|
||||||
|
pending_colors:
|
||||||
|
- [255, 255, 0]
|
||||||
|
- [0, 255, 255]
|
||||||
|
display:
|
||||||
|
show_window: false
|
||||||
|
encoder: auto
|
||||||
|
output_bitrate_kbps: 4000
|
||||||
|
codec_preference: [avc1, mp4v, H264]
|
||||||
|
performance:
|
||||||
|
half: false
|
||||||
|
overlay_buffer_reuse: true
|
||||||
|
inference_stride: 2
|
||||||
|
stream:
|
||||||
|
enabled: true
|
||||||
|
shm_dir: /dev/shm
|
||||||
|
interval_frames: 5
|
||||||
|
feedback:
|
||||||
|
enabled: true
|
||||||
|
every_n_frames: 3000
|
||||||
|
save_images: true
|
||||||
|
log_to_terminal: true
|
||||||
|
roi:
|
||||||
|
inset_left_px: 60
|
||||||
|
inset_right_px: 60
|
||||||
|
inset_top_px: 0
|
||||||
|
inset_bottom_px: 0
|
||||||
|
min_overlap_ratio: 0.30
|
||||||
|
|
||||||
|
cameras:
|
||||||
|
CC1:
|
||||||
|
camera_num: 1
|
||||||
|
count_anchor: [780, 120]
|
||||||
|
roi:
|
||||||
|
points:
|
||||||
|
- [250, 330]
|
||||||
|
- [1650, 330]
|
||||||
|
- [1650, 720]
|
||||||
|
- [250, 720]
|
||||||
|
CC2:
|
||||||
|
camera_num: 2
|
||||||
|
count_anchor: [900, 120]
|
||||||
|
roi:
|
||||||
|
points:
|
||||||
|
- [20, 380]
|
||||||
|
- [1880, 380]
|
||||||
|
- [1880, 720]
|
||||||
|
- [20, 720]
|
||||||
|
CC3:
|
||||||
|
camera_num: 3
|
||||||
|
count_anchor: [900, 120]
|
||||||
|
roi:
|
||||||
|
points:
|
||||||
|
- [20, 330]
|
||||||
|
- [1880, 330]
|
||||||
|
- [1880, 720]
|
||||||
|
- [20, 720]
|
||||||
|
CC4:
|
||||||
|
camera_num: 4
|
||||||
|
count_anchor: [700, 120]
|
||||||
|
roi:
|
||||||
|
points:
|
||||||
|
- [50, 330]
|
||||||
|
- [1450, 330]
|
||||||
|
- [1450, 720]
|
||||||
|
- [50, 720]
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
tracker_type: botsort
|
tracker_type: botsort
|
||||||
track_high_thresh: 0.55
|
track_high_thresh: 0.65
|
||||||
track_low_thresh: 0.1
|
track_low_thresh: 0.3
|
||||||
new_track_thresh: 0.7
|
new_track_thresh: 0.7
|
||||||
track_buffer: 75
|
track_buffer: 75
|
||||||
match_thresh: 0.9
|
match_thresh: 0.9
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ set(CMAKE_CXX_STANDARD 17)
|
|||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE Release)
|
||||||
|
endif()
|
||||||
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
|
||||||
|
|
||||||
find_package(OpenCV 4.0 REQUIRED COMPONENTS core imgproc video videoio highgui imgcodecs dnn)
|
find_package(OpenCV 4.0 REQUIRED COMPONENTS core imgproc video videoio highgui imgcodecs dnn)
|
||||||
find_package(nlohmann_json 3.0 REQUIRED)
|
find_package(nlohmann_json 3.0 REQUIRED)
|
||||||
find_package(yaml-cpp REQUIRED)
|
find_package(yaml-cpp REQUIRED)
|
||||||
@@ -31,11 +36,13 @@ target_include_directories(chicken_counter_lib PUBLIC
|
|||||||
/usr/include/aarch64-linux-gnu
|
/usr/include/aarch64-linux-gnu
|
||||||
)
|
)
|
||||||
target_link_libraries(chicken_counter_lib PUBLIC ${COMMON_LIBS})
|
target_link_libraries(chicken_counter_lib PUBLIC ${COMMON_LIBS})
|
||||||
|
target_compile_options(chicken_counter_lib PRIVATE -O3 -march=armv8.2-a+fp16+dotprod -flto -DNDEBUG)
|
||||||
|
|
||||||
add_executable(chicken_counter_cli
|
add_executable(chicken_counter_cli
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
)
|
)
|
||||||
target_link_libraries(chicken_counter_cli PRIVATE chicken_counter_lib)
|
target_link_libraries(chicken_counter_cli PRIVATE chicken_counter_lib)
|
||||||
|
target_link_options(chicken_counter_cli PRIVATE -Wl,--strip-all)
|
||||||
|
|
||||||
add_executable(test_config
|
add_executable(test_config
|
||||||
tests/test_config.cpp
|
tests/test_config.cpp
|
||||||
|
|||||||
@@ -284,6 +284,7 @@ struct OverlayConfig {
|
|||||||
bool show_track_ring = false;
|
bool show_track_ring = false;
|
||||||
cv::Point2i count_anchor = {900, 120};
|
cv::Point2i count_anchor = {900, 120};
|
||||||
bool inside_box_only = true;
|
bool inside_box_only = true;
|
||||||
|
bool validated_only = false;
|
||||||
bool pending_blink = true;
|
bool pending_blink = true;
|
||||||
std::vector<cv::Scalar> pending_colors = {cv::Scalar(255, 255, 0), cv::Scalar(0, 255, 255)};
|
std::vector<cv::Scalar> pending_colors = {cv::Scalar(255, 255, 0), cv::Scalar(0, 255, 255)};
|
||||||
};
|
};
|
||||||
@@ -291,7 +292,8 @@ inline void to_json(nlohmann::json& j, const OverlayConfig& c) {
|
|||||||
j = {{"show_boxes", c.show_boxes}, {"show_track_trails", c.show_track_trails},
|
j = {{"show_boxes", c.show_boxes}, {"show_track_trails", c.show_track_trails},
|
||||||
{"trail_length", c.trail_length}, {"show_center_marker", c.show_center_marker},
|
{"trail_length", c.trail_length}, {"show_center_marker", c.show_center_marker},
|
||||||
{"show_track_ring", c.show_track_ring}, {"count_anchor", c.count_anchor},
|
{"show_track_ring", c.show_track_ring}, {"count_anchor", c.count_anchor},
|
||||||
{"inside_box_only", c.inside_box_only}, {"pending_blink", c.pending_blink},
|
{"inside_box_only", c.inside_box_only}, {"validated_only", c.validated_only},
|
||||||
|
{"pending_blink", c.pending_blink},
|
||||||
{"pending_colors", c.pending_colors}};
|
{"pending_colors", c.pending_colors}};
|
||||||
}
|
}
|
||||||
inline void from_json(const nlohmann::json& j, OverlayConfig& c) {
|
inline void from_json(const nlohmann::json& j, OverlayConfig& c) {
|
||||||
@@ -302,6 +304,7 @@ inline void from_json(const nlohmann::json& j, OverlayConfig& c) {
|
|||||||
c.show_track_ring = j.value("show_track_ring", false);
|
c.show_track_ring = j.value("show_track_ring", false);
|
||||||
c.count_anchor = j.value("count_anchor", cv::Point2i{900, 120});
|
c.count_anchor = j.value("count_anchor", cv::Point2i{900, 120});
|
||||||
c.inside_box_only = j.value("inside_box_only", true);
|
c.inside_box_only = j.value("inside_box_only", true);
|
||||||
|
c.validated_only = j.value("validated_only", false);
|
||||||
c.pending_blink = j.value("pending_blink", true);
|
c.pending_blink = j.value("pending_blink", true);
|
||||||
c.pending_colors = j.value("pending_colors",
|
c.pending_colors = j.value("pending_colors",
|
||||||
std::vector<cv::Scalar>{cv::Scalar(255, 255, 0), cv::Scalar(0, 255, 255)});
|
std::vector<cv::Scalar>{cv::Scalar(255, 255, 0), cv::Scalar(0, 255, 255)});
|
||||||
@@ -328,7 +331,8 @@ inline void to_json(nlohmann::json& j, const DisplayConfig& c) {
|
|||||||
inline void from_json(const nlohmann::json& j, DisplayConfig& c) {
|
inline void from_json(const nlohmann::json& j, DisplayConfig& c) {
|
||||||
c.window_name = j.value("window_name", "Chicken Counter");
|
c.window_name = j.value("window_name", "Chicken Counter");
|
||||||
c.show_window = j.value("show_window", true);
|
c.show_window = j.value("show_window", true);
|
||||||
c.output_path = j.value("output_path", "");
|
if (j.contains("output_path") && !j["output_path"].is_null())
|
||||||
|
c.output_path = j["output_path"].get<std::string>();
|
||||||
c.write_fps = j.value("write_fps", -1.0f);
|
c.write_fps = j.value("write_fps", -1.0f);
|
||||||
c.max_frames = j.value("max_frames", -1);
|
c.max_frames = j.value("max_frames", -1);
|
||||||
c.encoder = j.value("encoder", "auto");
|
c.encoder = j.value("encoder", "auto");
|
||||||
@@ -455,9 +459,10 @@ struct CameraPreset {
|
|||||||
std::string camera_id;
|
std::string camera_id;
|
||||||
int camera_num;
|
int camera_num;
|
||||||
RoiConfig roi;
|
RoiConfig roi;
|
||||||
cv::Point2i count_anchor = {-1, -1}; // (-1,-1) = not set
|
cv::Point2i count_anchor = {-1, -1};
|
||||||
GateConfig gate;
|
GateConfig gate;
|
||||||
MotionConfig motion;
|
MotionConfig motion;
|
||||||
|
nlohmann::json detection_overrides;
|
||||||
bool has_gate = false;
|
bool has_gate = false;
|
||||||
bool has_motion = false;
|
bool has_motion = false;
|
||||||
};
|
};
|
||||||
@@ -536,6 +541,9 @@ inline BatchSettings load_batch_config(const std::string& path) {
|
|||||||
preset.motion = cam["motion"].get<MotionConfig>();
|
preset.motion = cam["motion"].get<MotionConfig>();
|
||||||
preset.has_motion = true;
|
preset.has_motion = true;
|
||||||
}
|
}
|
||||||
|
if (cam.contains("detection")) {
|
||||||
|
preset.detection_overrides = cam["detection"];
|
||||||
|
}
|
||||||
|
|
||||||
settings.cameras[id] = std::move(preset);
|
settings.cameras[id] = std::move(preset);
|
||||||
}
|
}
|
||||||
@@ -561,6 +569,10 @@ inline CameraConfig build_camera_config_from_batch(
|
|||||||
if (!raw.contains("overlay")) raw["overlay"] = nlohmann::json::object();
|
if (!raw.contains("overlay")) raw["overlay"] = nlohmann::json::object();
|
||||||
raw["overlay"]["count_anchor"] = preset.count_anchor;
|
raw["overlay"]["count_anchor"] = preset.count_anchor;
|
||||||
}
|
}
|
||||||
|
if (!preset.detection_overrides.empty()) {
|
||||||
|
if (!raw.contains("detection")) raw["detection"] = nlohmann::json::object();
|
||||||
|
raw["detection"].update(preset.detection_overrides);
|
||||||
|
}
|
||||||
if (!raw.contains("roi")) raw["roi"] = nlohmann::json::object();
|
if (!raw.contains("roi")) raw["roi"] = nlohmann::json::object();
|
||||||
raw["roi"]["points"] = preset.roi.points;
|
raw["roi"]["points"] = preset.roi.points;
|
||||||
|
|
||||||
@@ -589,7 +601,7 @@ inline CameraConfig build_camera_config_from_batch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!raw.contains("display")) raw["display"] = nlohmann::json::object();
|
if (!raw.contains("display")) raw["display"] = nlohmann::json::object();
|
||||||
raw["display"]["output_path"] = output_path.empty() ? nlohmann::json(nullptr) : nlohmann::json(output_path);
|
if (!output_path.empty()) raw["display"]["output_path"] = output_path;
|
||||||
raw["display"]["show_window"] = false;
|
raw["display"]["show_window"] = false;
|
||||||
|
|
||||||
if (!raw.contains("feedback")) raw["feedback"] = nlohmann::json::object();
|
if (!raw.contains("feedback")) raw["feedback"] = nlohmann::json::object();
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ inline cv::Mat draw_overlay(
|
|||||||
if (config.overlay.inside_box_only && !inside_box) continue;
|
if (config.overlay.inside_box_only && !inside_box) continue;
|
||||||
|
|
||||||
bool validated = counting_zone.is_validated(track.track_id);
|
bool validated = counting_zone.is_validated(track.track_id);
|
||||||
|
if (config.overlay.validated_only && !validated) continue;
|
||||||
int x1 = track.bbox_x1, y1 = track.bbox_y1, x2 = track.bbox_x2, y2 = track.bbox_y2;
|
int x1 = track.bbox_x1, y1 = track.bbox_y1, x2 = track.bbox_x2, y2 = track.bbox_y2;
|
||||||
int cx = track.centroid_x, cy = track.centroid_y;
|
int cx = track.centroid_x, cy = track.centroid_y;
|
||||||
int seq = static_cast<int>(counting_zone.sequence_number_for(track.track_id));
|
int seq = static_cast<int>(counting_zone.sequence_number_for(track.track_id));
|
||||||
|
|||||||
@@ -119,8 +119,6 @@ public:
|
|||||||
|
|
||||||
std::string input_name, output_name;
|
std::string input_name, output_name;
|
||||||
size_t input_bytes = 0, output_bytes = 0;
|
size_t input_bytes = 0, output_bytes = 0;
|
||||||
int output_num_classes = 1, output_num_cells = 8400;
|
|
||||||
bool output_layout_packed = true; // true = [1,N,8400], false = [1,8400,N]
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Logger : nvinfer1::ILogger {
|
struct Logger : nvinfer1::ILogger {
|
||||||
@@ -158,25 +156,12 @@ private:
|
|||||||
input_name = name; input_bytes = bytes;
|
input_name = name; input_bytes = bytes;
|
||||||
} else {
|
} else {
|
||||||
output_name = name; output_bytes = bytes;
|
output_name = name; output_bytes = bytes;
|
||||||
// cache output layout once
|
|
||||||
int d1 = (shape.nbDims >= 2) ? shape.d[1] : 1;
|
|
||||||
int d2 = (shape.nbDims >= 3) ? shape.d[2] : 1;
|
|
||||||
if (d2 > d1) {
|
|
||||||
output_layout_packed = true; // [1, N, cells]
|
|
||||||
output_num_classes = std::max(1, d1 - 4);
|
|
||||||
output_num_cells = d2;
|
|
||||||
} else {
|
|
||||||
output_layout_packed = false; // [1, cells, N]
|
|
||||||
output_num_classes = std::max(1, d2 - 4);
|
|
||||||
output_num_cells = d1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stream.reset(new cudaStream_t{});
|
stream.reset(new cudaStream_t{});
|
||||||
trt_check(cudaStreamCreate(stream.get()));
|
trt_check(cudaStreamCreate(stream.get()));
|
||||||
std::cerr << "[trt] ready: in=" << input_name << " (" << input_bytes
|
std::cerr << "[trt] ready: in=" << input_name << " (" << input_bytes
|
||||||
<< "B) out=" << output_name << " (" << output_bytes
|
<< "B) out=" << output_name << " (" << output_bytes << "B)\n";
|
||||||
<< "B) cls=" << output_num_classes << " cells=" << output_num_cells << "\n";
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -311,52 +296,37 @@ private:
|
|||||||
return cv::dnn::blobFromImage(p, 1.0/255.0, {imgsz, imgsz}, cv::Scalar(), true, false);
|
return cv::dnn::blobFromImage(p, 1.0/255.0, {imgsz, imgsz}, cv::Scalar(), true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- decode (uses cache layout) ---
|
// --- decode: model outputs (1, 300, 6) = [x1,y1,x2,y2,conf,cls] in letterbox space ---
|
||||||
std::vector<cv::Rect2f> decode(float scale, int pad_x, int pad_y, int ow, int oh) {
|
std::vector<cv::Rect2f> decode(float scale, int pad_x, int pad_y, int ow, int oh) {
|
||||||
std::vector<cv::Rect> iboxes; iboxes.reserve(32);
|
std::vector<cv::Rect> iboxes; iboxes.reserve(32);
|
||||||
std::vector<float> scores; scores.reserve(32);
|
std::vector<float> scores; scores.reserve(32);
|
||||||
|
|
||||||
int nc = trt->output_num_classes;
|
|
||||||
int cells = trt->output_num_cells;
|
|
||||||
int stride = nc + 4; // total channels per cell
|
|
||||||
const float* d = output_buf;
|
const float* d = output_buf;
|
||||||
|
int stride = 6; // (x1,y1,x2,y2,conf,cls) per detection
|
||||||
|
|
||||||
if (trt->output_layout_packed) {
|
for (int i = 0; i < output_buf_size / stride; ++i) {
|
||||||
// layout [1, N, cells] — each channel is contiguous stride apart
|
const float* row = d + i * stride;
|
||||||
for (int i = 0; i < cells; ++i) {
|
float conf = row[4];
|
||||||
float maxc = 0;
|
if (conf < conf_thresh) continue;
|
||||||
for (int c = 0; c < nc; ++c) {
|
|
||||||
float v = d[(4 + c) * cells + i];
|
// boxes are in letterbox coordinates, scale back
|
||||||
if (v > maxc) maxc = v;
|
float x1 = (row[0] - pad_x) / scale;
|
||||||
}
|
float y1 = (row[1] - pad_y) / scale;
|
||||||
if (maxc < conf_thresh) continue;
|
float x2 = (row[2] - pad_x) / scale;
|
||||||
float cx = d[i], cy = d[cells + i], w = d[2*cells + i], h = d[3*cells + i];
|
float y2 = (row[3] - pad_y) / scale;
|
||||||
float x = (cx - pad_x) / scale, y = (cy - pad_y) / scale;
|
|
||||||
float bw = w / scale, bh = h / scale;
|
int ix1 = std::max(0, std::min(static_cast<int>(x1), ow));
|
||||||
int x1 = std::max(0, std::min(static_cast<int>(x - bw/2), ow));
|
int iy1 = std::max(0, std::min(static_cast<int>(y1), oh));
|
||||||
int y1 = std::max(0, std::min(static_cast<int>(y - bh/2), oh));
|
int ix2 = std::max(0, std::min(static_cast<int>(x2), ow));
|
||||||
int x2 = std::max(0, std::min(static_cast<int>(x + bw/2), ow));
|
int iy2 = std::max(0, std::min(static_cast<int>(y2), oh));
|
||||||
int y2 = std::max(0, std::min(static_cast<int>(y + bh/2), oh));
|
if (ix2 > ix1 && iy2 > iy1) {
|
||||||
if (x2 > x1 && y2 > y1) { iboxes.push_back({x1, y1, x2 - x1, y2 - y1}); scores.push_back(maxc); }
|
iboxes.push_back({ix1, iy1, ix2 - ix1, iy2 - iy1});
|
||||||
}
|
scores.push_back(conf);
|
||||||
} else {
|
|
||||||
// layout [1, cells, N] — contiguous per row
|
|
||||||
for (int i = 0; i < cells; ++i) {
|
|
||||||
const float* row = d + i * stride;
|
|
||||||
float maxc = 0;
|
|
||||||
for (int c = 0; c < nc; ++c) { float v = row[4 + c]; if (v > maxc) maxc = v; }
|
|
||||||
if (maxc < conf_thresh) continue;
|
|
||||||
float cx = row[0], cy = row[1], w = row[2], h = row[3];
|
|
||||||
float x = (cx - pad_x) / scale, y = (cy - pad_y) / scale;
|
|
||||||
int x1 = std::max(0, std::min(static_cast<int>(x - w/scale/2), ow));
|
|
||||||
int y1 = std::max(0, std::min(static_cast<int>(y - h/scale/2), oh));
|
|
||||||
int x2 = std::max(0, std::min(static_cast<int>(x + w/scale/2), ow));
|
|
||||||
int y2 = std::max(0, std::min(static_cast<int>(y + h/scale/2), oh));
|
|
||||||
if (x2 > x1 && y2 > y1) { iboxes.push_back({x1, y1, x2 - x1, y2 - y1}); scores.push_back(maxc); }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> idx; cv::dnn::NMSBoxes(iboxes, scores, conf_thresh, iou_thresh, idx);
|
std::vector<int> idx;
|
||||||
|
cv::dnn::NMSBoxes(iboxes, scores, conf_thresh, iou_thresh, idx);
|
||||||
std::vector<cv::Rect2f> out; out.reserve(idx.size());
|
std::vector<cv::Rect2f> out; out.reserve(idx.size());
|
||||||
for (int i : idx) out.push_back(iboxes[i]);
|
for (int i : idx) out.push_back(iboxes[i]);
|
||||||
return out;
|
return out;
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ class OverlayConfig:
|
|||||||
show_track_ring: bool = False
|
show_track_ring: bool = False
|
||||||
count_anchor: Point = (900, 120)
|
count_anchor: Point = (900, 120)
|
||||||
inside_box_only: bool = True
|
inside_box_only: bool = True
|
||||||
|
validated_only: bool = False
|
||||||
pending_blink: bool = True
|
pending_blink: bool = True
|
||||||
pending_colors: list[Color] = field(
|
pending_colors: list[Color] = field(
|
||||||
default_factory=lambda: [(255, 255, 0), (0, 255, 255)]
|
default_factory=lambda: [(255, 255, 0), (0, 255, 255)]
|
||||||
@@ -222,6 +223,7 @@ class CameraPreset:
|
|||||||
count_anchor: Point | None = None
|
count_anchor: Point | None = None
|
||||||
gate: GateConfig | None = None
|
gate: GateConfig | None = None
|
||||||
motion: MotionConfig | None = None
|
motion: MotionConfig | None = None
|
||||||
|
detection_overrides: dict[str, Any] | None = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -330,6 +332,7 @@ def load_batch_config(path: str | Path) -> BatchSettings:
|
|||||||
|
|
||||||
gate = GateConfig(**camera_raw["gate"]) if "gate" in camera_raw else None
|
gate = GateConfig(**camera_raw["gate"]) if "gate" in camera_raw else None
|
||||||
motion = MotionConfig(**camera_raw["motion"]) if "motion" in camera_raw else None
|
motion = MotionConfig(**camera_raw["motion"]) if "motion" in camera_raw else None
|
||||||
|
detection_overrides = camera_raw.get("detection", None)
|
||||||
|
|
||||||
cameras[camera_id] = CameraPreset(
|
cameras[camera_id] = CameraPreset(
|
||||||
camera_id=camera_id,
|
camera_id=camera_id,
|
||||||
@@ -338,6 +341,7 @@ def load_batch_config(path: str | Path) -> BatchSettings:
|
|||||||
count_anchor=count_anchor,
|
count_anchor=count_anchor,
|
||||||
gate=gate,
|
gate=gate,
|
||||||
motion=motion,
|
motion=motion,
|
||||||
|
detection_overrides=detection_overrides,
|
||||||
)
|
)
|
||||||
|
|
||||||
return BatchSettings(batch=batch, defaults=defaults, cameras=cameras)
|
return BatchSettings(batch=batch, defaults=defaults, cameras=cameras)
|
||||||
@@ -387,6 +391,10 @@ def build_camera_config_from_batch(
|
|||||||
raw.setdefault("overlay", {})
|
raw.setdefault("overlay", {})
|
||||||
raw["overlay"]["count_anchor"] = list(preset.count_anchor)
|
raw["overlay"]["count_anchor"] = list(preset.count_anchor)
|
||||||
|
|
||||||
|
if preset.detection_overrides is not None:
|
||||||
|
raw.setdefault("detection", {})
|
||||||
|
raw["detection"].update(preset.detection_overrides)
|
||||||
|
|
||||||
raw.setdefault("display", {})
|
raw.setdefault("display", {})
|
||||||
raw["display"]["output_path"] = str(output_path) if output_path is not None else None
|
raw["display"]["output_path"] = str(output_path) if output_path is not None else None
|
||||||
raw["display"]["show_window"] = False
|
raw["display"]["show_window"] = False
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ def draw_overlay(
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
validated = counting_zone.is_validated(track.track_id)
|
validated = counting_zone.is_validated(track.track_id)
|
||||||
|
if config.overlay.validated_only and not validated:
|
||||||
|
continue
|
||||||
x1, y1, x2, y2 = track.bbox_xyxy
|
x1, y1, x2, y2 = track.bbox_xyxy
|
||||||
cx, cy = track.centroid
|
cx, cy = track.centroid
|
||||||
sequence_number = counting_zone.sequence_number_for(track.track_id)
|
sequence_number = counting_zone.sequence_number_for(track.track_id)
|
||||||
|
|||||||
Reference in new issue
Block a user