diff --git a/agent/internal/discovery/discovery_test.go b/agent/internal/discovery/discovery_test.go index dad5bf3..944144a 100644 --- a/agent/internal/discovery/discovery_test.go +++ b/agent/internal/discovery/discovery_test.go @@ -13,7 +13,7 @@ import ( func TestResponderIncludesConfigSummaryInReply(t *testing.T) { dir := t.TempDir() cfgPath := filepath.Join(dir, "media-server.json") - body := []byte(`{"metadata":{"config_id":"cfg-1","config_version":"v1","business_name":"A厂区视觉识别","template":"workshop_face_shoe_alarm","profile":"local_3588_test","overlays":["face_debug"],"instance_names":["cam1"],"instance_display_names":["东门入口"]},"instances":[]}`) + body := []byte(`{"metadata":{"config_id":"cfg-1","config_version":"v1","business_name":"A厂区视觉识别","template":"std_workshop_face_recognition_shoe_alarm","profile":"local_3588_test","overlays":["face_debug"],"instance_names":["cam1"],"instance_display_names":["东门入口"]},"instances":[]}`) if err := os.WriteFile(cfgPath, body, 0o644); err != nil { t.Fatalf("write config: %v", err) } @@ -71,7 +71,7 @@ func TestResponderIncludesConfigSummaryInReply(t *testing.T) { if reply["config_id"] != "cfg-1" { t.Fatalf("config_id = %#v", reply["config_id"]) } - if reply["template"] != "workshop_face_shoe_alarm" { + if reply["template"] != "std_workshop_face_recognition_shoe_alarm" { t.Fatalf("template = %#v", reply["template"]) } if reply["profile"] != "local_3588_test" { diff --git a/agent/internal/httpapi/config_status_test.go b/agent/internal/httpapi/config_status_test.go index b42dfed..abd351b 100644 --- a/agent/internal/httpapi/config_status_test.go +++ b/agent/internal/httpapi/config_status_test.go @@ -46,7 +46,7 @@ func (f fakeProcessController) RollbackBinary(string) (procctl.BinaryUpdateResul func TestHandleConfigStatusReportsMetadataHashAndMediaStatus(t *testing.T) { dir := t.TempDir() cfgPath := filepath.Join(dir, "config.json") - body := []byte(`{"metadata":{"config_id":"cfg-1","config_version":"v1","business_name":"A厂区视觉识别","template":"workshop_face_shoe_alarm","profile":"local_3588_test","overlays":["face_debug"],"instance_names":["cam1"],"instance_display_names":["东门入口"]},"instances":[]}`) + body := []byte(`{"metadata":{"config_id":"cfg-1","config_version":"v1","business_name":"A厂区视觉识别","template":"std_workshop_face_recognition_shoe_alarm","profile":"local_3588_test","overlays":["face_debug"],"instance_names":["cam1"],"instance_display_names":["东门入口"]},"instances":[]}`) if err := os.WriteFile(cfgPath, body, 0o644); err != nil { t.Fatalf("write config: %v", err) } @@ -85,7 +85,7 @@ func TestHandleConfigStatusReportsMetadataHashAndMediaStatus(t *testing.T) { if metadata["config_id"] != "cfg-1" || metadata["config_version"] != "v1" { t.Fatalf("metadata = %#v", metadata) } - if got["template"] != "workshop_face_shoe_alarm" { + if got["template"] != "std_workshop_face_recognition_shoe_alarm" { t.Fatalf("template = %#v", got["template"]) } if got["profile"] != "local_3588_test" { @@ -122,7 +122,7 @@ func TestHandleConfigStatusReportsMetadataHashAndMediaStatus(t *testing.T) { func TestHandleInfoIncludesCurrentConfigSummary(t *testing.T) { dir := t.TempDir() cfgPath := filepath.Join(dir, "config.json") - body := []byte(`{"metadata":{"config_id":"cfg-1","config_version":"v1","business_name":"A厂区视觉识别","template":"workshop_face_shoe_alarm","profile":"local_3588_test","overlays":["face_debug","production_quiet"],"instance_names":["cam1"],"instance_display_names":["东门入口"]},"instances":[]}`) + body := []byte(`{"metadata":{"config_id":"cfg-1","config_version":"v1","business_name":"A厂区视觉识别","template":"std_workshop_face_recognition_shoe_alarm","profile":"local_3588_test","overlays":["face_debug","production_quiet"],"instance_names":["cam1"],"instance_display_names":["东门入口"]},"instances":[]}`) if err := os.WriteFile(cfgPath, body, 0o644); err != nil { t.Fatalf("write config: %v", err) } @@ -159,7 +159,7 @@ func TestHandleInfoIncludesCurrentConfigSummary(t *testing.T) { if got["config_version"] != "v1" { t.Fatalf("config_version = %#v", got["config_version"]) } - if got["template"] != "workshop_face_shoe_alarm" { + if got["template"] != "std_workshop_face_recognition_shoe_alarm" { t.Fatalf("template = %#v", got["template"]) } if got["profile"] != "local_3588_test" { diff --git a/configs/overlays/face_debug.json b/configs/overlays/face_debug.json index 31a1b32..3677e7f 100644 --- a/configs/overlays/face_debug.json +++ b/configs/overlays/face_debug.json @@ -4,14 +4,14 @@ "*": { "override": { "nodes": { - "face_recog": { + "recognize_face": { "debug": { "enabled": true, "log_matches": true, "min_log_interval_ms": 0 } }, - "alarm": { + "alarm_violation": { "face_debug": { "log_unknown_candidates": true, "unknown_candidate_interval_ms": 0 diff --git a/configs/overlays/production_quiet.json b/configs/overlays/production_quiet.json index e048593..984a3ef 100644 --- a/configs/overlays/production_quiet.json +++ b/configs/overlays/production_quiet.json @@ -4,32 +4,32 @@ "*": { "override": { "nodes": { - "face_recog": { + "recognize_face": { "debug": { "enabled": false, "log_matches": false } }, - "alarm": { + "alarm_violation": { "face_debug": { "log_unknown_candidates": false } }, - "person_det": { + "detect_person": { "debug": { "stats": false, "detections": false } }, - "face_det": { + "detect_face": { "debug": { "stats": false } }, - "shoe_assoc": { + "rule_shoe_association": { "debug": false }, - "shoe_color": { + "rule_shoe_color": { "debug": false } } diff --git a/configs/profiles/local_3588_test.json b/configs/profiles/local_3588_test.json index 712c062..8a14d34 100644 --- a/configs/profiles/local_3588_test.json +++ b/configs/profiles/local_3588_test.json @@ -14,7 +14,7 @@ "rtsp_url": "rtsp://10.0.0.49:8554/cam", "site_name": "B厂区" }, - "template": "workshop_face_shoe_alarm" + "template": "std_workshop_face_recognition_shoe_alarm" } ], "name": "local_3588_test", diff --git a/configs/templates/std_face_recognition_stream.json b/configs/templates/std_face_recognition_stream.json new file mode 100644 index 0000000..a62064f --- /dev/null +++ b/configs/templates/std_face_recognition_stream.json @@ -0,0 +1,206 @@ +{ + "name": "std_face_recognition_stream", + "description": "1080p 人脸识别流程,包含滑窗人脸检测、人脸识别、画面叠加与视频发布。", + "source": "configs/test_scrfd_640_recog.json", + "params": {}, + "template": { + "executor": { + "batch_size": 2, + "run_budget": 6 + }, + "nodes": [ + { + "id": "input_rtsp_main", + "type": "input_rtsp", + "role": "source", + "enable": true, + "url": "${rtsp_url}", + "fps": 30, + "width": 1920, + "height": 1080, + "use_ffmpeg": true, + "use_mpp": false, + "force_tcp": true, + "reconnect_sec": 5, + "reconnect_backoff_max_sec": 30 + }, + { + "id": "preprocess_rgb", + "type": "preprocess", + "role": "filter", + "enable": true, + "cpu_affinity": [ + 2 + ], + "dst_w": 1920, + "dst_h": 1080, + "dst_format": "rgb", + "dst_packed": true, + "resize_mode": "stretch", + "keep_ratio": false, + "rga_gate": "face_recognition_pipeline_rga", + "use_rga": true + }, + { + "id": "detect_face", + "type": "ai_scrfd_sliding", + "role": "filter", + "enable": true, + "cpu_affinity": [ + 4 + ], + "infer_fps": 3, + "model_path": "./models/face_det_scrfd_500m_640_rk3588.rknn", + "model_w": 640, + "model_h": 640, + "windows": [ + { + "x": 0, + "y": 0, + "w": 960, + "h": 1080 + }, + { + "x": 960, + "y": 0, + "w": 960, + "h": 1080 + } + ], + "conf_thresh": 0.5, + "nms_thresh": 0.4, + "max_faces": 50, + "debug": { + "stats": false, + "stats_interval": 30 + } + }, + { + "id": "recognize_face", + "type": "ai_face_recog", + "role": "filter", + "enable": true, + "cpu_affinity": [ + 4 + ], + "infer_fps": 2, + "infer_phase_ms": 120, + "model_path": "./models/face_recog_mobilefacenet_arcface_112_rk3588.rknn", + "align": true, + "emit_embedding": false, + "max_faces": 50, + "person_class_id": 0, + "track_state_max_age_ms": 1000, + "input_format": "rgb", + "input_dtype": "uint8", + "threshold": { + "accept": 0.45, + "margin": 0.05 + }, + "gallery": { + "backend": "sqlite", + "path": "./models/face_gallery.db", + "load_on_start": true, + "dtype": "auto" + }, + "debug": { + "enabled": false, + "log_matches": false, + "min_log_interval_ms": 0 + } + }, + { + "id": "render_osd", + "type": "osd", + "role": "filter", + "enable": true, + "cpu_affinity": [ + 7 + ], + "draw_bbox": true, + "draw_text": true, + "draw_face_det": true, + "draw_face_recog": true, + "draw_face_bbox": true, + "line_width": 2.0, + "font_scale": 1.0, + "use_rga_bbox": false, + "labels": [] + }, + { + "id": "prepare_publish", + "type": "preprocess", + "role": "filter", + "enable": true, + "cpu_affinity": [ + 7 + ], + "dst_w": 1920, + "dst_h": 1080, + "dst_format": "nv12", + "resize_mode": "stretch", + "rga_gate": "face_recognition_pipeline_rga", + "use_rga": true + }, + { + "id": "publish_stream", + "type": "publish", + "role": "sink", + "enable": true, + "cpu_affinity": [ + 3 + ], + "queue": { + "size": 2, + "policy": "drop_oldest" + }, + "codec": "h264", + "fps": 30, + "gop": 60, + "bitrate_kbps": 4000, + "mpp_output_timeout_ms": 50, + "mpp_packet_wait_ms": 10, + "use_mpp": true, + "use_ffmpeg_mux": true, + "outputs": [ + { + "proto": "hls", + "path": "${publish_hls_path}", + "segment_sec": 2 + }, + { + "proto": "rtsp_server", + "port": "${publish_rtsp_port}", + "path": "${publish_rtsp_path}" + } + ] + } + ], + "edges": [ + [ + "input_rtsp_main", + "preprocess_rgb" + ], + [ + "preprocess_rgb", + "detect_face" + ], + [ + "detect_face", + "recognize_face" + ], + [ + "recognize_face", + "render_osd" + ], + [ + "render_osd", + "prepare_publish" + ], + [ + "prepare_publish", + "publish_stream" + ] + ] + } +} diff --git a/configs/templates/std_service_test_stream.json b/configs/templates/std_service_test_stream.json new file mode 100644 index 0000000..3e797fd --- /dev/null +++ b/configs/templates/std_service_test_stream.json @@ -0,0 +1,75 @@ +{ + "name": "std_service_test_stream", + "description": "最简服务可用性测试流程,用于验证视频解码、预处理、编码与发布链路是否正常。", + "source": "configs/test_face_only.json", + "params": {}, + "template": { + "executor": { + "batch_size": 1, + "run_budget": 3 + }, + "nodes": [ + { + "id": "input_rtsp_main", + "type": "input_rtsp", + "role": "source", + "enable": true, + "url": "${rtsp_url}", + "fps": 25, + "width": 1280, + "height": 720, + "use_ffmpeg": true, + "use_mpp": false, + "force_tcp": true, + "reconnect_sec": 5, + "reconnect_backoff_max_sec": 30 + }, + { + "id": "prepare_publish", + "type": "preprocess", + "role": "filter", + "enable": true, + "dst_w": 1280, + "dst_h": 720, + "dst_format": "nv12", + "resize_mode": "stretch", + "use_rga": true, + "rga_gate": "service_test_pipeline_rga" + }, + { + "id": "publish_stream", + "type": "publish", + "role": "sink", + "enable": true, + "codec": "h264", + "fps": 25, + "gop": 50, + "bitrate_kbps": 1500, + "use_mpp": true, + "use_ffmpeg_mux": true, + "outputs": [ + { + "proto": "hls", + "path": "${publish_hls_path}", + "segment_sec": 2 + }, + { + "proto": "rtsp_server", + "port": "${publish_rtsp_port}", + "path": "${publish_rtsp_path}" + } + ] + } + ], + "edges": [ + [ + "input_rtsp_main", + "prepare_publish" + ], + [ + "prepare_publish", + "publish_stream" + ] + ] + } +} diff --git a/configs/templates/std_workshoe_detection_stream.json b/configs/templates/std_workshoe_detection_stream.json new file mode 100644 index 0000000..97b32dd --- /dev/null +++ b/configs/templates/std_workshoe_detection_stream.json @@ -0,0 +1,294 @@ +{ + "name": "std_workshoe_detection_stream", + "description": "1080p 劳保鞋检测流程,包含人员检测、人员跟踪、劳保鞋规则判断、画面叠加与视频发布。", + "source": "configs/full_pipeline_1080p_test_alarm.json", + "params": {}, + "template": { + "executor": { + "batch_size": 2, + "run_budget": 6 + }, + "nodes": [ + { + "id": "input_rtsp_main", + "type": "input_rtsp", + "role": "source", + "enable": true, + "url": "${rtsp_url}", + "fps": 30, + "width": 1920, + "height": 1080, + "use_ffmpeg": true, + "use_mpp": false, + "force_tcp": true, + "reconnect_sec": 5, + "reconnect_backoff_max_sec": 30 + }, + { + "id": "preprocess_rgb", + "type": "preprocess", + "role": "filter", + "enable": true, + "cpu_affinity": [ + 2 + ], + "dst_w": 1920, + "dst_h": 1080, + "dst_format": "rgb", + "dst_packed": true, + "resize_mode": "stretch", + "keep_ratio": false, + "rga_gate": "workshoe_detection_pipeline_rga", + "use_rga": true + }, + { + "id": "detect_person", + "type": "ai_yolo", + "role": "filter", + "enable": true, + "cpu_affinity": [ + 5 + ], + "use_rga": true, + "rga_gate": "workshoe_detection_pipeline_rga", + "rga_max_inflight": 4, + "dst_packed": true, + "use_dma_input": true, + "infer_fps": 2, + "infer_phase_ms": 0, + "model_path": "./models/object_det_yolov8n_coco_640_rk3588.rknn", + "model_version": "v8", + "model_w": 640, + "model_h": 640, + "num_classes": 80, + "conf": 0.35, + "nms": 0.45, + "class_filter": [ + 0 + ], + "bbox_expand": { + "enable": true, + "class_id": 0, + "left": 0.06, + "right": 0.06, + "top": 0.04, + "bottom": 0.16 + }, + "debug": { + "stats": false, + "stats_interval": 30, + "detections": false + } + }, + { + "id": "track_person", + "type": "tracker", + "role": "filter", + "enable": true, + "cpu_affinity": [ + 5 + ], + "mode": "bytetrack_lite", + "per_class": true, + "track_classes": [ + 0 + ], + "ignore_classes": [], + "high_th": 0.55, + "low_th": 0.1, + "iou_th": 0.3, + "max_age_ms": 900, + "min_hits": 1, + "max_tracks": 128 + }, + { + "id": "detect_shoe", + "type": "ai_shoe_det", + "role": "filter", + "enable": true, + "cpu_affinity": [ + 6 + ], + "use_rga": true, + "rga_gate": "workshoe_detection_pipeline_rga", + "rga_max_inflight": 4, + "dst_packed": true, + "use_dma_input": false, + "infer_fps": 2, + "infer_phase_ms": 150, + "model_path": "./models/shoe_det_yolov8s_workshoe_640_rk3588.rknn", + "model_w": 640, + "model_h": 640, + "conf": 0.22, + "nms": 0.45, + "v8_box_format": "cxcywh", + "append_detections": true, + "dynamic_roi": { + "enable": true, + "person_class_id": 0, + "shoe_class_id": 1, + "debug_roi_class_id": -1, + "max_rois": 3, + "min_person_height": 60, + "max_box_area_ratio": 0.6, + "y_offset": 0.7, + "width_scale": 1.6, + "height_scale": 0.4 + } + }, + { + "id": "rule_shoe_association", + "type": "logic_gate", + "role": "filter", + "enable": true, + "cpu_affinity": [ + 6 + ], + "mode": "person_shoe_check", + "debug": false, + "person_shoe_check": { + "person_class": 0, + "shoe_class": 1, + "violation_class": 2, + "min_person_score": 0.3, + "min_shoe_score": 0.22, + "foot_region": { + "y_offset": 0.7, + "width_scale": 1.6, + "height_scale": 0.4 + }, + "min_shoe_height_ratio": 0.08, + "min_shoe_area_ratio": 0.012, + "max_shoe_height_ratio": 0.14, + "max_shoe_width_ratio": 0.38, + "max_shoe_area_ratio": 0.05, + "max_shoe_roi_width_ratio": 0.45, + "max_shoe_roi_height_ratio": 0.35, + "max_shoe_roi_area_ratio": 0.1 + } + }, + { + "id": "rule_shoe_color", + "type": "logic_gate", + "role": "filter", + "enable": true, + "cpu_affinity": [ + 6 + ], + "mode": "ppe_boots_check", + "anchor_class": 0, + "boots_class": 1, + "violation_class": 2, + "debug": false, + "color_check": { + "enable": true + } + }, + { + "id": "render_osd", + "type": "osd", + "role": "filter", + "enable": true, + "cpu_affinity": [ + 7 + ], + "draw_bbox": true, + "draw_text": true, + "line_width": 2.0, + "font_scale": 1.0, + "use_rga_bbox": false, + "labels": [ + "person", + "shoe", + "non_black_shoe" + ] + }, + { + "id": "prepare_publish", + "type": "preprocess", + "role": "filter", + "enable": true, + "cpu_affinity": [ + 7 + ], + "dst_w": 1920, + "dst_h": 1080, + "dst_format": "nv12", + "resize_mode": "stretch", + "rga_gate": "workshoe_detection_pipeline_rga", + "use_rga": true + }, + { + "id": "publish_stream", + "type": "publish", + "role": "sink", + "enable": true, + "cpu_affinity": [ + 3 + ], + "queue": { + "size": 2, + "policy": "drop_oldest" + }, + "codec": "h264", + "fps": 30, + "gop": 60, + "bitrate_kbps": 4000, + "mpp_output_timeout_ms": 50, + "mpp_packet_wait_ms": 10, + "use_mpp": true, + "use_ffmpeg_mux": true, + "outputs": [ + { + "proto": "hls", + "path": "${publish_hls_path}", + "segment_sec": 2 + }, + { + "proto": "rtsp_server", + "port": "${publish_rtsp_port}", + "path": "${publish_rtsp_path}" + } + ] + } + ], + "edges": [ + [ + "input_rtsp_main", + "preprocess_rgb" + ], + [ + "preprocess_rgb", + "detect_person" + ], + [ + "detect_person", + "track_person" + ], + [ + "track_person", + "detect_shoe" + ], + [ + "detect_shoe", + "rule_shoe_association" + ], + [ + "rule_shoe_association", + "rule_shoe_color" + ], + [ + "rule_shoe_color", + "render_osd" + ], + [ + "render_osd", + "prepare_publish" + ], + [ + "prepare_publish", + "publish_stream" + ] + ] + } +} diff --git a/configs/templates/workshop_face_shoe_alarm.json b/configs/templates/std_workshop_face_recognition_shoe_alarm.json similarity index 90% rename from configs/templates/workshop_face_shoe_alarm.json rename to configs/templates/std_workshop_face_recognition_shoe_alarm.json index 11ae70f..0c5d8b0 100644 --- a/configs/templates/workshop_face_shoe_alarm.json +++ b/configs/templates/std_workshop_face_recognition_shoe_alarm.json @@ -1,6 +1,6 @@ { - "name": "workshop_face_shoe_alarm", - "description": "Full 1080p workshop pipeline with face detection/recognition, person tracking, shoe detection, OSD, publishing, alarms, snapshots, clips, and external API upload.", + "name": "std_workshop_face_recognition_shoe_alarm", + "description": "1080p 车间全功能识别流程,包含人脸检测与识别、人员跟踪、劳保鞋检测、画面叠加、视频发布、告警上报以及抓拍存证。", "source": "configs/full_pipeline_1080p_test_alarm.json", "params": { "minio_endpoint": "http://10.0.0.49:9000", @@ -18,7 +18,7 @@ }, "nodes": [ { - "id": "in", + "id": "input_rtsp_main", "type": "input_rtsp", "role": "source", "enable": true, @@ -33,7 +33,7 @@ "reconnect_backoff_max_sec": 30 }, { - "id": "pre_rgb", + "id": "preprocess_rgb", "type": "preprocess", "role": "filter", "enable": true, @@ -50,7 +50,7 @@ "use_rga": true }, { - "id": "face_det", + "id": "detect_face", "type": "ai_scrfd_sliding", "role": "filter", "enable": true, @@ -84,7 +84,7 @@ } }, { - "id": "face_recog", + "id": "recognize_face", "type": "ai_face_recog", "role": "filter", "enable": true, @@ -118,7 +118,7 @@ } }, { - "id": "person_det", + "id": "detect_person", "type": "ai_yolo", "role": "filter", "enable": true, @@ -157,7 +157,7 @@ } }, { - "id": "person_trk", + "id": "track_person", "type": "tracker", "role": "filter", "enable": true, @@ -178,7 +178,7 @@ "max_tracks": 128 }, { - "id": "shoe_det", + "id": "detect_shoe", "type": "ai_shoe_det", "role": "filter", "enable": true, @@ -213,7 +213,7 @@ } }, { - "id": "shoe_assoc", + "id": "rule_shoe_association", "type": "logic_gate", "role": "filter", "enable": true, @@ -244,7 +244,7 @@ } }, { - "id": "shoe_color", + "id": "rule_shoe_color", "type": "logic_gate", "role": "filter", "enable": true, @@ -261,7 +261,7 @@ } }, { - "id": "pre_osd", + "id": "prepare_publish", "type": "preprocess", "role": "filter", "enable": true, @@ -276,7 +276,7 @@ "use_rga": true }, { - "id": "osd", + "id": "render_osd", "type": "osd", "role": "filter", "enable": true, @@ -298,7 +298,7 @@ ] }, { - "id": "publish", + "id": "publish_stream", "type": "publish", "role": "filter", "enable": true, @@ -331,7 +331,7 @@ ] }, { - "id": "alarm", + "id": "alarm_violation", "type": "alarm", "role": "sink", "enable": true, @@ -454,28 +454,28 @@ ], "edges": [ [ - "in", - "pre_rgb" + "input_rtsp_main", + "preprocess_rgb" ], [ - "pre_rgb", - "face_det" + "preprocess_rgb", + "detect_face" ], [ - "face_det", - "person_det" + "detect_face", + "detect_person" ], [ - "person_det", - "person_trk" + "detect_person", + "track_person" ], [ - "person_trk", - "face_recog" + "track_person", + "recognize_face" ], [ - "face_recog", - "shoe_det", + "recognize_face", + "detect_shoe", { "queue": { "size": 16, @@ -484,8 +484,8 @@ } ], [ - "shoe_det", - "shoe_assoc", + "detect_shoe", + "rule_shoe_association", { "queue": { "size": 16, @@ -494,8 +494,8 @@ } ], [ - "shoe_assoc", - "shoe_color", + "rule_shoe_association", + "rule_shoe_color", { "queue": { "size": 16, @@ -504,8 +504,8 @@ } ], [ - "shoe_color", - "osd", + "rule_shoe_color", + "render_osd", { "queue": { "size": 16, @@ -514,8 +514,8 @@ } ], [ - "osd", - "pre_osd", + "render_osd", + "prepare_publish", { "queue": { "size": 32, @@ -524,8 +524,8 @@ } ], [ - "pre_osd", - "publish", + "prepare_publish", + "publish_stream", { "queue": { "size": 64, @@ -534,8 +534,8 @@ } ], [ - "publish", - "alarm", + "publish_stream", + "alarm_violation", { "queue": { "size": 64,