diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d6d9256 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Build artifacts +build/*.o +build/kp_firmware_host_stream +build/*.nef + +# Python +__pycache__/ +*.pyc +*.pyo + +# Editor +.vscode/ diff --git a/build_and_serve.py b/build_and_serve.py index 96fb4c4..d2a7bd9 100644 --- a/build_and_serve.py +++ b/build_and_serve.py @@ -284,51 +284,4 @@ def main(): parser.add_argument("--no-copy",action="store_true", help="不要複製 binary 到網路資料夾") parser.add_argument("--no-serve", action="store_true", - help="只 build + check,不啟動 HTTP server") - parser.add_argument("--port", type=int, default=8080, - help="HTTP server port (default: 8080)") - parser.add_argument("--image", default=DOCKER_IMAGE, - help=f"Docker image name (default: {DOCKER_IMAGE})") - args = parser.parse_args() - - - DOCKER_IMAGE = args.image - - print(head("KL630 Build & Serve")) - print(f" Root : {SCRIPT_DIR}") - print(f" Binary : {BINARY_PATH}") - print(f" Port : {args.port}") - print(f" Copy : {args.copy_dst}") - - # ===== Clean only ===== - if args.clean: - step_clean() - sys.exit(0) - - # ===== Clean then build ===== - if args.clean_build: - step_clean() - - # ===== Step 1: Build ===== - if not args.no_build: - if not step_ensure_image(): - sys.exit(1) - if not step_build(): - sys.exit(1) - # ===== Step 2: Check ===== - if not args.no_check: - if not step_check(): - if args.no_build: - print(warn(" (使用 --no-build 時 binary 可能是舊的)")) - else: - sys.exit(1) - # ===== Step 3: Copy(重點)===== - if not args.no_copy: - if not step_copy_to_network(args.copy_dst): - sys.exit(1) - # ===== Step 4: Serve ===== - if not args.no_serve: - step_serve(args.port) - -if __name__ == "__main__": - main() + \ No newline at end of file diff --git a/src/host_stream/app_header_init.c b/src/host_stream/app_header_init.c index 424752f..8391083 100644 --- a/src/host_stream/app_header_init.c +++ b/src/host_stream/app_header_init.c @@ -1,625 +1,604 @@ -#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" - -//#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 - /* ── [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; /* 找到一輛車就夠了 */ - } - - /* 狀態有變化時,檢查 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; - } - 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; -} - +#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" + +//#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 + /* ── [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; /* 找到一輛車就夠了 */ + } + + /* 狀態有變化時,檢查 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; + } + 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, \ No newline at end of file diff --git a/src/host_stream/bt_uart.c b/src/host_stream/bt_uart.c index d240868..91f5a95 100644 --- a/src/host_stream/bt_uart.c +++ b/src/host_stream/bt_uart.c @@ -507,26 +507,4 @@ void bt_uart_set_identity(const char *aresx_version, const char *bt_name) if (aresx_version && *aresx_version) snprintf(s_ares_version, sizeof(s_ares_version), "%s", aresx_version); if (bt_name && *bt_name) - snprintf(s_bt_name, sizeof(s_bt_name), "%s", bt_name); - printf("[BT] identity: version=%s name=%s\n", s_ares_version, s_bt_name); -} - -int bt_uart_get_rent_status(void) -{ - pthread_mutex_lock(&s_rent_mtx); - int st = s_rent_status; - pthread_mutex_unlock(&s_rent_mtx); - return st; -} - -void bt_uart_set_rent_status(int status) -{ - pthread_mutex_lock(&s_rent_mtx); - s_rent_status = status; - pthread_mutex_unlock(&s_rent_mtx); -} - -void bt_uart_set_intervention_cb(bt_intervention_fn fn) -{ - s_intervention_cb = fn; -} + snprintf(s_bt_name, siz \ No newline at end of file diff --git a/src/host_stream/event_recorder.c b/src/host_stream/event_recorder.c index c8f32ea..f6fb0d6 100644 --- a/src/host_stream/event_recorder.c +++ b/src/host_stream/event_recorder.c @@ -871,27 +871,4 @@ void event_recorder_provide_frame(void) if (snap_lazy_init() != 0) return; - uint8_t *jpeg = (uint8_t *)MemBroker_GetMemory(SNAP_BUF_SIZE, VMF_ALIGN_TYPE_DEFAULT); - if (!jpeg) { - printf("[EVT] snap: MemBroker_GetMemory failed\n"); - return; - } - - int jpeg_size = VMF_SNAP_ProcessOneFrame(s_snap, 1920, 1080, SNAP_BUF_SIZE, jpeg); - int snap_ok = (jpeg_size > 0); - if (snap_ok) { - char out_path[384]; - snprintf(out_path, sizeof(out_path), "%s/%s", req.work_dir, req.filename); - save_jpeg(out_path, jpeg, jpeg_size); - } else { - printf("[EVT] snap: ProcessOneFrame failed (%d) — upload will proceed without image\n", jpeg_size); - } - MemBroker_FreeMemory(jpeg); - - if (req.immediate_upload) { - const char imgs[4][64] = { "snapshot.jpg", "", "", "" }; - int image_count = snap_ok ? 1 : 0; - launch_upload(req.work_dir, req.event_id, req.event_type, - req.max_level, 0.0f, imgs, image_count, 0); - } -} + uint8_t *jpeg = (uint8_t *)MemBroker_GetMemory(SNAP_BU \ No newline at end of file diff --git a/src/host_stream/kdp2_host_stream.c b/src/host_stream/kdp2_host_stream.c index 3b2984b..d375339 100644 --- a/src/host_stream/kdp2_host_stream.c +++ b/src/host_stream/kdp2_host_stream.c @@ -440,8 +440,8 @@ static int init_video_source(HOST_STREAM_INIT_OPT_T* pHostStreamInit) } } else { /* FEC passthrough (no lens correction). Load FEC config for gFecDefValue defaults only. - * Pass NULL for ptFrontConfig ??skips lens curve node loading (not needed for passthrough). - * Do NOT set ptFecConfig here ??SDK sets only eFecMethod=GTR (below) for this case. */ + * Pass NULL for ptFrontConfig — skips lens curve node loading (not needed for passthrough). + * Do NOT set ptFecConfig here — SDK sets only eFecMethod=GTR (below) for this case. */ if(loadFECConfig(pHostStreamInit, NULL) == -1){ printf("[%s] No fec config file, using defaults\n", __func__); } @@ -535,14 +535,14 @@ void set_data_to_yuv(unsigned char* pucYBuff, unsigned int dwWidth, unsigned int dwOffsetV = dwOffsetU + dwPlaneSize ; /* YCbCr colour table indexed by YOLO class number (BT.601 full-range). - * class=0 person ??green (Y=149, Cb= 43, Cr= 21) - * class=1 (other) ??white (Y=235, Cb=128, Cr=128) - * class=2 vehicle ??red (Y= 76, Cb= 84, Cr=255) - * fallback ??white */ + * class=0 person → green (Y=149, Cb= 43, Cr= 21) + * class=1 (other) → white (Y=235, Cb=128, Cr=128) + * class=2 vehicle → red (Y= 76, Cb= 84, Cr=255) + * fallback → white */ static const unsigned char CLASS_YUV[][3] = { - {149, 43, 21}, /* 0: person ??green */ - {235, 128, 128}, /* 1: other ??white */ - { 76, 84, 255}, /* 2: vehicle ??red */ + {149, 43, 21}, /* 0: person – green */ + {235, 128, 128}, /* 1: other – white */ + { 76, 84, 255}, /* 2: vehicle – red */ }; const unsigned char *yuv; if (iColor >= 0 && iColor < (int)(sizeof(CLASS_YUV)/sizeof(CLASS_YUV[0]))) @@ -669,7 +669,7 @@ void draw_rect(YUV_BUFF_INFO_T* yuvBuffInfo, DETECT_INFO *ptDetInfo, int iColor) * Per-class YCbCr colours for STDC segmentation overlay (BT.601 full-range). * Order matches STDC_CLASS_* indices: * 0=bunker 1=car 2=grass 3=greenery 4=person 5=pond 6=road 7=tree - * road (class 6) is left transparent ??it occupies most of the frame and adds + * road (class 6) is left transparent — it occupies most of the frame and adds * visual noise rather than information. */ static const uint8_t s_stdc_yuv[STDC_NUM_CLASSES][3] = { @@ -679,7 +679,7 @@ static const uint8_t s_stdc_yuv[STDC_NUM_CLASSES][3] = { {137, 104, 55}, /* greenery #22c55e */ {144, 59, 203}, /* person #f97316 */ {154, 182, 87}, /* pond #60a5fa */ - { 0, 0, 0}, /* road ??transparent (unused) */ + { 0, 0, 0}, /* road — transparent (unused) */ { 88, 113, 80}, /* tree #15803d */ }; @@ -710,7 +710,7 @@ static void stdc_paint_seg_overlay(unsigned char *base_buf, uint32_t y_stride = info->dwYStride; uint32_t uv_stride = info->dwYStride >> 1; - /* Pre-compute column mapping: output x ??seg col index */ + /* Pre-compute column mapping: output x → seg col index */ uint8_t col_lut[1920]; uint32_t fx_max = (frame_w < 1920) ? frame_w : 1920; for (uint32_t fx = 0; fx < fx_max; fx++) @@ -721,7 +721,7 @@ static void stdc_paint_seg_overlay(unsigned char *base_buf, const uint8_t *seg_row = local_map + seg_r * seg_w; uint8_t *y_row = y_buf + fy * y_stride; - /* Y plane ??every pixel */ + /* Y plane — every pixel */ for (uint32_t fx = 0; fx < fx_max; fx++) { uint8_t cls = seg_row[col_lut[fx]]; if (cls == STDC_CLASS_ROAD || cls >= STDC_NUM_CLASSES) continue; @@ -729,7 +729,7 @@ static void stdc_paint_seg_overlay(unsigned char *base_buf, y_row[fx] = (uint8_t)(((uint16_t)y_row[fx] + col[0]) >> 1); } - /* UV plane ??every other row, every other column */ + /* UV plane — every other row, every other column */ if ((fy & 1) == 0) { uint8_t *cb_row = cb_buf + (fy >> 1) * uv_stride; uint8_t *cr_row = cr_buf + (fy >> 1) * uv_stride; @@ -1543,8 +1543,8 @@ void *kdp2_host_voc_thread(void *arg) if (vsrc_ssm_info.dwOffset[0] != 0 && vsrc_ssm_info.dwOffset[1] != 0) { abuf[q_idx].apdwData[0] = ssm_buf[q_idx].buffer + vsrc_ssm_info.dwOffset[0]; abuf[q_idx].apdwData[1] = ssm_buf[q_idx].buffer + vsrc_ssm_info.dwOffset[1]; - /* NV12 (semi-planar): offset[2]==0 means no separate Cr plane ??NULL. - * YM12 (planar): offset[2]!=0 ??Cr plane pointer. */ + /* NV12 (semi-planar): offset[2]==0 means no separate Cr plane → NULL. + * YM12 (planar): offset[2]!=0 → Cr plane pointer. */ abuf[q_idx].apdwData[2] = vsrc_ssm_info.dwOffset[2] ? (ssm_buf[q_idx].buffer + vsrc_ssm_info.dwOffset[2]) : NULL; } else { @@ -1672,7 +1672,7 @@ void *kdp2_host_stream_image_thread(void *arg) * VOC thread unblocks on g_dwInitBind and immediately checks g_dwDrawBoxType * to decide which SSM pin to read from. If draw box is enabled on stream 0, * it must see g_dwDrawBoxType=1 so it reads from VENC_VSRC_B_PIN (overlay - * output) rather than the raw ISP SSM ??fixes intermittent HDMI no-overlay. */ + * output) rather than the raw ISP SSM — fixes intermittent HDMI no-overlay. */ if (pHostStreamInit->bDrawBoxEnable && pHostStreamInit->dwEncodeStreamCount > 0) g_dwDrawBoxType = 1; g_dwInitBind = 1; @@ -1879,58 +1879,4 @@ EXIT_MIPI_IMAGE_THREAD: return NULL; } -void *kdp2_host_update_result_thread(void *arg) -{ - HOST_STREAM_INIT_OPT_T* pHostStreamInit = (HOST_STREAM_INIT_OPT_T*)arg; - uint32_t buf_addr = 0; - uint32_t phy_buf_addr = 0; - int buf_size = 0; - int sts = 0; - - dbg_log("[%s] starting ..\n", __FUNCTION__); - if (app_hdr_recv_inf_cb == NULL) { - printf("[%s] recv_inf_cb is NULL \n", __FUNCTION__); - goto EXIT_UPDATE_RESULT_THREAD; - } - - g_dwRoiX = pHostStreamInit->dwRoiX; - g_dwRoiY = pHostStreamInit->dwRoiY; - g_dwDrawBoxEnable = pHostStreamInit->bDrawBoxEnable; - g_dwOnlyPerson = pHostStreamInit->dwOnlyPerson; - - while (true == _blResultRunning) { - // get result data from queue blocking wait - int ret = VMF_NNM_Fifoq_Manager_Result_Dequeue(&buf_addr, &phy_buf_addr, &buf_size, -1); - - if (KP_FW_FIFOQ_ACCESS_FAILED_125 == ret) { - continue; - } else if (KP_SUCCESS != ret) { - printf("[%s] Error: FIFO queue error %d.\n", __FUNCTION__, sts); - goto EXIT_UPDATE_RESULT_THREAD; - } - - //dbg_log("[%s] buf 0x%x len %d -- usb --> host\n", __FUNCTION__, buf_addr, header_stamp->total_size); - sts = app_hdr_recv_inf_cb(buf_addr, &_blResultRunning); //printf result - if (KP_SUCCESS != sts) { - printf("[%s] Error: callback function error (%d)\n", __FUNCTION__, sts); - goto EXIT_UPDATE_RESULT_THREAD; - } - - // return free buf back to queue - ret = VMF_NNM_Fifoq_Manager_Result_Put_Free_Buffer(buf_addr, phy_buf_addr, buf_size, -1); - if (KP_FW_FIFOQ_ACCESS_FAILED_125 == ret) { - continue; - } else if (KP_SUCCESS != ret) { - goto EXIT_UPDATE_RESULT_THREAD; - } - } - -EXIT_UPDATE_RESULT_THREAD: - if (NULL != g_pm_buf) { - MemBroker_FreeMemory(g_pm_buf); - g_pm_buf = NULL; - } - - dbg_log("[%s] exit ..\n", __FUNCTION__); - return NULL; -} +void *kdp2_host_updat \ No newline at end of file