#include #include #include #include #include #include #include #include "kp_struct.h" #include "model_type.h" #include "kdp2_inf_app_yolo.h" //#include "demo_customize_inf_single_model.h" #include "stdc_app.h" #include "kdp2_host_stream.h" #include "event_recorder.h" #include "can_bus.h" #include "bt_uart.h" //#define ENABLE_DBG_LOG #ifdef ENABLE_DBG_LOG #define dbg_log(__format__, ...) printf(""__format__, ##__VA_ARGS__) #else #define dbg_log(__format__, ...) #endif #define FDFR_MODEL_SIZE_ROW 90 #define FDFR_MODEL_SIZE_COL 90 #define MAX_RESULT_BOX 80 #define STDC_ROI_SEGMENTS 12 #define STDC_ROI_EDGE_THICKNESS 2 static bool _init_config_yolo_params = false; extern unsigned int g_dwDrawBoxEnable; extern unsigned int g_dwDrawBoxType; extern unsigned int g_verbose_log; /* ── In-place log trimmer ────────────────────────────────────────────────────── * Keeps /tmp/fw.log under 2 MB by overwriting in-place via STDOUT_FILENO. * Works only when stdout is redirected to a file (nohup ... > /tmp/fw.log). */ #define LOG_MAX_BYTES (2u * 1024u * 1024u) #define LOG_KEEP_BYTES (1u * 1024u * 1024u) static void log_trim_if_needed(void) { off_t sz = lseek(STDOUT_FILENO, 0, SEEK_END); if (sz < (off_t)LOG_MAX_BYTES) return; uint8_t *buf = (uint8_t *)malloc(LOG_KEEP_BYTES); if (!buf) return; fflush(stdout); off_t start = sz - (off_t)LOG_KEEP_BYTES; ssize_t got = pread(STDOUT_FILENO, buf, LOG_KEEP_BYTES, start); if (got > 0) { lseek(STDOUT_FILENO, 0, SEEK_SET); write(STDOUT_FILENO, buf, (size_t)got); ftruncate(STDOUT_FILENO, (off_t)got); lseek(STDOUT_FILENO, 0, SEEK_END); printf("[LOG] trimmed to %u KB\n", LOG_KEEP_BYTES / 1024); fflush(stdout); } free(buf); } static kp_app_yolo_post_proc_config_t post_proc_params_v5s = { .prob_thresh = 0.15, .nms_thresh = 0.5, .max_detection_per_class = 100, .anchor_row = 3, .anchor_col = 6, .stride_size = 3, .reserved_size = 0, .data = { // anchors[3][6] 10, 13, 16, 30, 33, 23, 30, 61, 62, 45, 59, 119, 116, 90, 156, 198, 373, 326, // strides[3] 8, 16, 32, }, }; static kp_app_yolo_post_proc_config_t post_proc_params_v5_vd = { .prob_thresh = 0.15, .nms_thresh = 0.5, .max_detection_per_class = 100, .anchor_row = 3, .anchor_col = 6, .stride_size = 3, .reserved_size = 0, .data = { // anchors[3][6] 7, 8, 16, 12, 11, 26, 27, 20, 48, 36, 28, 69, 95, 62, 70, 172, 184, 242, // strides[3] 8, 16, 32, }, }; static void print_yolo_result(kp_app_yolo_result_t *yolo_data) { unsigned int i = 0; static unsigned int dwResultCounts = 0; if (dwResultCounts > MAX_RESULT_BOX) dwResultCounts = MAX_RESULT_BOX; #if 0 if (g_dwDrawBoxEnable) { for (i = 0 ; i < dwResultCounts; i++) { if (g_atDrawInfo[i].bDrawFlag && !g_dwDrawBoxType) setup_isp_privacy_mask(0, g_atDrawInfo[i].dwStartX, g_atDrawInfo[i].dwStartY, g_atDrawInfo[i].dwWidth, g_atDrawInfo[i].dwHeight); } memset(&g_atDrawInfo, 0, sizeof(g_atDrawInfo)); dwResultCounts = yolo_data->box_count; g_dwResultCounts = 0; for (i = 0; i < yolo_data->box_count; i++) { if (g_dwOnlyPerson) { if (yolo_data->boxes[i].class_num != 0 && yolo_data->boxes[i].class_num != 2) continue; } calculate_bbox_postion(&g_atDrawInfo[i], yolo_data->boxes[i].x1, yolo_data->boxes[i].y1, yolo_data->boxes[i].x2, yolo_data->boxes[i].y2, yolo_data->boxes[i].score, yolo_data->boxes[i].class_num); g_atDrawInfo[i].bDrawFlag = true; if(!g_dwDrawBoxType) { setup_isp_privacy_mask(1, g_atDrawInfo[i].dwStartX, g_atDrawInfo[i].dwStartY, g_atDrawInfo[i].dwWidth, g_atDrawInfo[i].dwHeight); } g_dwResultCounts++; } if(g_dwResultCounts > MAX_RESULT_BOX) { g_dwResultCounts = MAX_RESULT_BOX; } } for (i = 0; i < yolo_data->box_count; i++) { if (g_dwOnlyPerson) { if (yolo_data->boxes[i].class_num != 0 && yolo_data->boxes[i].class_num != 2) continue; } printf("[%s][%d] [AI coordinate] Count = %d x1 = %f y1 = %f x2 = %f y2 = %f score = %f class_num = %d\n", __func__, i, yolo_data->box_count, yolo_data->boxes[i].x1, yolo_data->boxes[i].y1, yolo_data->boxes[i].x2, yolo_data->boxes[i].y2, yolo_data->boxes[i].score, yolo_data->boxes[i].class_num); } #else if (g_test_mode) return; /* ── [YOLO] collision_warning(2.2.5)───────────────────────────── * 偵測到 vehicle(class=2) 時送 BLE notify。 * 狀態有變化(進入/離開)才送,debounce 2 秒,避免頻繁發送。 * 將 #else 改為 #if 1 可啟用原有僅列印的邏輯。 * ---------------------------------------------------------------- */ { static int s_cw_level = 0; static char s_cw_type[16] = ""; static struct timeval s_cw_last_sent = {0, 0}; int cur_level = 0; char cur_type[16] = ""; if (g_dwDrawBoxEnable) { for (i = 0 ; i < dwResultCounts; i++) { if (g_atDrawInfo[i].bDrawFlag && !g_dwDrawBoxType) setup_isp_privacy_mask(0, g_atDrawInfo[i].dwStartX, g_atDrawInfo[i].dwStartY, g_atDrawInfo[i].dwWidth, g_atDrawInfo[i].dwHeight); } memset(&g_atDrawInfo, 0, sizeof(g_atDrawInfo)); dwResultCounts = yolo_data->box_count; g_dwResultCounts = 0; for (i = 0; i < yolo_data->box_count; i++) { if (g_dwOnlyPerson) { if (yolo_data->boxes[i].class_num != 0 && yolo_data->boxes[i].class_num != 2) continue; } calculate_bbox_postion(&g_atDrawInfo[i], yolo_data->boxes[i].x1, yolo_data->boxes[i].y1, yolo_data->boxes[i].x2, yolo_data->boxes[i].y2, yolo_data->boxes[i].score, yolo_data->boxes[i].class_num); g_atDrawInfo[i].bDrawFlag = true; if(!g_dwDrawBoxType) { setup_isp_privacy_mask(1, g_atDrawInfo[i].dwStartX, g_atDrawInfo[i].dwStartY, g_atDrawInfo[i].dwWidth, g_atDrawInfo[i].dwHeight); } g_dwResultCounts++; } if(g_dwResultCounts > MAX_RESULT_BOX) { g_dwResultCounts = MAX_RESULT_BOX; } } for (i = 0; i < yolo_data->box_count; i++) { int cls = (int)yolo_data->boxes[i].class_num; if (cls != 2) continue; /* 只看 vehicle(2) */ cur_level = 1; snprintf(cur_type, sizeof(cur_type), "vehicle"); break; /* 找到一輛車就夠了 */ } /* ── [CAN] vehicle speed control + BLE status notify ──────────── * 偵測到 vehicle(class=2):限速 10,並透過 BLE 通知目前 CAN 狀態。 * 無 vehicle:恢復正常速度 240,並通知解除。 * 只在狀態改變時呼叫,keepalive thread 會持續維持最新速度。 * ---------------------------------------------------------------- */ { static int s_can_vehicle_detected = -1; /* -1 = 未初始化 */ int vehicle_now = (cur_level == 1) ? 1 : 0; if (vehicle_now != s_can_vehicle_detected) { uint8_t speed = vehicle_now ? 10 : 240; /* 送 CAN 速度指令 */ { can_ctrl_cmd_t ctrl = { .speed = speed, .led_enable = 0, .led_r = 0, .led_l = 0, .keepalive_interval_ms = 0 }; can_bus_send_control_cmd(&ctrl); } /* 透過 BLE 通知目前 CAN bus 狀態 */ { char json[128]; snprintf(json, sizeof(json), "{\"response_type\":\"can_status\"," "\"content\":{" "\"vehicle_detected\":%d," "\"speed_cmd\":%u}}", vehicle_now, (unsigned)speed); bt_uart_send_json(json); } printf("[YOLO] CAN speed cmd: %u (%s)\n", speed, vehicle_now ? "vehicle detected" : "clear"); s_can_vehicle_detected = vehicle_now; } } /* 狀態有變化時,檢查 debounce */ if (cur_level != s_cw_level || strcmp(cur_type, s_cw_type) != 0) { struct timeval now; gettimeofday(&now, NULL); long elapsed_ms = (now.tv_sec - s_cw_last_sent.tv_sec) * 1000 + (now.tv_usec - s_cw_last_sent.tv_usec) / 1000; if (s_cw_last_sent.tv_sec == 0 || elapsed_ms >= 2000) { fire_collision_warning(cur_level, cur_level ? cur_type : NULL); s_cw_level = cur_level; snprintf(s_cw_type, sizeof(s_cw_type), "%s", cur_type); s_cw_last_sent = now; printf("[YOLO] collision_warning sent level=%d type=%s\n", cur_level, cur_level ? cur_type : "null"); } else { printf("[YOLO] collision_warning debounce bypass (%ldms < 5000ms)\n", elapsed_ms); } } } #endif } static unsigned int stdc_u32_lerp(unsigned int a, unsigned int b, unsigned int i, unsigned int n) { int64_t diff; int64_t v; if (n == 0) return a; diff = (int64_t)b - (int64_t)a; v = (int64_t)a + (diff * (int64_t)i) / (int64_t)n; if (v < 0) return 0; return (unsigned int)v; } static void stdc_add_rect(DETECT_INFO *di, unsigned int *cnt, unsigned int x, unsigned int y, unsigned int w, unsigned int h) { if (*cnt >= MAX_RESULT_BOX || w == 0 || h == 0) return; di[*cnt].dwStartX = x; di[*cnt].dwStartY = y; di[*cnt].dwWidth = w; di[*cnt].dwHeight = h; di[*cnt].fScore = 0; di[*cnt].dwClass = 0; di[*cnt].bDrawFlag = true; (*cnt)++; } static void stdc_add_trapezoid_outline(DETECT_INFO *di, unsigned int *cnt, unsigned int x_tl, unsigned int x_tr, unsigned int x_bl, unsigned int x_br, unsigned int y_top, unsigned int y_bottom) { /* Top and bottom edges */ if (x_tr > x_tl) { stdc_add_rect(di, cnt, x_tl, y_top, x_tr - x_tl, STDC_ROI_EDGE_THICKNESS); } if (x_br > x_bl) { stdc_add_rect(di, cnt, x_bl, y_bottom, x_br - x_bl, STDC_ROI_EDGE_THICKNESS); } /* Left and right slanted edges as sampled points */ for (unsigned int i = 0; i <= STDC_ROI_SEGMENTS; i++) { unsigned int y = stdc_u32_lerp(y_top, y_bottom, i, STDC_ROI_SEGMENTS); unsigned int xl = stdc_u32_lerp(x_tl, x_bl, i, STDC_ROI_SEGMENTS); unsigned int xr = stdc_u32_lerp(x_tr, x_br, i, STDC_ROI_SEGMENTS); stdc_add_rect(di, cnt, xl, y, STDC_ROI_EDGE_THICKNESS, STDC_ROI_EDGE_THICKNESS); stdc_add_rect(di, cnt, xr, y, STDC_ROI_EDGE_THICKNESS, STDC_ROI_EDGE_THICKNESS); } } /**************************************************************** * application header/result callback function (Please override the callback functions for other application verify) ****************************************************************/ int app_header_send_inference(uint32_t buf_addr, bool *bl_run_next_inference, void* arg, unsigned char* image_buffer, VMF_VSRC_SSM_OUTPUT_INFO_T* vsrc_ssm_info) { HOST_STREAM_INIT_OPT_T* pInitOpt=(HOST_STREAM_INIT_OPT_T*)arg; unsigned int dwWidth = pInitOpt->tVEncoder[pInitOpt->dwInferenceStream].dwWidth; unsigned int dwHeight = pInitOpt->tVEncoder[pInitOpt->dwInferenceStream].dwHeight; int ret = 0; kp_image_format_t kp_image_format = KP_IMAGE_FORMAT_YUV420; //The default image format of host_stream/hico_mipi is KP_IMAGE_FORMAT_YUV420. if(pInitOpt->dwJobId == KDP2_INF_ID_APP_YOLO) { if (false == _init_config_yolo_params) { /**************************************************************** * configure application pre/post-processing params ****************************************************************/ _init_config_yolo_params = true; kdp2_ipc_app_yolo_post_proc_config_t *app_yolo_post_proc_config = (kdp2_ipc_app_yolo_post_proc_config_t *)buf_addr; kp_inference_header_stamp_t *header_stamp = &app_yolo_post_proc_config->header_stamp; header_stamp->magic_type = KDP2_MAGIC_TYPE_INFERENCE; header_stamp->total_size = sizeof(kdp2_ipc_app_yolo_post_proc_config_t); header_stamp->total_image = 1; header_stamp->image_index = 1; header_stamp->job_id = KDP2_JOB_ID_APP_YOLO_CONFIG_POST_PROC; app_yolo_post_proc_config->model_id = pInitOpt->dwModelId; app_yolo_post_proc_config->param_size = sizeof(kp_app_yolo_post_proc_config_t); app_yolo_post_proc_config->set_or_get = 1; switch (pInitOpt->dwModelId) { case KNERON_YOLOV5S_COCO80_640_640_3: case KNERON_YOLOV5S_PersonBottleChairPottedplant4_288_640_3: case KNERON_YOLOV5S_PersonBicycleCarMotorcycleBusTruckCatDog8_256_480_3: post_proc_params_v5s.prob_thresh = pInitOpt->fThreshold; memcpy((void *)app_yolo_post_proc_config->param_data, (void *)&post_proc_params_v5s, sizeof(kp_app_yolo_post_proc_config_t)); break; case KNERON_YOLOV5S0375_PersonBicycleCarMotorcycleBusTruck6_256_352_3: post_proc_params_v5_vd.prob_thresh = pInitOpt->fThreshold; memcpy((void *)app_yolo_post_proc_config->param_data, (void *)&post_proc_params_v5_vd, sizeof(kp_app_yolo_post_proc_config_t)); break; default: // cannot find matched post-proc config printf("%s, model_id = %d, cannot find matched post-proc config\n", __func__, pInitOpt->dwModelId); break; } return KP_SUCCESS; } kdp2_ipc_app_yolo_inf_header_t *app_yolo_header = (kdp2_ipc_app_yolo_inf_header_t *)buf_addr; kp_inference_header_stamp_t *header_stamp = &app_yolo_header->header_stamp; static int inf_number = 0; inf_number = inf_number+1; inf_number &= 65535; header_stamp->magic_type = KDP2_MAGIC_TYPE_INFERENCE; header_stamp->total_size = sizeof(kdp2_ipc_app_yolo_inf_header_t) + (uint32_t)(vsrc_ssm_info->dwWidth*vsrc_ssm_info->dwHeight*1.5); header_stamp->total_image = 1; header_stamp->image_index = 1; header_stamp->job_id = KDP2_INF_ID_APP_YOLO; app_yolo_header->inf_number = inf_number; app_yolo_header->model_id = pInitOpt->dwModelId; app_yolo_header->width = dwWidth; app_yolo_header->height = dwHeight; app_yolo_header->image_format = kp_image_format; app_yolo_header->model_normalize = KP_NORMALIZE_KNERON;//KP_NORMALIZE_DISABLE; //printf("[SEND] inf_number = %d \n", inf_number); //memcpy((void *)(buf_addr + sizeof(kdp2_ipc_app_yolo_inf_header_t)), image_buffer, image_buffer_size); ret = dma2d_copy(pInitOpt->ptDmaInfo->ptDmaHandle, pInitOpt->ptDmaInfo->ptDmaDesc, (void *)(buf_addr + sizeof(kdp2_ipc_app_yolo_inf_header_t)), (void*)image_buffer, vsrc_ssm_info); if(ret){ printf("dma2d_copy failed\n"); } //_inference_number++; /**************************************************************** * finish application - close send thead ****************************************************************/ //if (_loop_time <= _inference_number) // *bl_run_next_inference = false; } /* else if(pInitOpt->dwJobId == DEMO_KL630_CUSTOMIZE_INF_SINGLE_MODEL_JOB_ID) { demo_customize_inf_single_model_header_t *pAppSingle = (demo_customize_inf_single_model_header_t *)buf_addr; kp_inference_header_stamp_t *header_stamp = &pAppSingle->header_stamp; header_stamp->magic_type = KDP2_MAGIC_TYPE_INFERENCE; header_stamp->total_size = sizeof(demo_customize_inf_single_model_header_t) + (uint32_t)(vsrc_ssm_info->dwWidth*vsrc_ssm_info->dwHeight*1.5); header_stamp->total_image = 1; header_stamp->image_index = 0; header_stamp->job_id = DEMO_KL630_CUSTOMIZE_INF_SINGLE_MODEL_JOB_ID; pAppSingle->width = dwWidth; pAppSingle->height = dwHeight; //memcpy((void *)(buf_addr + sizeof(demo_customize_inf_single_model_header_t)), image_buffer, image_buffer_size); ret = dma2d_copy(pInitOpt->ptDmaInfo->ptDmaHandle, pInitOpt->ptDmaInfo->ptDmaDesc, (void *)(buf_addr + sizeof(demo_customize_inf_single_model_header_t)), (void*)image_buffer, vsrc_ssm_info); if(ret){ printf("dma2d_copy failed\n"); } } */ else if(pInitOpt->dwJobId == STDC_JOB_ID) { stdc_inf_header_t *pStdcHdr = (stdc_inf_header_t *)buf_addr; kp_inference_header_stamp_t *header_stamp = &pStdcHdr->header_stamp; header_stamp->magic_type = KDP2_MAGIC_TYPE_INFERENCE; header_stamp->total_size = sizeof(stdc_inf_header_t) + (uint32_t)(vsrc_ssm_info->dwWidth*vsrc_ssm_info->dwHeight*1.5); header_stamp->total_image = 1; header_stamp->image_index = 0; header_stamp->job_id = STDC_JOB_ID; pStdcHdr->width = dwWidth; pStdcHdr->height = dwHeight; pStdcHdr->model_id = pInitOpt->dwModelId; pStdcHdr->fps = pInitOpt->fFps; static int stdc_send_count = 0; if (++stdc_send_count % 30 == 1) printf("[STDC SEND] frame=%d %dx%d\n", stdc_send_count, dwWidth, dwHeight); ret = dma2d_copy(pInitOpt->ptDmaInfo->ptDmaHandle, pInitOpt->ptDmaInfo->ptDmaDesc, (void *)(buf_addr + sizeof(stdc_inf_header_t)), (void*)image_buffer, vsrc_ssm_info); if(ret){ printf("dma2d_copy failed\n"); } } else { printf("[%s] Error: Job ID %u\n", __FUNCTION__, pInitOpt->dwJobId); return KP_FW_ERROR_UNKNOWN_APP; } /* Take VMF_SNAP if event_recorder has a pending request */ event_recorder_provide_frame(); return KP_SUCCESS; } int app_header_recv_inference(uint32_t buf_addr, bool *bl_run_next_inference) { kp_inference_header_stamp_t *header_stamp = (kp_inference_header_stamp_t *)buf_addr; if (KDP2_INF_ID_APP_YOLO == header_stamp->job_id) { /**************************************************************** * receive & dump application result ****************************************************************/ if (KP_SUCCESS != header_stamp->status_code) { printf("[%s] Error: Run application fail %u\n", __func__, header_stamp->status_code); return header_stamp->status_code; } kdp2_ipc_app_yolo_result_t *app_yolo_result = (kdp2_ipc_app_yolo_result_t *)header_stamp; kp_app_yolo_result_t *yolo_data = &app_yolo_result->yolo_data; //printf("[RECV] inf_number = %d \n", app_yolo_result->inf_number); print_yolo_result(yolo_data); /**************************************************************** * finish application - close receive thead ****************************************************************/ //if ((_loop_time - 1) <= app_yolo_result->inf_number) // *bl_run_next_inference = false; } else if (KDP2_JOB_ID_APP_YOLO_CONFIG_POST_PROC == header_stamp->job_id) { /**************************************************************** * receive pre/post-processing configuration respones ****************************************************************/ if (KP_SUCCESS != header_stamp->status_code) { printf("[%s] Error: config post-processing params fail %u\n", __func__, header_stamp->status_code); return header_stamp->status_code; } printf("[%s] config post-processing params success.\n", __func__); } /* else if(DEMO_KL630_CUSTOMIZE_INF_SINGLE_MODEL_JOB_ID == header_stamp->job_id) { printf("[%s] Job ID %u\n", __FUNCTION__, header_stamp->job_id); } */ else if(STDC_JOB_ID == header_stamp->job_id) { if (KP_SUCCESS != header_stamp->status_code) { printf("[%s] STDC inference error %u\n", __func__, header_stamp->status_code); return header_stamp->status_code; } if (g_test_mode) return KP_SUCCESS; stdc_inf_result_t *stdc_res = (stdc_inf_result_t *)header_stamp; stdc_analysis_t *ana = &stdc_res->stdc_result.analysis; /* Copy seg map to shared globals for draw-box thread */ if (ana->seg_width > 0 && ana->seg_height > 0 && ana->seg_width * ana->seg_height <= STDC_SEG_MAP_MAX) { pthread_mutex_lock(&g_stdc_seg_mutex); memcpy(g_stdc_seg_map, stdc_res->stdc_result.seg_map, ana->seg_width * ana->seg_height); g_stdc_seg_w = ana->seg_width; g_stdc_seg_h = ana->seg_height; pthread_mutex_unlock(&g_stdc_seg_mutex); } /* ── 1. Console log — class ratios + status (at most once per second) ── */ { static struct timeval s_last_log = {0, 0}; struct timeval now; gettimeofday(&now, NULL); long elapsed_us = (now.tv_sec - s_last_log.tv_sec) * 1000000L + (now.tv_usec - s_last_log.tv_usec); if (elapsed_us >= 1000000L) { s_last_log = now; log_trim_if_needed(); /* keep /tmp/fw.log under LOG_MAX_BYTES */ if (g_verbose_log) { printf("[STDC] frame=%u mov=%d diff=%.1f " "bunker=%.1f%% car=%.1f%% grass=%.1f%% greenery=%.1f%% " "person=%.1f%% pond=%.1f%% road=%.1f%% tree=%.1f%%\n", ana->frame_count, ana->is_moving, ana->motion_diff, ana->class_ratio[STDC_CLASS_BUNKER] * 100.0f, ana->class_ratio[STDC_CLASS_CAR] * 100.0f, ana->class_ratio[STDC_CLASS_GRASS] * 100.0f, ana->class_ratio[STDC_CLASS_GREENERY] * 100.0f, ana->class_ratio[STDC_CLASS_PERSON] * 100.0f, ana->class_ratio[STDC_CLASS_POND] * 100.0f, ana->class_ratio[STDC_CLASS_ROAD] * 100.0f, ana->class_ratio[STDC_CLASS_TREE] * 100.0f); /* ── 2. Grass / motion state ── */ if (ana->on_grass) printf("[STDC] ON GRASS: %.1fs%s\n", ana->grass_duration_sec, ana->grass_warning ? " *** GRASS WARNING ***" : ""); else printf("[STDC] ON ROAD\n"); } } } /* ── 3. Warning log (on state change — matches Python sections H/I/J) ── */ { static int s_last_warn = -1; int warn_mask = (ana->collision_risk ? 0x001 : 0) | (ana->person_warning ? 0x002 : 0) | (ana->car_warning ? 0x004 : 0) | (ana->grass_warning ? 0x008 : 0) | (ana->tree_dense ? 0x010 : 0) | (ana->tree_approaching ? 0x020 : 0) | (ana->bunker_warning ? 0x040 : 0) | (ana->pond_warning ? 0x080 : 0) | (ana->greenery_warning ? 0x100 : 0); if (warn_mask != s_last_warn) { if (ana->collision_risk) printf("[STDC WARN] COLLISION RISK (person=%.1f%% car=%.1f%% " "tree=%.1f%% bunker=%.1f%% pond=%.1f%%)\n", ana->col_person_ratio*100, ana->col_car_ratio*100, ana->col_tree_ratio*100, ana->col_bunker_ratio*100, ana->col_pond_ratio*100); if (ana->person_warning) printf("[STDC WARN] PERSON %.1f%%\n", ana->class_ratio[STDC_CLASS_PERSON]*100); if (ana->car_warning) printf("[STDC WARN] CAR %.1f%%\n", ana->class_ratio[STDC_CLASS_CAR]*100); if (ana->greenery_warning) printf("[STDC WARN] GREENERY AREA %.1f%%\n", ana->class_ratio[STDC_CLASS_GREENERY]*100); if (ana->bunker_warning) printf("[STDC WARN] BUNKER %.1f%%\n", ana->class_ratio[STDC_CLASS_BUNKER]*100); if (ana->pond_warning) printf("[STDC WARN] POND %.1f%%\n", ana->class_ratio[STDC_CLASS_POND]*100); if (ana->tree_approaching) printf("[STDC WARN] TREE APPROACHING (roi_growth=%.1f%%)\n", ana->tree_roi_growth*100); if (ana->tree_dense) printf("[STDC WARN] TREE DENSE AREA %.1f%%\n", ana->class_ratio[STDC_CLASS_TREE]*100); s_last_warn = warn_mask; } } /* ── 4. Visual overlay via g_atDrawInfo (rendered by draw_box DMA thread) ── * * Requires DrawBoxEnable=1 in host_stream.ini / demo_rtsp.sh. * The draw_box thread calls draw_rect() for each entry 0..g_dwResultCounts-1. * * Shared ROI trapezoid (same as web overlay polygon): * points="45,55 55,55 70,95 30,95" * i.e. top 45%~55%, bottom 30%~70%, y 55%~95%. * * [top-left] Warning boxes 200×55px, stride 63px (when warning active): * y=10 collision_risk y=199 grass_warning * y=73 person_warning y=262 tree_dense * y=136 car_warning y=325 tree_approaching * * [right edge] Class presence 40×55px, stride 63px (when ratio > threshold): * ci=0 bunker y=10 (thr 1%) * ci=1 car y=73 (thr 5%) * ci=2 grass y=136 (thr 10%) * ci=3 greenery y=199 (thr 20%) * ci=4 person y=262 (thr 1%) * ci=5 pond y=325 (thr 1%) * ci=6 road y=388 (thr 50%) * ci=7 tree y=451 (thr 30%) */ if (g_dwDrawBoxEnable) { unsigned int cnt = 0; DETECT_INFO *di = g_atDrawInfo; const unsigned int draw_w = 1920u; const unsigned int draw_h = 1080u; unsigned int x_tl = (draw_w * 45u) / 100u; unsigned int x_tr = (draw_w * 55u) / 100u; unsigned int x_bl = (draw_w * 30u) / 100u; unsigned int x_br = (draw_w * 70u) / 100u; unsigned int y_top = (draw_h * 55u) / 100u; unsigned int y_bottom = (draw_h * 95u) / 100u; stdc_add_trapezoid_outline(di, &cnt, x_tl, x_tr, x_bl, x_br, y_top, y_bottom); /* Collision emphasis: draw a second inner trapezoid when active. */ if (ana->collision_risk && x_tr > x_tl + 8u && x_br > x_bl + 16u && y_bottom > y_top + 8u) { stdc_add_trapezoid_outline(di, &cnt, x_tl + 4u, x_tr - 4u, x_bl + 8u, x_br - 8u, y_top + 4u, y_bottom - 4u); } /* Side warning/class panels are intentionally disabled. * Keep only central ROI overlays to avoid per-class side boxes. */ g_dwResultCounts = cnt; } /* Drive violation event state machine */ event_recorder_update(ana); } else { printf("[%s] Error: Job ID %u\n", __FUNCTION__, header_stamp->job_id); return KP_FW_ERROR_UNKNOWN_APP; } return KP_SUCCESS; }