To move to main

This commit is contained in:
proitlab committed 2026-07-22 11:58:27 +07:00
1 parent f34eaa3af4
commit be204b91cc
11 files changed
+186 -62

No files matched your search

+7 -4
View File
@@ -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
classes: [0]
ignored_classes: [1, 2]
conf: 0.45
conf: 0.55
iou: 0.55
imgsz: 640
device: "0"
min_box_area_px: 3000
min_box_area_px: 5000
validate_while_inside: true
detection_zone:
enabled: true
@@ -53,6 +53,7 @@ defaults:
show_track_ring: false
count_anchor: [780, 120]
inside_box_only: true
validated_only: true
pending_blink: true
pending_colors:
- [255, 255, 0]
@@ -67,7 +68,7 @@ defaults:
overlay_buffer_reuse: true
inference_stride: 2
stream:
enabled: false
enabled: true
shm_dir: /dev/shm
interval_frames: 5
feedback:
@@ -80,7 +81,7 @@ defaults:
inset_right_px: 60
inset_top_px: 0
inset_bottom_px: 0
min_overlap_ratio: 0.30
min_overlap_ratio: 0.35
cameras:
CC1:
@@ -95,6 +96,8 @@ cameras:
CC2:
camera_num: 2
count_anchor: [900, 120]
detection:
min_box_area_px: 3000
roi:
points:
- [20, 380]
+121
View File
@@ -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]
+2 -2
View File
@@ -1,6 +1,6 @@
tracker_type: botsort
track_high_thresh: 0.55
track_low_thresh: 0.1
track_high_thresh: 0.65
track_low_thresh: 0.3
new_track_thresh: 0.7
track_buffer: 75
match_thresh: 0.9
+7
View File
@@ -5,6 +5,11 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
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(nlohmann_json 3.0 REQUIRED)
find_package(yaml-cpp REQUIRED)
@@ -31,11 +36,13 @@ target_include_directories(chicken_counter_lib PUBLIC
/usr/include/aarch64-linux-gnu
)
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
src/main.cpp
)
target_link_libraries(chicken_counter_cli PRIVATE chicken_counter_lib)
target_link_options(chicken_counter_cli PRIVATE -Wl,--strip-all)
add_executable(test_config
tests/test_config.cpp
+16 -4
View File
@@ -284,6 +284,7 @@ struct OverlayConfig {
bool show_track_ring = false;
cv::Point2i count_anchor = {900, 120};
bool inside_box_only = true;
bool validated_only = false;
bool pending_blink = true;
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},
{"trail_length", c.trail_length}, {"show_center_marker", c.show_center_marker},
{"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}};
}
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.count_anchor = j.value("count_anchor", cv::Point2i{900, 120});
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_colors = j.value("pending_colors",
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) {
c.window_name = j.value("window_name", "Chicken Counter");
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.max_frames = j.value("max_frames", -1);
c.encoder = j.value("encoder", "auto");
@@ -455,9 +459,10 @@ struct CameraPreset {
std::string camera_id;
int camera_num;
RoiConfig roi;
cv::Point2i count_anchor = {-1, -1}; // (-1,-1) = not set
cv::Point2i count_anchor = {-1, -1};
GateConfig gate;
MotionConfig motion;
nlohmann::json detection_overrides;
bool has_gate = 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.has_motion = true;
}
if (cam.contains("detection")) {
preset.detection_overrides = cam["detection"];
}
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();
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();
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();
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;
if (!raw.contains("feedback")) raw["feedback"] = nlohmann::json::object();
+1
View File
@@ -108,6 +108,7 @@ inline cv::Mat draw_overlay(
if (config.overlay.inside_box_only && !inside_box) continue;
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 cx = track.centroid_x, cy = track.centroid_y;
int seq = static_cast<int>(counting_zone.sequence_number_for(track.track_id));
+22 -52
View File
@@ -119,8 +119,6 @@ public:
std::string input_name, output_name;
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:
struct Logger : nvinfer1::ILogger {
@@ -158,25 +156,12 @@ private:
input_name = name; input_bytes = bytes;
} else {
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{});
trt_check(cudaStreamCreate(stream.get()));
std::cerr << "[trt] ready: in=" << input_name << " (" << input_bytes
<< "B) out=" << output_name << " (" << output_bytes
<< "B) cls=" << output_num_classes << " cells=" << output_num_cells << "\n";
<< "B) out=" << output_name << " (" << output_bytes << "B)\n";
}
};
@@ -311,52 +296,37 @@ private:
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::Rect> iboxes; iboxes.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;
int stride = 6; // (x1,y1,x2,y2,conf,cls) per detection
if (trt->output_layout_packed) {
// layout [1, N, cells] — each channel is contiguous stride apart
for (int i = 0; i < cells; ++i) {
float maxc = 0;
for (int c = 0; c < nc; ++c) {
float v = d[(4 + c) * cells + i];
if (v > maxc) maxc = v;
}
if (maxc < conf_thresh) continue;
float cx = d[i], cy = d[cells + i], w = d[2*cells + i], h = d[3*cells + i];
float x = (cx - pad_x) / scale, y = (cy - pad_y) / scale;
float bw = w / scale, bh = h / scale;
int x1 = std::max(0, std::min(static_cast<int>(x - bw/2), ow));
int y1 = std::max(0, std::min(static_cast<int>(y - bh/2), oh));
int x2 = std::max(0, std::min(static_cast<int>(x + bw/2), ow));
int y2 = std::max(0, std::min(static_cast<int>(y + bh/2), oh));
if (x2 > x1 && y2 > y1) { iboxes.push_back({x1, y1, x2 - x1, y2 - y1}); scores.push_back(maxc); }
}
} else {
// layout [1, cells, N] — contiguous per row
for (int i = 0; i < cells; ++i) {
for (int i = 0; i < output_buf_size / stride; ++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); }
float conf = row[4];
if (conf < conf_thresh) continue;
// boxes are in letterbox coordinates, scale back
float x1 = (row[0] - pad_x) / scale;
float y1 = (row[1] - pad_y) / scale;
float x2 = (row[2] - pad_x) / scale;
float y2 = (row[3] - pad_y) / scale;
int ix1 = std::max(0, std::min(static_cast<int>(x1), ow));
int iy1 = std::max(0, std::min(static_cast<int>(y1), oh));
int ix2 = std::max(0, std::min(static_cast<int>(x2), ow));
int iy2 = std::max(0, std::min(static_cast<int>(y2), oh));
if (ix2 > ix1 && iy2 > iy1) {
iboxes.push_back({ix1, iy1, ix2 - ix1, iy2 - iy1});
scores.push_back(conf);
}
}
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());
for (int i : idx) out.push_back(iboxes[i]);
return out;
+8
View File
@@ -145,6 +145,7 @@ class OverlayConfig:
show_track_ring: bool = False
count_anchor: Point = (900, 120)
inside_box_only: bool = True
validated_only: bool = False
pending_blink: bool = True
pending_colors: list[Color] = field(
default_factory=lambda: [(255, 255, 0), (0, 255, 255)]
@@ -222,6 +223,7 @@ class CameraPreset:
count_anchor: Point | None = None
gate: GateConfig | None = None
motion: MotionConfig | None = None
detection_overrides: dict[str, Any] | None = None
@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
motion = MotionConfig(**camera_raw["motion"]) if "motion" in camera_raw else None
detection_overrides = camera_raw.get("detection", None)
cameras[camera_id] = CameraPreset(
camera_id=camera_id,
@@ -338,6 +341,7 @@ def load_batch_config(path: str | Path) -> BatchSettings:
count_anchor=count_anchor,
gate=gate,
motion=motion,
detection_overrides=detection_overrides,
)
return BatchSettings(batch=batch, defaults=defaults, cameras=cameras)
@@ -387,6 +391,10 @@ def build_camera_config_from_batch(
raw.setdefault("overlay", {})
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["display"]["output_path"] = str(output_path) if output_path is not None else None
raw["display"]["show_window"] = False
+2
View File
@@ -52,6 +52,8 @@ def draw_overlay(
continue
validated = counting_zone.is_validated(track.track_id)
if config.overlay.validated_only and not validated:
continue
x1, y1, x2, y2 = track.bbox_xyxy
cx, cy = track.centroid
sequence_number = counting_zone.sequence_number_for(track.track_id)