├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── asserts ├── clientInstance.gif ├── clientInstanceLogin.jpg ├── clientInstanceRegister.jpg ├── clientInstanceSplash.jpg ├── crossed.png ├── gitlink&&mmdeploy.png ├── glccWorkflow.png ├── ic_launcher.png ├── logoWithText.png ├── serverInstance.gif └── weixing.jpg ├── bytetrack ├── CMakeLists.txt ├── include │ ├── BYTETracker.h │ ├── STrack.h │ ├── dataType.h │ ├── kalmanFilter.h │ └── lapjv.h └── src │ ├── BYTETracker.cpp │ ├── STrack.cpp │ ├── kalmanFilter.cpp │ ├── lapjv.cpp │ └── utils.cpp ├── configs └── config.json ├── include ├── common.h ├── dealtor.h ├── loguru.hpp └── server.h ├── models └── yolox_tiny_cat_dynamic_fp16_trt │ ├── deploy.json │ ├── detail.json │ ├── end2end.engine │ ├── end2end.onnx │ ├── output_pytorch.jpg │ ├── output_tensorrt.jpg │ └── pipeline.json └── src ├── common.cpp ├── dealtor.cpp ├── loguru.cpp ├── main.cpp └── server.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.py[cod] 3 | *$py.class 4 | # C extensions 5 | .ssl/ 6 | *.so 7 | build/ 8 | eggs/ 9 | lib/ 10 | lib64/ 11 | wheels 12 | *.egg-infos/ 13 | *.egg 14 | MANIFEST 15 | *.log 16 | tmp/ 17 | 18 | # model 19 | *.pkl 20 | 21 | # envs 22 | .env 23 | .venv 24 | env/ 25 | venv/ 26 | 27 | # data 28 | 29 | data/ 30 | 31 | # idel 32 | .vscode 33 | .cache 34 | 35 | # experiments 36 | my_exps/ 37 | work_dir/ 38 | log/ 39 | 40 | # extra libs 41 | tags -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | project(glcc_server) 3 | set(CMAKE_BUILD_TYPE Debug) 4 | set(CMAKE_CXX_STANDARD 14) 5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fconcepts --std=c++14") 6 | 7 | find_package(OpenCV REQUIRED) 8 | include_directories($(OpenCV_INCLUDE_DIRS)) 9 | set(EXTRA_LIBS ${EXTRA_LIBS} ${OpenCV_LIBS}) 10 | if (FOUND_OpenCV) 11 | message(STATUS "OpenCV Library status: ") 12 | message(STATUS " version: {OpenCV_VERSION}" \n) 13 | endif() 14 | 15 | # mmdeploy 16 | find_package(MMDeploy REQUIRED) 17 | 18 | # custom 19 | include_directories(${PROJECT_SOURCE_DIR}/include) 20 | aux_source_directory(${PROJECT_SOURCE_DIR}/src DIR_SRCS) 21 | 22 | add_executable(${CMAKE_PROJECT_NAME} ${DIR_SRCS}) 23 | add_subdirectory(bytetrack) 24 | include_directories(${bytetrack_SOURCE_DIR}/include) 25 | 26 | # eigen3 27 | find_package(Eigen3 REQUIRED) 28 | include_directories(${EIGEN3_INCLUDE_DIRS}) 29 | 30 | set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}) 31 | set(EXTRA_LIBS ${EXTRA_LIBS} jsoncpp workflow bytetrack) 32 | 33 | target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE -Wl,--disable-new-dtags) 34 | mmdeploy_load_static(${CMAKE_PROJECT_NAME} MMDeployStaticModules) 35 | mmdeploy_load_dynamic(${CMAKE_PROJECT_NAME} MMDeployDynamicModules) 36 | target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE MMDeployLibs ${EXTRA_LIBS}) 37 | 38 | 39 | add_definitions(-O0 -pthread) 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 DDGRCF 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
 
3 |
4 | 5 |
6 | logo-catcat 7 |
8 |
9 |
 
10 |
11 | logo-mmdeply&&gitlink 12 |
13 |
14 | 15 |

16 | 17 | Cat Cat | MMDeply $\times$ GLCC 开源项目 | MMDeploy 实践 18 | 19 |

20 | 21 |

22 | System 23 |   24 | Role 25 |   26 | Build 27 |

28 | 29 | # 介绍 30 | 31 | ✨ 本项目是发起于[CCF GitLink开源编程夏令营](https://www.gitlink.org.cn/)(GitLink Code Camp, 简称GLCC). CCF GitLink 开源编程夏令营(GitLink Code Camp,简称 GLCC),是在 CCF 中国计算机学会指导下,由 GitLink 社区联合 CCF 开源发展委员会(CCF ODC)共同举办的面向全国高校学生的暑期开源项目实习计划。活动将联合各大开源企业、开源基金会、开源社区及开源领域专家,旨在鼓励高校学生通过参与真实的开源软件开发,感受开源文化,提升自身技术能力,进而达到为开源企业和社区输送优秀人才的目的。作为此次活动的特邀合作单位,OpenMMLab 设置了 12 个项目方向共 15 个课题 32 | 33 | ✨ 本项目基于[OpenMMLab](https://github.com/open-mmlab)下的子开源项目[MMDeploy](https://github.com/open-mmlab/mmdeploy)🔥🔥🔥实现的。MMDeploy 是 OpenMMLab 模型部署工具箱,为各算法库提供统一的部署体验。基于 MMDeploy,开发者可以轻松从训练 repo 生成指定硬件所需 SDK,省去大量适配时间。目前 MMDeploy 支持的算法 repo 有:mmcls、mmdet、mmdet3d、mmedit、mmocr、mmpose、mmseg、mmrazor。MMDeploy 同时支持超多推理后端:ONNX Runtime、TensorRT、ppl.nn、ncnn、OpenVINO,目前新版已经发布,快去体验一下吧🔥🔥🔥 34 | 35 | ✨ 针对如今猫猫饲养普及,但人们工作越来越忙碌,不能够照看猫猫的问题,本项目基于MMDeploy,实现了猫猫的检测识别、跟踪、视频记录、视频推送等服务,该服务主要具有以下两个功能: 36 | 37 | * 能够对猫猫进行检测和跟踪,并将检测结果进行推流,可通过手机App播放 38 | * 能够对猫猫进行划线检测,对于猫猫进入一定区域的时间点的视频进行记录并推送给用户 39 | 40 | 实际上通过简单的修改配置文件,能够很容易将本服务拓展到其他宠物的检测识别、跟踪服务,请根据后面章节修改[配置文件](configs/config.json) 41 | 42 | 目前,该项目包括两个部分: 43 | 44 | * **能够响应Http请求,完成检测跟踪的服务器** 👈 **目前位置** 45 | * [能够发起Http请求,完成实现播放的客服端](https://github.com/DDGRCF/GLCC_AndroidApplication) 46 | 47 | **✨服务器目前支持的功能:** 48 | 49 | - [x] 响应基础的Http请求 50 | - [x] 猫猫检测和跟踪 51 | - [x] 猫猫的越线检测 52 | - [x] 猫猫的越线时间点视频的记录 53 | - [x] 猫猫监控的直播视频流的推送 54 | - [x] 记录视频的文件视频流的推送 55 | - [x] 提供配置文件可拓展其他类别 56 | - [x] 猫猫记录视频的定时删除 57 | - [ ] 加载SSL证书 58 | 59 | **✨客服端目前完成的功能:** 60 | 61 | - [x] 发起基础的Http请求 62 | - [x] 用户登录注册 63 | - [x] 视频源注册删除 64 | - [x] 猫猫监控的直播视频流的拉取 65 | - [x] 猫猫记录的文件视频流的拉取 66 | - [x] 支持服务器配置端口,IP等信息 67 | - [ ] 加载SSL证书 68 | 69 | 70 | # 预览 71 | 72 | ## 服务器 73 | 74 | ![serverInstance](/asserts/serverInstance.gif) 75 | 76 | ## 客服端 77 | server-Instance server-Instance server-Instance server-Instance 78 | 79 | 80 | ## 说明 81 | 82 | |
类型
|
说明
| 83 | |---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 84 | |
**服务端**
| 在上面的预览中,我们可以看到服务端运行后,(在客服端发送播放后)跳出来了一个视频界面,视频上两只猫猫被检测出来,并进行了跟踪。另外,我们可以注意到,外面有一个不规则的红色的框。这红色框就代表我们预先设置的栅栏,围栏之类的。猫猫进入了该区域就代表进入了危险区域,那么该红色框就变为实心,并发送通知给用户。这里为了方便演示,我直接将危险框放置在目标中心。值得一提的是,只有猫猫在该区域待够一定时间(可通过配置文件修改),服务器才会将猫猫识别为进入该危险区域 | 85 | |
**客户端**
| 在上面的预览中,我们可以看到在客服端登录后,客服端向服务器发送一个拉流请求并播放相关视频(视频播放界面的纵横比为4/3,如果拉取视频流的纵横比大于或小于这个尺寸,会使用灰色进行填充),视频下有一个工具栏,工具栏上有**REGISTER**、**DELETE**、**DRAW**、**SOURCE**等字样,分别代表视频流的注册,视频流的删除,放置边界框(栅栏,围栏之类),目前的播放源功能的按钮(可通过旁边的下拉栏可切换播放源)。然后工具栏下面,有一个可滑动的视频栏目,这个视频栏目就猫猫进入危险时所记录的视频,点击每一个视频栏目可实现视频的播放 | 86 | 87 | 88 | 89 | # 工作流程 90 | glcc-workflow 91 | 92 | # 安装与运行 93 | ### 环境依赖 94 | * **MMDeploy**(OpenMMLab下部署工作包) ➡️ [点击进入](https://github.com/open-mmlab/mmdeploy.git) 95 | * **Eigen**(C/C++的矩阵计算库) ➡️ [点击进入](https://eigen.tuxfamily.org/index.php?title=Main_Page) 96 | * **FFmpeg**(包含视频、音频处理工具包和开发库) ➡️ [点击进入](https://ffmpeg.org/) 97 | * **Mysql**(开放源码的数据库) ➡ [点击进入](https://www.mysql.com/) 98 | * **JsonCpp**(基于C/C++的Json格式解析库) ➡ [点击进入](https://github.com/open-source-parsers/jsoncpp) 99 | * **Lal**(基于Go开发的高性能流服务器) ➡ [点击进入](https://github.com/q191201771/lal) 100 | ### 安装命令 101 | 上面的依赖都具有详细的安装文档,安装完所有依赖后,再进行下面的操作: 102 | 103 | 1. 设置相关变量 104 | ```bash 105 | MMDeploy_DIR=/path/to/your/mmdeploy/install 106 | e.g. MMDeploy_DIR=mmdeploy/build/install/cmake/MMDeploy 107 | 108 | OpenCV_DIR=/path/to/your/opencv/install 109 | e.g. OpenCV_DIR=/usr/local/opencv/lib/cmake/opencv4 110 | 111 | DEigen3_DIR=/path/to/your/Eigen/install 112 | e.g. DEigen3_DIR=/usr/local/eign/share/eigen3/cmake 113 | ``` 114 | 115 | 2. 生成MakeFile 116 | ```bash 117 | mkdir -p build && cd build && cmake .. -DMMDeploy_DIR=${MMDeploy_DIR} -DOpencv_DIR=${Opencv_DIR} -DEigen3_DIR=${Eigen3_DIR} 118 | ``` 119 | 3. 编译 120 | ```bash 121 | make -j$(nproc) 122 | ``` 123 | ### 运行命令 124 | 运行之前请确保Lal流服务器以及Mysql数据服务器启动,并按照章节修改配置 125 | ```bash 126 | cd build 127 | config=/path/your/config # it just put in configs/configs.json 128 | SPDLOG_LEVEL=error ./glcc_server ${config} 129 | ``` 130 | 131 | # 服务器配置 132 | ```json 133 | { 134 | "Server": { 135 | "server_ip": "0.0.0.0", // 服务器的IP地址 136 | "server_port": 9999, // 服务器的Port 137 | "work_dir": "work_dir", // 服务器的工作目录,用于储存用户资源[default: ./work_dir] 138 | "ssl_crt_path": "/path/your/server.crt", // ssl 证书路径[must] 139 | "ssl_key_path": "/path/your/server_rsa_private.pem.unsecure" // ssl 私钥路径[must] 140 | }, 141 | "Log": { 142 | "log_dir": "log", // log保存的目录[default: ./log] 143 | "log_file_time_format": "%Y-%m-%d_%H:%M:%S", // log保存的格式 144 | "log_add_file_verbosity": "INFO", // 新的log保存的等级 145 | "log_all_file_verbosity": "INFO" // 所有log保存的等级 146 | }, 147 | "Detector": { 148 | "mode": "TrackerDetector", // Detector的模式,默认为跟踪模式 149 | "ObjectDetector": { // 检测模型配置 150 | "model": "/path/to/your/model", // 模型所在位置[must] 151 | "device": "cuda", // 模型运行设备 152 | "device_id": 0, // 模型运行设备id 153 | "extra_config": { 154 | "score_thre": 0.3, // 检测框的得分阈值 155 | "into_contour_time_gap_second": 5, // 宠物进预设框的阈值 156 | "out_contour_time_gap_second": 20, // 宠物出预设框的阈值 157 | "imshow_result_image": true, // 是否在播放时可视化结果(服务端) 158 | "class_names": ["cat"] // 检测的类别 159 | } 160 | }, 161 | "TrackerDetector": { // 跟踪模型配置 162 | "model": "/path/your/model", // 模型所在位置[must] 163 | "device": "cuda", // 模型运行设备 164 | "device_id": 0, // 模型运行设备id 165 | "extra_config": { 166 | "score_thre": 0.3, // 检测框的得分阈值 167 | "tracker_buffer": 30, // 跟踪的时所存储的最大帧数 168 | "into_contour_time_gap_second": 5, // 宠物进预设框的阈值 169 | "out_contour_time_gap_second": 20, // 宠物出预设框的阈值 170 | "imshow_result_image": true, // 是否在播放时可视化结果(服务端) 171 | "wh_ratio_thre_to_show": 1.6, // 可视化框的纵横比阈值(1.6>) 172 | "wh_multiply_thre_to_show": 20, // 可视化框的面积阈值(20<) 173 | "class_names": ["cat"] // 检测类别 174 | } 175 | } 176 | }, 177 | "LiveGo": { 178 | "camera_push_port": 5544, // 推流视频源端口,推流本地摄像头: ffmpeg -re -framerate 25 -video_size 640x480 -i /dev/video0 -vcodec h264 -f rtsp rtsp://127.0.0.1:5544/live/test 179 | "dect_push_port": 1935, // 检测结果推送端口 180 | "state_check_port": 8083 // 流服务器状态查询端口 181 | }, 182 | "DB": { 183 | "user_name": "root", // 数据库用户名 184 | "user_password": "9696", // 数据库密码 185 | "db_server_ip": "127.0.0.1", // 数据库的IP 186 | "db_server_port": 3306 // 数据库的端口 187 | }, 188 | "Timer": { 189 | "interval_to_watch_detector_second": 60, // 间隔多少时间对直播视频流进行检查(检查是否过期) 190 | "interval_to_watch_file_second":5, // 间隔多少时间对文件视频进行检测(检查是否过期) 191 | "max_video_file_save_day": 2, // 保存视频文件最大储存天数 192 | "max_detector_live_day": 365 // 直播视频流最大存在天数 193 | } 194 | } 195 | 196 | ``` 197 | 198 | # 开源许可证 199 | 本项目采用 [MIT](./LICENSE) 开源许可证 200 | 201 | # 感谢以下项目 202 | * [MMDeploy](https://github.com/open-mmlab/mmdeploy.git) 203 | * [MMDetection](https://github.com/open-mmlab/mmdetection) 204 | * [YOLOX](https://github.com/Megvii-BaseDetection/YOLOX) 205 | * [Workflow](https://github.com/sogou/workflow/blob/master) 206 | * [Lal](https://github.com/q191201771/lal) 207 | * [MegFlow](https://github.com/MegEngine/MegFlow) 208 | * [ByteTrack](https://github.com/ifzhang/ByteTrack) 209 | * [loguru](https://github.com/emilk/loguru) 210 | * [Eigen](https://gitlab.com/libeigen/eigen) 211 | * [FFmpeg](https://github.com/FFmpeg/FFmpeg) 212 | * [JsonCpp](https://github.com/open-source-parsers/jsoncpp) 213 | 214 | 215 | # 技术交流 216 | weixing 217 | -------------------------------------------------------------------------------- /asserts/clientInstance.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDGRCF/GLCC_Server/8aa41355b01830b1a6003ea222a4dfd8794c6396/asserts/clientInstance.gif -------------------------------------------------------------------------------- /asserts/clientInstanceLogin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDGRCF/GLCC_Server/8aa41355b01830b1a6003ea222a4dfd8794c6396/asserts/clientInstanceLogin.jpg -------------------------------------------------------------------------------- /asserts/clientInstanceRegister.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDGRCF/GLCC_Server/8aa41355b01830b1a6003ea222a4dfd8794c6396/asserts/clientInstanceRegister.jpg -------------------------------------------------------------------------------- /asserts/clientInstanceSplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDGRCF/GLCC_Server/8aa41355b01830b1a6003ea222a4dfd8794c6396/asserts/clientInstanceSplash.jpg -------------------------------------------------------------------------------- /asserts/crossed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDGRCF/GLCC_Server/8aa41355b01830b1a6003ea222a4dfd8794c6396/asserts/crossed.png -------------------------------------------------------------------------------- /asserts/gitlink&&mmdeploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDGRCF/GLCC_Server/8aa41355b01830b1a6003ea222a4dfd8794c6396/asserts/gitlink&&mmdeploy.png -------------------------------------------------------------------------------- /asserts/glccWorkflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDGRCF/GLCC_Server/8aa41355b01830b1a6003ea222a4dfd8794c6396/asserts/glccWorkflow.png -------------------------------------------------------------------------------- /asserts/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDGRCF/GLCC_Server/8aa41355b01830b1a6003ea222a4dfd8794c6396/asserts/ic_launcher.png -------------------------------------------------------------------------------- /asserts/logoWithText.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDGRCF/GLCC_Server/8aa41355b01830b1a6003ea222a4dfd8794c6396/asserts/logoWithText.png -------------------------------------------------------------------------------- /asserts/serverInstance.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDGRCF/GLCC_Server/8aa41355b01830b1a6003ea222a4dfd8794c6396/asserts/serverInstance.gif -------------------------------------------------------------------------------- /asserts/weixing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDGRCF/GLCC_Server/8aa41355b01830b1a6003ea222a4dfd8794c6396/asserts/weixing.jpg -------------------------------------------------------------------------------- /bytetrack/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | 3 | project(bytetrack) 4 | 5 | add_definitions(-std=c++14) 6 | 7 | option(CUDA_USE_STATIC_CUDA_RUNTIME OFF) 8 | set(CMAKE_CXX_STANDARD 14) 9 | set(CMAKE_BUILD_TYPE Debug) 10 | 11 | find_package(Eigen3 REQUIRED) 12 | include_directories(${EIGEN3_INCLUDE_DIRS}) 13 | 14 | find_package(OpenCV REQUIRED) 15 | include_directories(${OpenCV_INCLUDE_DIRS}) 16 | 17 | include_directories(${PROJECT_SOURCE_DIR}/include) 18 | link_directories(${PROJECT_SOURCE_DIR}/include) 19 | 20 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -Ofast -Wfatal-errors") 21 | 22 | 23 | file(GLOB mSOURCE_FILES ${PROJECT_SOURCE_DIR}/src/*.cpp) 24 | add_library(bytetrack SHARED ${mSOURCE_FILES}) 25 | target_link_libraries(bytetrack ${OpenCV_LIBS}) 26 | add_definitions(-O2 -pthread) -------------------------------------------------------------------------------- /bytetrack/include/BYTETracker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "STrack.h" 4 | 5 | struct Object 6 | { 7 | Object(cv::Rect_ _rect, int _label, float _prob): rect(_rect), label(_label), prob(_prob){} 8 | cv::Rect_ rect; 9 | int label; 10 | float prob; 11 | }; 12 | 13 | class BYTETracker 14 | { 15 | public: 16 | BYTETracker(int frame_rate = 30, int track_buffer = 30); 17 | ~BYTETracker(); 18 | 19 | vector update(const vector& objects); 20 | Scalar get_color(int idx); 21 | 22 | private: 23 | vector joint_stracks(vector &tlista, vector &tlistb); 24 | vector joint_stracks(vector &tlista, vector &tlistb); 25 | 26 | vector sub_stracks(vector &tlista, vector &tlistb); 27 | void remove_duplicate_stracks(vector &resa, vector &resb, vector &stracksa, vector &stracksb); 28 | 29 | void linear_assignment(vector > &cost_matrix, int cost_matrix_size, int cost_matrix_size_size, float thresh, 30 | vector > &matches, vector &unmatched_a, vector &unmatched_b); 31 | vector > iou_distance(vector &atracks, vector &btracks, int &dist_size, int &dist_size_size); 32 | vector > iou_distance(vector &atracks, vector &btracks); 33 | vector > ious(vector > &atlbrs, vector > &btlbrs); 34 | 35 | double lapjv(const vector > &cost, vector &rowsol, vector &colsol, 36 | bool extend_cost = false, float cost_limit = LONG_MAX, bool return_cost = true); 37 | 38 | private: 39 | 40 | float track_thresh; 41 | float high_thresh; 42 | float match_thresh; 43 | int frame_id; 44 | int max_time_lost; 45 | 46 | vector tracked_stracks; 47 | vector lost_stracks; 48 | vector removed_stracks; 49 | byte_kalman::KalmanFilter kalman_filter; 50 | }; -------------------------------------------------------------------------------- /bytetrack/include/STrack.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "kalmanFilter.h" 5 | 6 | using namespace cv; 7 | using namespace std; 8 | 9 | enum TrackState { New = 0, Tracked, Lost, Removed }; 10 | 11 | class STrack 12 | { 13 | public: 14 | STrack(vector tlwh_, float score); 15 | ~STrack(); 16 | 17 | vector static tlbr_to_tlwh(vector &tlbr); 18 | void static multi_predict(vector &stracks, byte_kalman::KalmanFilter &kalman_filter); 19 | void static_tlwh(); 20 | void static_tlbr(); 21 | vector tlwh_to_xyah(vector tlwh_tmp); 22 | vector to_xyah(); 23 | void mark_lost(); 24 | void mark_removed(); 25 | int next_id(); 26 | int end_frame(); 27 | 28 | void activate(byte_kalman::KalmanFilter &kalman_filter, int frame_id); 29 | void re_activate(STrack &new_track, int frame_id, bool new_id = false); 30 | void update(STrack &new_track, int frame_id); 31 | 32 | public: 33 | bool is_activated; 34 | int track_id; 35 | int state; 36 | 37 | vector _tlwh; 38 | vector tlwh; 39 | vector tlbr; 40 | int frame_id; 41 | int tracklet_len; 42 | int start_frame; 43 | 44 | KAL_MEAN mean; 45 | KAL_COVA covariance; 46 | float score; 47 | 48 | private: 49 | byte_kalman::KalmanFilter kalman_filter; 50 | }; -------------------------------------------------------------------------------- /bytetrack/include/dataType.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | typedef Eigen::Matrix DETECTBOX; 9 | typedef Eigen::Matrix DETECTBOXSS; 10 | typedef Eigen::Matrix FEATURE; 11 | typedef Eigen::Matrix FEATURESS; 12 | //typedef std::vector FEATURESS; 13 | 14 | //Kalmanfilter 15 | //typedef Eigen::Matrix KAL_FILTER; 16 | typedef Eigen::Matrix KAL_MEAN; 17 | typedef Eigen::Matrix KAL_COVA; 18 | typedef Eigen::Matrix KAL_HMEAN; 19 | typedef Eigen::Matrix KAL_HCOVA; 20 | using KAL_DATA = std::pair; 21 | using KAL_HDATA = std::pair; 22 | 23 | //main 24 | using RESULT_DATA = std::pair; 25 | 26 | //tracker: 27 | using TRACKER_DATA = std::pair; 28 | using MATCH_DATA = std::pair; 29 | typedef struct t { 30 | std::vector matches; 31 | std::vector unmatched_tracks; 32 | std::vector unmatched_detections; 33 | }TRACHER_MATCHD; 34 | 35 | //linear_assignment: 36 | typedef Eigen::Matrix DYNAMICM; -------------------------------------------------------------------------------- /bytetrack/include/kalmanFilter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "dataType.h" 4 | 5 | namespace byte_kalman 6 | { 7 | class KalmanFilter 8 | { 9 | public: 10 | static const double chi2inv95[10]; 11 | KalmanFilter(); 12 | KAL_DATA initiate(const DETECTBOX& measurement); 13 | void predict(KAL_MEAN& mean, KAL_COVA& covariance); 14 | KAL_HDATA project(const KAL_MEAN& mean, const KAL_COVA& covariance); 15 | KAL_DATA update(const KAL_MEAN& mean, 16 | const KAL_COVA& covariance, 17 | const DETECTBOX& measurement); 18 | 19 | Eigen::Matrix gating_distance( 20 | const KAL_MEAN& mean, 21 | const KAL_COVA& covariance, 22 | const std::vector& measurements, 23 | bool only_position = false); 24 | 25 | private: 26 | Eigen::Matrix _motion_mat; 27 | Eigen::Matrix _update_mat; 28 | float _std_weight_position; 29 | float _std_weight_velocity; 30 | }; 31 | } -------------------------------------------------------------------------------- /bytetrack/include/lapjv.h: -------------------------------------------------------------------------------- 1 | #ifndef LAPJV_H 2 | #define LAPJV_H 3 | 4 | #define LARGE 1000000 5 | 6 | #if !defined TRUE 7 | #define TRUE 1 8 | #endif 9 | #if !defined FALSE 10 | #define FALSE 0 11 | #endif 12 | 13 | #define NEW(x, t, n) if ((x = (t *)malloc(sizeof(t) * (n))) == 0) { return -1; } 14 | #define FREE(x) if (x != 0) { free(x); x = 0; } 15 | #define SWAP_INDICES(a, b) { int_t _temp_index = a; a = b; b = _temp_index; } 16 | 17 | #if 0 18 | #include 19 | #define ASSERT(cond) assert(cond) 20 | #define PRINTF(fmt, ...) printf(fmt, ##__VA_ARGS__) 21 | #define PRINT_COST_ARRAY(a, n) \ 22 | while (1) { \ 23 | printf(#a" = ["); \ 24 | if ((n) > 0) { \ 25 | printf("%f", (a)[0]); \ 26 | for (uint_t j = 1; j < n; j++) { \ 27 | printf(", %f", (a)[j]); \ 28 | } \ 29 | } \ 30 | printf("]\n"); \ 31 | break; \ 32 | } 33 | #define PRINT_INDEX_ARRAY(a, n) \ 34 | while (1) { \ 35 | printf(#a" = ["); \ 36 | if ((n) > 0) { \ 37 | printf("%d", (a)[0]); \ 38 | for (uint_t j = 1; j < n; j++) { \ 39 | printf(", %d", (a)[j]); \ 40 | } \ 41 | } \ 42 | printf("]\n"); \ 43 | break; \ 44 | } 45 | #else 46 | #define ASSERT(cond) 47 | #define PRINTF(fmt, ...) 48 | #define PRINT_COST_ARRAY(a, n) 49 | #define PRINT_INDEX_ARRAY(a, n) 50 | #endif 51 | 52 | 53 | typedef signed int int_t; 54 | typedef unsigned int uint_t; 55 | typedef double cost_t; 56 | typedef char boolean; 57 | typedef enum fp_t { FP_1 = 1, FP_2 = 2, FP_DYNAMIC = 3 } fp_t; 58 | 59 | extern int_t lapjv_internal( 60 | const uint_t n, cost_t *cost[], 61 | int_t *x, int_t *y); 62 | 63 | #endif // LAPJV_H -------------------------------------------------------------------------------- /bytetrack/src/BYTETracker.cpp: -------------------------------------------------------------------------------- 1 | #include "BYTETracker.h" 2 | #include 3 | 4 | BYTETracker::BYTETracker(int frame_rate, int track_buffer) 5 | { 6 | track_thresh = 0.5; 7 | high_thresh = 0.6; 8 | match_thresh = 0.8; 9 | 10 | frame_id = 0; 11 | max_time_lost = int(frame_rate / 30.0 * track_buffer); 12 | cout << "Init ByteTrack!" << endl; 13 | } 14 | 15 | BYTETracker::~BYTETracker() 16 | { 17 | } 18 | 19 | vector BYTETracker::update(const vector& objects) 20 | { 21 | 22 | ////////////////// Step 1: Get detections ////////////////// 23 | this->frame_id++; 24 | vector activated_stracks; 25 | vector refind_stracks; 26 | vector removed_stracks; 27 | vector lost_stracks; 28 | vector detections; 29 | vector detections_low; 30 | 31 | vector detections_cp; 32 | vector tracked_stracks_swap; 33 | vector resa, resb; 34 | vector output_stracks; 35 | 36 | vector unconfirmed; 37 | vector tracked_stracks; 38 | vector strack_pool; 39 | vector r_tracked_stracks; 40 | 41 | if (objects.size() > 0) 42 | { 43 | for (int i = 0; i < objects.size(); i++) 44 | { 45 | vector tlbr_; 46 | tlbr_.resize(4); 47 | tlbr_[0] = objects[i].rect.x; 48 | tlbr_[1] = objects[i].rect.y; 49 | tlbr_[2] = objects[i].rect.x + objects[i].rect.width; 50 | tlbr_[3] = objects[i].rect.y + objects[i].rect.height; 51 | 52 | float score = objects[i].prob; 53 | 54 | STrack strack(STrack::tlbr_to_tlwh(tlbr_), score); 55 | if (score >= track_thresh) 56 | { 57 | detections.push_back(strack); 58 | } 59 | else 60 | { 61 | detections_low.push_back(strack); 62 | } 63 | 64 | } 65 | } 66 | 67 | // Add newly detected tracklets to tracked_stracks 68 | for (int i = 0; i < this->tracked_stracks.size(); i++) 69 | { 70 | if (!this->tracked_stracks[i].is_activated) 71 | unconfirmed.push_back(&this->tracked_stracks[i]); 72 | else 73 | tracked_stracks.push_back(&this->tracked_stracks[i]); 74 | } 75 | 76 | ////////////////// Step 2: First association, with IoU ////////////////// 77 | strack_pool = joint_stracks(tracked_stracks, this->lost_stracks); 78 | STrack::multi_predict(strack_pool, this->kalman_filter); 79 | 80 | vector > dists; 81 | int dist_size = 0, dist_size_size = 0; 82 | dists = iou_distance(strack_pool, detections, dist_size, dist_size_size); 83 | 84 | vector > matches; 85 | vector u_track, u_detection; 86 | linear_assignment(dists, dist_size, dist_size_size, match_thresh, matches, u_track, u_detection); 87 | 88 | for (int i = 0; i < matches.size(); i++) 89 | { 90 | STrack *track = strack_pool[matches[i][0]]; 91 | STrack *det = &detections[matches[i][1]]; 92 | if (track->state == TrackState::Tracked) 93 | { 94 | track->update(*det, this->frame_id); 95 | activated_stracks.push_back(*track); 96 | } 97 | else 98 | { 99 | track->re_activate(*det, this->frame_id, false); 100 | refind_stracks.push_back(*track); 101 | } 102 | } 103 | 104 | ////////////////// Step 3: Second association, using low score dets ////////////////// 105 | for (int i = 0; i < u_detection.size(); i++) 106 | { 107 | detections_cp.push_back(detections[u_detection[i]]); 108 | } 109 | detections.clear(); 110 | detections.assign(detections_low.begin(), detections_low.end()); 111 | 112 | for (int i = 0; i < u_track.size(); i++) 113 | { 114 | if (strack_pool[u_track[i]]->state == TrackState::Tracked) 115 | { 116 | r_tracked_stracks.push_back(strack_pool[u_track[i]]); 117 | } 118 | } 119 | 120 | dists.clear(); 121 | dists = iou_distance(r_tracked_stracks, detections, dist_size, dist_size_size); 122 | 123 | matches.clear(); 124 | u_track.clear(); 125 | u_detection.clear(); 126 | linear_assignment(dists, dist_size, dist_size_size, 0.5, matches, u_track, u_detection); 127 | 128 | for (int i = 0; i < matches.size(); i++) 129 | { 130 | STrack *track = r_tracked_stracks[matches[i][0]]; 131 | STrack *det = &detections[matches[i][1]]; 132 | if (track->state == TrackState::Tracked) 133 | { 134 | track->update(*det, this->frame_id); 135 | activated_stracks.push_back(*track); 136 | } 137 | else 138 | { 139 | track->re_activate(*det, this->frame_id, false); 140 | refind_stracks.push_back(*track); 141 | } 142 | } 143 | 144 | for (int i = 0; i < u_track.size(); i++) 145 | { 146 | STrack *track = r_tracked_stracks[u_track[i]]; 147 | if (track->state != TrackState::Lost) 148 | { 149 | track->mark_lost(); 150 | lost_stracks.push_back(*track); 151 | } 152 | } 153 | 154 | // Deal with unconfirmed tracks, usually tracks with only one beginning frame 155 | detections.clear(); 156 | detections.assign(detections_cp.begin(), detections_cp.end()); 157 | 158 | dists.clear(); 159 | dists = iou_distance(unconfirmed, detections, dist_size, dist_size_size); 160 | 161 | matches.clear(); 162 | vector u_unconfirmed; 163 | u_detection.clear(); 164 | linear_assignment(dists, dist_size, dist_size_size, 0.7, matches, u_unconfirmed, u_detection); 165 | 166 | for (int i = 0; i < matches.size(); i++) 167 | { 168 | unconfirmed[matches[i][0]]->update(detections[matches[i][1]], this->frame_id); 169 | activated_stracks.push_back(*unconfirmed[matches[i][0]]); 170 | } 171 | 172 | for (int i = 0; i < u_unconfirmed.size(); i++) 173 | { 174 | STrack *track = unconfirmed[u_unconfirmed[i]]; 175 | track->mark_removed(); 176 | removed_stracks.push_back(*track); 177 | } 178 | 179 | ////////////////// Step 4: Init new stracks ////////////////// 180 | for (int i = 0; i < u_detection.size(); i++) 181 | { 182 | STrack *track = &detections[u_detection[i]]; 183 | if (track->score < this->high_thresh) 184 | continue; 185 | track->activate(this->kalman_filter, this->frame_id); 186 | activated_stracks.push_back(*track); 187 | } 188 | 189 | ////////////////// Step 5: Update state ////////////////// 190 | for (int i = 0; i < this->lost_stracks.size(); i++) 191 | { 192 | if (this->frame_id - this->lost_stracks[i].end_frame() > this->max_time_lost) 193 | { 194 | this->lost_stracks[i].mark_removed(); 195 | removed_stracks.push_back(this->lost_stracks[i]); 196 | } 197 | } 198 | 199 | for (int i = 0; i < this->tracked_stracks.size(); i++) 200 | { 201 | if (this->tracked_stracks[i].state == TrackState::Tracked) 202 | { 203 | tracked_stracks_swap.push_back(this->tracked_stracks[i]); 204 | } 205 | } 206 | this->tracked_stracks.clear(); 207 | this->tracked_stracks.assign(tracked_stracks_swap.begin(), tracked_stracks_swap.end()); 208 | 209 | this->tracked_stracks = joint_stracks(this->tracked_stracks, activated_stracks); 210 | this->tracked_stracks = joint_stracks(this->tracked_stracks, refind_stracks); 211 | 212 | //std::cout << activated_stracks.size() << std::endl; 213 | 214 | this->lost_stracks = sub_stracks(this->lost_stracks, this->tracked_stracks); 215 | for (int i = 0; i < lost_stracks.size(); i++) 216 | { 217 | this->lost_stracks.push_back(lost_stracks[i]); 218 | } 219 | 220 | this->lost_stracks = sub_stracks(this->lost_stracks, this->removed_stracks); 221 | for (int i = 0; i < removed_stracks.size(); i++) 222 | { 223 | this->removed_stracks.push_back(removed_stracks[i]); 224 | } 225 | 226 | remove_duplicate_stracks(resa, resb, this->tracked_stracks, this->lost_stracks); 227 | 228 | this->tracked_stracks.clear(); 229 | this->tracked_stracks.assign(resa.begin(), resa.end()); 230 | this->lost_stracks.clear(); 231 | this->lost_stracks.assign(resb.begin(), resb.end()); 232 | 233 | for (int i = 0; i < this->tracked_stracks.size(); i++) 234 | { 235 | if (this->tracked_stracks[i].is_activated) 236 | { 237 | output_stracks.push_back(this->tracked_stracks[i]); 238 | } 239 | } 240 | return output_stracks; 241 | } -------------------------------------------------------------------------------- /bytetrack/src/STrack.cpp: -------------------------------------------------------------------------------- 1 | #include "STrack.h" 2 | 3 | STrack::STrack(vector tlwh_, float score) 4 | { 5 | _tlwh.resize(4); 6 | _tlwh.assign(tlwh_.begin(), tlwh_.end()); 7 | 8 | is_activated = false; 9 | track_id = 0; 10 | state = TrackState::New; 11 | 12 | tlwh.resize(4); 13 | tlbr.resize(4); 14 | 15 | static_tlwh(); 16 | static_tlbr(); 17 | frame_id = 0; 18 | tracklet_len = 0; 19 | this->score = score; 20 | start_frame = 0; 21 | } 22 | 23 | STrack::~STrack() 24 | { 25 | } 26 | 27 | void STrack::activate(byte_kalman::KalmanFilter &kalman_filter, int frame_id) 28 | { 29 | this->kalman_filter = kalman_filter; 30 | this->track_id = this->next_id(); 31 | 32 | vector _tlwh_tmp(4); 33 | _tlwh_tmp[0] = this->_tlwh[0]; 34 | _tlwh_tmp[1] = this->_tlwh[1]; 35 | _tlwh_tmp[2] = this->_tlwh[2]; 36 | _tlwh_tmp[3] = this->_tlwh[3]; 37 | vector xyah = tlwh_to_xyah(_tlwh_tmp); 38 | DETECTBOX xyah_box; 39 | xyah_box[0] = xyah[0]; 40 | xyah_box[1] = xyah[1]; 41 | xyah_box[2] = xyah[2]; 42 | xyah_box[3] = xyah[3]; 43 | auto mc = this->kalman_filter.initiate(xyah_box); 44 | this->mean = mc.first; 45 | this->covariance = mc.second; 46 | 47 | static_tlwh(); 48 | static_tlbr(); 49 | 50 | this->tracklet_len = 0; 51 | this->state = TrackState::Tracked; 52 | if (frame_id == 1) 53 | { 54 | this->is_activated = true; 55 | } 56 | //this->is_activated = true; 57 | this->frame_id = frame_id; 58 | this->start_frame = frame_id; 59 | } 60 | 61 | void STrack::re_activate(STrack &new_track, int frame_id, bool new_id) 62 | { 63 | vector xyah = tlwh_to_xyah(new_track.tlwh); 64 | DETECTBOX xyah_box; 65 | xyah_box[0] = xyah[0]; 66 | xyah_box[1] = xyah[1]; 67 | xyah_box[2] = xyah[2]; 68 | xyah_box[3] = xyah[3]; 69 | auto mc = this->kalman_filter.update(this->mean, this->covariance, xyah_box); 70 | this->mean = mc.first; 71 | this->covariance = mc.second; 72 | 73 | static_tlwh(); 74 | static_tlbr(); 75 | 76 | this->tracklet_len = 0; 77 | this->state = TrackState::Tracked; 78 | this->is_activated = true; 79 | this->frame_id = frame_id; 80 | this->score = new_track.score; 81 | if (new_id) 82 | this->track_id = next_id(); 83 | } 84 | 85 | void STrack::update(STrack &new_track, int frame_id) 86 | { 87 | this->frame_id = frame_id; 88 | this->tracklet_len++; 89 | 90 | vector xyah = tlwh_to_xyah(new_track.tlwh); 91 | DETECTBOX xyah_box; 92 | xyah_box[0] = xyah[0]; 93 | xyah_box[1] = xyah[1]; 94 | xyah_box[2] = xyah[2]; 95 | xyah_box[3] = xyah[3]; 96 | 97 | auto mc = this->kalman_filter.update(this->mean, this->covariance, xyah_box); 98 | this->mean = mc.first; 99 | this->covariance = mc.second; 100 | 101 | static_tlwh(); 102 | static_tlbr(); 103 | 104 | this->state = TrackState::Tracked; 105 | this->is_activated = true; 106 | 107 | this->score = new_track.score; 108 | } 109 | 110 | void STrack::static_tlwh() 111 | { 112 | if (this->state == TrackState::New) 113 | { 114 | tlwh[0] = _tlwh[0]; 115 | tlwh[1] = _tlwh[1]; 116 | tlwh[2] = _tlwh[2]; 117 | tlwh[3] = _tlwh[3]; 118 | return; 119 | } 120 | 121 | tlwh[0] = mean[0]; 122 | tlwh[1] = mean[1]; 123 | tlwh[2] = mean[2]; 124 | tlwh[3] = mean[3]; 125 | 126 | tlwh[2] *= tlwh[3]; 127 | tlwh[0] -= tlwh[2] / 2; 128 | tlwh[1] -= tlwh[3] / 2; 129 | } 130 | 131 | void STrack::static_tlbr() 132 | { 133 | tlbr.clear(); 134 | tlbr.assign(tlwh.begin(), tlwh.end()); 135 | tlbr[2] += tlbr[0]; 136 | tlbr[3] += tlbr[1]; 137 | } 138 | 139 | vector STrack::tlwh_to_xyah(vector tlwh_tmp) 140 | { 141 | vector tlwh_output = tlwh_tmp; 142 | tlwh_output[0] += tlwh_output[2] / 2; 143 | tlwh_output[1] += tlwh_output[3] / 2; 144 | tlwh_output[2] /= tlwh_output[3]; 145 | return tlwh_output; 146 | } 147 | 148 | vector STrack::to_xyah() 149 | { 150 | return tlwh_to_xyah(tlwh); 151 | } 152 | 153 | vector STrack::tlbr_to_tlwh(vector &tlbr) 154 | { 155 | tlbr[2] -= tlbr[0]; 156 | tlbr[3] -= tlbr[1]; 157 | return tlbr; 158 | } 159 | 160 | void STrack::mark_lost() 161 | { 162 | state = TrackState::Lost; 163 | } 164 | 165 | void STrack::mark_removed() 166 | { 167 | state = TrackState::Removed; 168 | } 169 | 170 | int STrack::next_id() 171 | { 172 | static int _count = 0; 173 | _count++; 174 | return _count; 175 | } 176 | 177 | int STrack::end_frame() 178 | { 179 | return this->frame_id; 180 | } 181 | 182 | void STrack::multi_predict(vector &stracks, byte_kalman::KalmanFilter &kalman_filter) 183 | { 184 | for (int i = 0; i < stracks.size(); i++) 185 | { 186 | if (stracks[i]->state != TrackState::Tracked) 187 | { 188 | stracks[i]->mean[7] = 0; 189 | } 190 | kalman_filter.predict(stracks[i]->mean, stracks[i]->covariance); 191 | stracks[i]->static_tlwh(); 192 | stracks[i]->static_tlbr(); 193 | } 194 | } -------------------------------------------------------------------------------- /bytetrack/src/kalmanFilter.cpp: -------------------------------------------------------------------------------- 1 | #include "kalmanFilter.h" 2 | #include 3 | 4 | namespace byte_kalman 5 | { 6 | const double KalmanFilter::chi2inv95[10] = { 7 | 0, 8 | 3.8415, 9 | 5.9915, 10 | 7.8147, 11 | 9.4877, 12 | 11.070, 13 | 12.592, 14 | 14.067, 15 | 15.507, 16 | 16.919 17 | }; 18 | KalmanFilter::KalmanFilter() 19 | { 20 | int ndim = 4; 21 | double dt = 1.; 22 | 23 | _motion_mat = Eigen::MatrixXf::Identity(8, 8); 24 | for (int i = 0; i < ndim; i++) { 25 | _motion_mat(i, ndim + i) = dt; 26 | } 27 | _update_mat = Eigen::MatrixXf::Identity(4, 8); 28 | 29 | this->_std_weight_position = 1. / 20; 30 | this->_std_weight_velocity = 1. / 160; 31 | } 32 | 33 | KAL_DATA KalmanFilter::initiate(const DETECTBOX &measurement) 34 | { 35 | DETECTBOX mean_pos = measurement; 36 | DETECTBOX mean_vel; 37 | for (int i = 0; i < 4; i++) mean_vel(i) = 0; 38 | 39 | KAL_MEAN mean; 40 | for (int i = 0; i < 8; i++) { 41 | if (i < 4) mean(i) = mean_pos(i); 42 | else mean(i) = mean_vel(i - 4); 43 | } 44 | 45 | KAL_MEAN std; 46 | std(0) = 2 * _std_weight_position * measurement[3]; 47 | std(1) = 2 * _std_weight_position * measurement[3]; 48 | std(2) = 1e-2; 49 | std(3) = 2 * _std_weight_position * measurement[3]; 50 | std(4) = 10 * _std_weight_velocity * measurement[3]; 51 | std(5) = 10 * _std_weight_velocity * measurement[3]; 52 | std(6) = 1e-5; 53 | std(7) = 10 * _std_weight_velocity * measurement[3]; 54 | 55 | KAL_MEAN tmp = std.array().square(); 56 | KAL_COVA var = tmp.asDiagonal(); 57 | return std::make_pair(mean, var); 58 | } 59 | 60 | void KalmanFilter::predict(KAL_MEAN &mean, KAL_COVA &covariance) 61 | { 62 | //revise the data; 63 | DETECTBOX std_pos; 64 | std_pos << _std_weight_position * mean(3), 65 | _std_weight_position * mean(3), 66 | 1e-2, 67 | _std_weight_position * mean(3); 68 | DETECTBOX std_vel; 69 | std_vel << _std_weight_velocity * mean(3), 70 | _std_weight_velocity * mean(3), 71 | 1e-5, 72 | _std_weight_velocity * mean(3); 73 | KAL_MEAN tmp; 74 | tmp.block<1, 4>(0, 0) = std_pos; 75 | tmp.block<1, 4>(0, 4) = std_vel; 76 | tmp = tmp.array().square(); 77 | KAL_COVA motion_cov = tmp.asDiagonal(); 78 | KAL_MEAN mean1 = this->_motion_mat * mean.transpose(); 79 | KAL_COVA covariance1 = this->_motion_mat * covariance *(_motion_mat.transpose()); 80 | covariance1 += motion_cov; 81 | 82 | mean = mean1; 83 | covariance = covariance1; 84 | } 85 | 86 | KAL_HDATA KalmanFilter::project(const KAL_MEAN &mean, const KAL_COVA &covariance) 87 | { 88 | DETECTBOX std; 89 | std << _std_weight_position * mean(3), _std_weight_position * mean(3), 90 | 1e-1, _std_weight_position * mean(3); 91 | KAL_HMEAN mean1 = _update_mat * mean.transpose(); 92 | KAL_HCOVA covariance1 = _update_mat * covariance * (_update_mat.transpose()); 93 | Eigen::Matrix diag = std.asDiagonal(); 94 | diag = diag.array().square().matrix(); 95 | covariance1 += diag; 96 | // covariance1.diagonal() << diag; 97 | return std::make_pair(mean1, covariance1); 98 | } 99 | 100 | KAL_DATA 101 | KalmanFilter::update( 102 | const KAL_MEAN &mean, 103 | const KAL_COVA &covariance, 104 | const DETECTBOX &measurement) 105 | { 106 | KAL_HDATA pa = project(mean, covariance); 107 | KAL_HMEAN projected_mean = pa.first; 108 | KAL_HCOVA projected_cov = pa.second; 109 | 110 | //chol_factor, lower = 111 | //scipy.linalg.cho_factor(projected_cov, lower=True, check_finite=False) 112 | //kalmain_gain = 113 | //scipy.linalg.cho_solve((cho_factor, lower), 114 | //np.dot(covariance, self._upadte_mat.T).T, 115 | //check_finite=False).T 116 | Eigen::Matrix B = (covariance * (_update_mat.transpose())).transpose(); 117 | Eigen::Matrix kalman_gain = (projected_cov.llt().solve(B)).transpose(); // eg.8x4 118 | Eigen::Matrix innovation = measurement - projected_mean; //eg.1x4 119 | auto tmp = innovation * (kalman_gain.transpose()); 120 | KAL_MEAN new_mean = (mean.array() + tmp.array()).matrix(); 121 | KAL_COVA new_covariance = covariance - kalman_gain * projected_cov*(kalman_gain.transpose()); 122 | return std::make_pair(new_mean, new_covariance); 123 | } 124 | 125 | Eigen::Matrix 126 | KalmanFilter::gating_distance( 127 | const KAL_MEAN &mean, 128 | const KAL_COVA &covariance, 129 | const std::vector &measurements, 130 | bool only_position) 131 | { 132 | KAL_HDATA pa = this->project(mean, covariance); 133 | if (only_position) { 134 | printf("not implement!"); 135 | exit(0); 136 | } 137 | KAL_HMEAN mean1 = pa.first; 138 | KAL_HCOVA covariance1 = pa.second; 139 | 140 | // Eigen::Matrix d(size, 4); 141 | DETECTBOXSS d(measurements.size(), 4); 142 | int pos = 0; 143 | for (DETECTBOX box : measurements) { 144 | d.row(pos++) = box - mean1; 145 | } 146 | Eigen::Matrix factor = covariance1.llt().matrixL(); 147 | Eigen::Matrix z = factor.triangularView().solve(d).transpose(); 148 | auto zz = ((z.array())*(z.array())).matrix(); 149 | auto square_maha = zz.colwise().sum(); 150 | return square_maha; 151 | } 152 | } -------------------------------------------------------------------------------- /bytetrack/src/lapjv.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "lapjv.h" 6 | 7 | /** Column-reduction and reduction transfer for a dense cost matrix. 8 | */ 9 | int_t _ccrrt_dense(const uint_t n, cost_t *cost[], 10 | int_t *free_rows, int_t *x, int_t *y, cost_t *v) 11 | { 12 | int_t n_free_rows; 13 | boolean *unique; 14 | 15 | for (uint_t i = 0; i < n; i++) { 16 | x[i] = -1; 17 | v[i] = LARGE; 18 | y[i] = 0; 19 | } 20 | for (uint_t i = 0; i < n; i++) { 21 | for (uint_t j = 0; j < n; j++) { 22 | const cost_t c = cost[i][j]; 23 | if (c < v[j]) { 24 | v[j] = c; 25 | y[j] = i; 26 | } 27 | PRINTF("i=%d, j=%d, c[i,j]=%f, v[j]=%f y[j]=%d\n", i, j, c, v[j], y[j]); 28 | } 29 | } 30 | PRINT_COST_ARRAY(v, n); 31 | PRINT_INDEX_ARRAY(y, n); 32 | NEW(unique, boolean, n); 33 | memset(unique, TRUE, n); 34 | { 35 | int_t j = n; 36 | do { 37 | j--; 38 | const int_t i = y[j]; 39 | if (x[i] < 0) { 40 | x[i] = j; 41 | } 42 | else { 43 | unique[i] = FALSE; 44 | y[j] = -1; 45 | } 46 | } while (j > 0); 47 | } 48 | n_free_rows = 0; 49 | for (uint_t i = 0; i < n; i++) { 50 | if (x[i] < 0) { 51 | free_rows[n_free_rows++] = i; 52 | } 53 | else if (unique[i]) { 54 | const int_t j = x[i]; 55 | cost_t min = LARGE; 56 | for (uint_t j2 = 0; j2 < n; j2++) { 57 | if (j2 == (uint_t)j) { 58 | continue; 59 | } 60 | const cost_t c = cost[i][j2] - v[j2]; 61 | if (c < min) { 62 | min = c; 63 | } 64 | } 65 | PRINTF("v[%d] = %f - %f\n", j, v[j], min); 66 | v[j] -= min; 67 | } 68 | } 69 | FREE(unique); 70 | return n_free_rows; 71 | } 72 | 73 | 74 | /** Augmenting row reduction for a dense cost matrix. 75 | */ 76 | int_t _carr_dense( 77 | const uint_t n, cost_t *cost[], 78 | const uint_t n_free_rows, 79 | int_t *free_rows, int_t *x, int_t *y, cost_t *v) 80 | { 81 | uint_t current = 0; 82 | int_t new_free_rows = 0; 83 | uint_t rr_cnt = 0; 84 | PRINT_INDEX_ARRAY(x, n); 85 | PRINT_INDEX_ARRAY(y, n); 86 | PRINT_COST_ARRAY(v, n); 87 | PRINT_INDEX_ARRAY(free_rows, n_free_rows); 88 | while (current < n_free_rows) { 89 | int_t i0; 90 | int_t j1, j2; 91 | cost_t v1, v2, v1_new; 92 | boolean v1_lowers; 93 | 94 | rr_cnt++; 95 | PRINTF("current = %d rr_cnt = %d\n", current, rr_cnt); 96 | const int_t free_i = free_rows[current++]; 97 | j1 = 0; 98 | v1 = cost[free_i][0] - v[0]; 99 | j2 = -1; 100 | v2 = LARGE; 101 | for (uint_t j = 1; j < n; j++) { 102 | PRINTF("%d = %f %d = %f\n", j1, v1, j2, v2); 103 | const cost_t c = cost[free_i][j] - v[j]; 104 | if (c < v2) { 105 | if (c >= v1) { 106 | v2 = c; 107 | j2 = j; 108 | } 109 | else { 110 | v2 = v1; 111 | v1 = c; 112 | j2 = j1; 113 | j1 = j; 114 | } 115 | } 116 | } 117 | i0 = y[j1]; 118 | v1_new = v[j1] - (v2 - v1); 119 | v1_lowers = v1_new < v[j1]; 120 | PRINTF("%d %d 1=%d,%f 2=%d,%f v1'=%f(%d,%g) \n", free_i, i0, j1, v1, j2, v2, v1_new, v1_lowers, v[j1] - v1_new); 121 | if (rr_cnt < current * n) { 122 | if (v1_lowers) { 123 | v[j1] = v1_new; 124 | } 125 | else if (i0 >= 0 && j2 >= 0) { 126 | j1 = j2; 127 | i0 = y[j2]; 128 | } 129 | if (i0 >= 0) { 130 | if (v1_lowers) { 131 | free_rows[--current] = i0; 132 | } 133 | else { 134 | free_rows[new_free_rows++] = i0; 135 | } 136 | } 137 | } 138 | else { 139 | PRINTF("rr_cnt=%d >= %d (current=%d * n=%d)\n", rr_cnt, current * n, current, n); 140 | if (i0 >= 0) { 141 | free_rows[new_free_rows++] = i0; 142 | } 143 | } 144 | x[free_i] = j1; 145 | y[j1] = free_i; 146 | } 147 | return new_free_rows; 148 | } 149 | 150 | 151 | /** Find columns with minimum d[j] and put them on the SCAN list. 152 | */ 153 | uint_t _find_dense(const uint_t n, uint_t lo, cost_t *d, int_t *cols, int_t *y) 154 | { 155 | uint_t hi = lo + 1; 156 | cost_t mind = d[cols[lo]]; 157 | for (uint_t k = hi; k < n; k++) { 158 | int_t j = cols[k]; 159 | if (d[j] <= mind) { 160 | if (d[j] < mind) { 161 | hi = lo; 162 | mind = d[j]; 163 | } 164 | cols[k] = cols[hi]; 165 | cols[hi++] = j; 166 | } 167 | } 168 | return hi; 169 | } 170 | 171 | 172 | // Scan all columns in TODO starting from arbitrary column in SCAN 173 | // and try to decrease d of the TODO columns using the SCAN column. 174 | int_t _scan_dense(const uint_t n, cost_t *cost[], 175 | uint_t *plo, uint_t*phi, 176 | cost_t *d, int_t *cols, int_t *pred, 177 | int_t *y, cost_t *v) 178 | { 179 | uint_t lo = *plo; 180 | uint_t hi = *phi; 181 | cost_t h, cred_ij; 182 | 183 | while (lo != hi) { 184 | int_t j = cols[lo++]; 185 | const int_t i = y[j]; 186 | const cost_t mind = d[j]; 187 | h = cost[i][j] - v[j] - mind; 188 | PRINTF("i=%d j=%d h=%f\n", i, j, h); 189 | // For all columns in TODO 190 | for (uint_t k = hi; k < n; k++) { 191 | j = cols[k]; 192 | cred_ij = cost[i][j] - v[j] - h; 193 | if (cred_ij < d[j]) { 194 | d[j] = cred_ij; 195 | pred[j] = i; 196 | if (cred_ij == mind) { 197 | if (y[j] < 0) { 198 | return j; 199 | } 200 | cols[k] = cols[hi]; 201 | cols[hi++] = j; 202 | } 203 | } 204 | } 205 | } 206 | *plo = lo; 207 | *phi = hi; 208 | return -1; 209 | } 210 | 211 | 212 | /** Single iteration of modified Dijkstra shortest path algorithm as explained in the JV paper. 213 | * 214 | * This is a dense matrix version. 215 | * 216 | * \return The closest free column index. 217 | */ 218 | int_t find_path_dense( 219 | const uint_t n, cost_t *cost[], 220 | const int_t start_i, 221 | int_t *y, cost_t *v, 222 | int_t *pred) 223 | { 224 | uint_t lo = 0, hi = 0; 225 | int_t final_j = -1; 226 | uint_t n_ready = 0; 227 | int_t *cols; 228 | cost_t *d; 229 | 230 | NEW(cols, int_t, n); 231 | NEW(d, cost_t, n); 232 | 233 | for (uint_t i = 0; i < n; i++) { 234 | cols[i] = i; 235 | pred[i] = start_i; 236 | d[i] = cost[start_i][i] - v[i]; 237 | } 238 | PRINT_COST_ARRAY(d, n); 239 | while (final_j == -1) { 240 | // No columns left on the SCAN list. 241 | if (lo == hi) { 242 | PRINTF("%d..%d -> find\n", lo, hi); 243 | n_ready = lo; 244 | hi = _find_dense(n, lo, d, cols, y); 245 | PRINTF("check %d..%d\n", lo, hi); 246 | PRINT_INDEX_ARRAY(cols, n); 247 | for (uint_t k = lo; k < hi; k++) { 248 | const int_t j = cols[k]; 249 | if (y[j] < 0) { 250 | final_j = j; 251 | } 252 | } 253 | } 254 | if (final_j == -1) { 255 | PRINTF("%d..%d -> scan\n", lo, hi); 256 | final_j = _scan_dense( 257 | n, cost, &lo, &hi, d, cols, pred, y, v); 258 | PRINT_COST_ARRAY(d, n); 259 | PRINT_INDEX_ARRAY(cols, n); 260 | PRINT_INDEX_ARRAY(pred, n); 261 | } 262 | } 263 | 264 | PRINTF("found final_j=%d\n", final_j); 265 | PRINT_INDEX_ARRAY(cols, n); 266 | { 267 | const cost_t mind = d[cols[lo]]; 268 | for (uint_t k = 0; k < n_ready; k++) { 269 | const int_t j = cols[k]; 270 | v[j] += d[j] - mind; 271 | } 272 | } 273 | 274 | FREE(cols); 275 | FREE(d); 276 | 277 | return final_j; 278 | } 279 | 280 | 281 | /** Augment for a dense cost matrix. 282 | */ 283 | int_t _ca_dense( 284 | const uint_t n, cost_t *cost[], 285 | const uint_t n_free_rows, 286 | int_t *free_rows, int_t *x, int_t *y, cost_t *v) 287 | { 288 | int_t *pred; 289 | 290 | NEW(pred, int_t, n); 291 | 292 | for (int_t *pfree_i = free_rows; pfree_i < free_rows + n_free_rows; pfree_i++) { 293 | int_t i = -1, j; 294 | uint_t k = 0; 295 | 296 | PRINTF("looking at free_i=%d\n", *pfree_i); 297 | j = find_path_dense(n, cost, *pfree_i, y, v, pred); 298 | ASSERT(j >= 0); 299 | ASSERT(j < n); 300 | while (i != *pfree_i) { 301 | PRINTF("augment %d\n", j); 302 | PRINT_INDEX_ARRAY(pred, n); 303 | i = pred[j]; 304 | PRINTF("y[%d]=%d -> %d\n", j, y[j], i); 305 | y[j] = i; 306 | PRINT_INDEX_ARRAY(x, n); 307 | SWAP_INDICES(j, x[i]); 308 | k++; 309 | if (k >= n) { 310 | ASSERT(FALSE); 311 | } 312 | } 313 | } 314 | FREE(pred); 315 | return 0; 316 | } 317 | 318 | 319 | /** Solve dense sparse LAP. 320 | */ 321 | int lapjv_internal( 322 | const uint_t n, cost_t *cost[], 323 | int_t *x, int_t *y) 324 | { 325 | int ret; 326 | int_t *free_rows; 327 | cost_t *v; 328 | 329 | NEW(free_rows, int_t, n); 330 | NEW(v, cost_t, n); 331 | ret = _ccrrt_dense(n, cost, free_rows, x, y, v); 332 | int i = 0; 333 | while (ret > 0 && i < 2) { 334 | ret = _carr_dense(n, cost, ret, free_rows, x, y, v); 335 | i++; 336 | } 337 | if (ret > 0) { 338 | ret = _ca_dense(n, cost, ret, free_rows, x, y, v); 339 | } 340 | FREE(v); 341 | FREE(free_rows); 342 | return ret; 343 | } -------------------------------------------------------------------------------- /bytetrack/src/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "BYTETracker.h" 2 | #include "lapjv.h" 3 | 4 | vector BYTETracker::joint_stracks(vector &tlista, vector &tlistb) 5 | { 6 | map exists; 7 | vector res; 8 | for (int i = 0; i < tlista.size(); i++) 9 | { 10 | exists.insert(pair(tlista[i]->track_id, 1)); 11 | res.push_back(tlista[i]); 12 | } 13 | for (int i = 0; i < tlistb.size(); i++) 14 | { 15 | int tid = tlistb[i].track_id; 16 | if (!exists[tid] || exists.count(tid) == 0) 17 | { 18 | exists[tid] = 1; 19 | res.push_back(&tlistb[i]); 20 | } 21 | } 22 | return res; 23 | } 24 | 25 | vector BYTETracker::joint_stracks(vector &tlista, vector &tlistb) 26 | { 27 | map exists; 28 | vector res; 29 | for (int i = 0; i < tlista.size(); i++) 30 | { 31 | exists.insert(pair(tlista[i].track_id, 1)); 32 | res.push_back(tlista[i]); 33 | } 34 | for (int i = 0; i < tlistb.size(); i++) 35 | { 36 | int tid = tlistb[i].track_id; 37 | if (!exists[tid] || exists.count(tid) == 0) 38 | { 39 | exists[tid] = 1; 40 | res.push_back(tlistb[i]); 41 | } 42 | } 43 | return res; 44 | } 45 | 46 | vector BYTETracker::sub_stracks(vector &tlista, vector &tlistb) 47 | { 48 | map stracks; 49 | for (int i = 0; i < tlista.size(); i++) 50 | { 51 | stracks.insert(pair(tlista[i].track_id, tlista[i])); 52 | } 53 | for (int i = 0; i < tlistb.size(); i++) 54 | { 55 | int tid = tlistb[i].track_id; 56 | if (stracks.count(tid) != 0) 57 | { 58 | stracks.erase(tid); 59 | } 60 | } 61 | 62 | vector res; 63 | std::map::iterator it; 64 | for (it = stracks.begin(); it != stracks.end(); ++it) 65 | { 66 | res.push_back(it->second); 67 | } 68 | 69 | return res; 70 | } 71 | 72 | void BYTETracker::remove_duplicate_stracks(vector &resa, vector &resb, vector &stracksa, vector &stracksb) 73 | { 74 | vector > pdist = iou_distance(stracksa, stracksb); 75 | vector > pairs; 76 | for (int i = 0; i < pdist.size(); i++) 77 | { 78 | for (int j = 0; j < pdist[i].size(); j++) 79 | { 80 | if (pdist[i][j] < 0.15) 81 | { 82 | pairs.push_back(pair(i, j)); 83 | } 84 | } 85 | } 86 | 87 | vector dupa, dupb; 88 | for (int i = 0; i < pairs.size(); i++) 89 | { 90 | int timep = stracksa[pairs[i].first].frame_id - stracksa[pairs[i].first].start_frame; 91 | int timeq = stracksb[pairs[i].second].frame_id - stracksb[pairs[i].second].start_frame; 92 | if (timep > timeq) 93 | dupb.push_back(pairs[i].second); 94 | else 95 | dupa.push_back(pairs[i].first); 96 | } 97 | 98 | for (int i = 0; i < stracksa.size(); i++) 99 | { 100 | vector::iterator iter = find(dupa.begin(), dupa.end(), i); 101 | if (iter == dupa.end()) 102 | { 103 | resa.push_back(stracksa[i]); 104 | } 105 | } 106 | 107 | for (int i = 0; i < stracksb.size(); i++) 108 | { 109 | vector::iterator iter = find(dupb.begin(), dupb.end(), i); 110 | if (iter == dupb.end()) 111 | { 112 | resb.push_back(stracksb[i]); 113 | } 114 | } 115 | } 116 | 117 | void BYTETracker::linear_assignment(vector > &cost_matrix, int cost_matrix_size, int cost_matrix_size_size, float thresh, 118 | vector > &matches, vector &unmatched_a, vector &unmatched_b) 119 | { 120 | if (cost_matrix.size() == 0) 121 | { 122 | for (int i = 0; i < cost_matrix_size; i++) 123 | { 124 | unmatched_a.push_back(i); 125 | } 126 | for (int i = 0; i < cost_matrix_size_size; i++) 127 | { 128 | unmatched_b.push_back(i); 129 | } 130 | return; 131 | } 132 | 133 | vector rowsol; vector colsol; 134 | float c = lapjv(cost_matrix, rowsol, colsol, true, thresh); 135 | for (int i = 0; i < rowsol.size(); i++) 136 | { 137 | if (rowsol[i] >= 0) 138 | { 139 | vector match; 140 | match.push_back(i); 141 | match.push_back(rowsol[i]); 142 | matches.push_back(match); 143 | } 144 | else 145 | { 146 | unmatched_a.push_back(i); 147 | } 148 | } 149 | 150 | for (int i = 0; i < colsol.size(); i++) 151 | { 152 | if (colsol[i] < 0) 153 | { 154 | unmatched_b.push_back(i); 155 | } 156 | } 157 | } 158 | 159 | vector > BYTETracker::ious(vector > &atlbrs, vector > &btlbrs) 160 | { 161 | vector > ious; 162 | if (atlbrs.size()*btlbrs.size() == 0) 163 | return ious; 164 | 165 | ious.resize(atlbrs.size()); 166 | for (int i = 0; i < ious.size(); i++) 167 | { 168 | ious[i].resize(btlbrs.size()); 169 | } 170 | 171 | //bbox_ious 172 | for (int k = 0; k < btlbrs.size(); k++) 173 | { 174 | vector ious_tmp; 175 | float box_area = (btlbrs[k][2] - btlbrs[k][0] + 1)*(btlbrs[k][3] - btlbrs[k][1] + 1); 176 | for (int n = 0; n < atlbrs.size(); n++) 177 | { 178 | float iw = min(atlbrs[n][2], btlbrs[k][2]) - max(atlbrs[n][0], btlbrs[k][0]) + 1; 179 | if (iw > 0) 180 | { 181 | float ih = min(atlbrs[n][3], btlbrs[k][3]) - max(atlbrs[n][1], btlbrs[k][1]) + 1; 182 | if(ih > 0) 183 | { 184 | float ua = (atlbrs[n][2] - atlbrs[n][0] + 1)*(atlbrs[n][3] - atlbrs[n][1] + 1) + box_area - iw * ih; 185 | ious[n][k] = iw * ih / ua; 186 | } 187 | else 188 | { 189 | ious[n][k] = 0.0; 190 | } 191 | } 192 | else 193 | { 194 | ious[n][k] = 0.0; 195 | } 196 | } 197 | } 198 | 199 | return ious; 200 | } 201 | 202 | vector > BYTETracker::iou_distance(vector &atracks, vector &btracks, int &dist_size, int &dist_size_size) 203 | { 204 | vector > cost_matrix; 205 | if (atracks.size() * btracks.size() == 0) 206 | { 207 | dist_size = atracks.size(); 208 | dist_size_size = btracks.size(); 209 | return cost_matrix; 210 | } 211 | vector > atlbrs, btlbrs; 212 | for (int i = 0; i < atracks.size(); i++) 213 | { 214 | atlbrs.push_back(atracks[i]->tlbr); 215 | } 216 | for (int i = 0; i < btracks.size(); i++) 217 | { 218 | btlbrs.push_back(btracks[i].tlbr); 219 | } 220 | 221 | dist_size = atracks.size(); 222 | dist_size_size = btracks.size(); 223 | 224 | vector > _ious = ious(atlbrs, btlbrs); 225 | 226 | for (int i = 0; i < _ious.size();i++) 227 | { 228 | vector _iou; 229 | for (int j = 0; j < _ious[i].size(); j++) 230 | { 231 | _iou.push_back(1 - _ious[i][j]); 232 | } 233 | cost_matrix.push_back(_iou); 234 | } 235 | 236 | return cost_matrix; 237 | } 238 | 239 | vector > BYTETracker::iou_distance(vector &atracks, vector &btracks) 240 | { 241 | vector > atlbrs, btlbrs; 242 | for (int i = 0; i < atracks.size(); i++) 243 | { 244 | atlbrs.push_back(atracks[i].tlbr); 245 | } 246 | for (int i = 0; i < btracks.size(); i++) 247 | { 248 | btlbrs.push_back(btracks[i].tlbr); 249 | } 250 | 251 | vector > _ious = ious(atlbrs, btlbrs); 252 | vector > cost_matrix; 253 | for (int i = 0; i < _ious.size(); i++) 254 | { 255 | vector _iou; 256 | for (int j = 0; j < _ious[i].size(); j++) 257 | { 258 | _iou.push_back(1 - _ious[i][j]); 259 | } 260 | cost_matrix.push_back(_iou); 261 | } 262 | 263 | return cost_matrix; 264 | } 265 | 266 | double BYTETracker::lapjv(const vector > &cost, vector &rowsol, vector &colsol, 267 | bool extend_cost, float cost_limit, bool return_cost) 268 | { 269 | vector > cost_c; 270 | cost_c.assign(cost.begin(), cost.end()); 271 | 272 | vector > cost_c_extended; 273 | 274 | int n_rows = cost.size(); 275 | int n_cols = cost[0].size(); 276 | rowsol.resize(n_rows); 277 | colsol.resize(n_cols); 278 | 279 | int n = 0; 280 | if (n_rows == n_cols) 281 | { 282 | n = n_rows; 283 | } 284 | else 285 | { 286 | if (!extend_cost) 287 | { 288 | cout << "set extend_cost=True" << endl; 289 | system("pause"); 290 | exit(0); 291 | } 292 | } 293 | 294 | if (extend_cost || cost_limit < LONG_MAX) 295 | { 296 | n = n_rows + n_cols; 297 | cost_c_extended.resize(n); 298 | for (int i = 0; i < cost_c_extended.size(); i++) 299 | cost_c_extended[i].resize(n); 300 | 301 | if (cost_limit < LONG_MAX) 302 | { 303 | for (int i = 0; i < cost_c_extended.size(); i++) 304 | { 305 | for (int j = 0; j < cost_c_extended[i].size(); j++) 306 | { 307 | cost_c_extended[i][j] = cost_limit / 2.0; 308 | } 309 | } 310 | } 311 | else 312 | { 313 | float cost_max = -1; 314 | for (int i = 0; i < cost_c.size(); i++) 315 | { 316 | for (int j = 0; j < cost_c[i].size(); j++) 317 | { 318 | if (cost_c[i][j] > cost_max) 319 | cost_max = cost_c[i][j]; 320 | } 321 | } 322 | for (int i = 0; i < cost_c_extended.size(); i++) 323 | { 324 | for (int j = 0; j < cost_c_extended[i].size(); j++) 325 | { 326 | cost_c_extended[i][j] = cost_max + 1; 327 | } 328 | } 329 | } 330 | 331 | for (int i = n_rows; i < cost_c_extended.size(); i++) 332 | { 333 | for (int j = n_cols; j < cost_c_extended[i].size(); j++) 334 | { 335 | cost_c_extended[i][j] = 0; 336 | } 337 | } 338 | for (int i = 0; i < n_rows; i++) 339 | { 340 | for (int j = 0; j < n_cols; j++) 341 | { 342 | cost_c_extended[i][j] = cost_c[i][j]; 343 | } 344 | } 345 | 346 | cost_c.clear(); 347 | cost_c.assign(cost_c_extended.begin(), cost_c_extended.end()); 348 | } 349 | 350 | double **cost_ptr; 351 | cost_ptr = new double *[sizeof(double *) * n]; 352 | for (int i = 0; i < n; i++) 353 | cost_ptr[i] = new double[sizeof(double) * n]; 354 | 355 | for (int i = 0; i < n; i++) 356 | { 357 | for (int j = 0; j < n; j++) 358 | { 359 | cost_ptr[i][j] = cost_c[i][j]; 360 | } 361 | } 362 | 363 | int* x_c = new int[sizeof(int) * n]; 364 | int *y_c = new int[sizeof(int) * n]; 365 | 366 | int ret = lapjv_internal(n, cost_ptr, x_c, y_c); 367 | if (ret != 0) 368 | { 369 | cout << "Calculate Wrong!" << endl; 370 | system("pause"); 371 | exit(0); 372 | } 373 | 374 | double opt = 0.0; 375 | 376 | if (n != n_rows) 377 | { 378 | for (int i = 0; i < n; i++) 379 | { 380 | if (x_c[i] >= n_cols) 381 | x_c[i] = -1; 382 | if (y_c[i] >= n_rows) 383 | y_c[i] = -1; 384 | } 385 | for (int i = 0; i < n_rows; i++) 386 | { 387 | rowsol[i] = x_c[i]; 388 | } 389 | for (int i = 0; i < n_cols; i++) 390 | { 391 | colsol[i] = y_c[i]; 392 | } 393 | 394 | if (return_cost) 395 | { 396 | for (int i = 0; i < rowsol.size(); i++) 397 | { 398 | if (rowsol[i] != -1) 399 | { 400 | //cout << i << "\t" << rowsol[i] << "\t" << cost_ptr[i][rowsol[i]] << endl; 401 | opt += cost_ptr[i][rowsol[i]]; 402 | } 403 | } 404 | } 405 | } 406 | else if (return_cost) 407 | { 408 | for (int i = 0; i < rowsol.size(); i++) 409 | { 410 | opt += cost_ptr[i][rowsol[i]]; 411 | } 412 | } 413 | 414 | for (int i = 0; i < n; i++) 415 | { 416 | delete[]cost_ptr[i]; 417 | } 418 | delete[]cost_ptr; 419 | delete[]x_c; 420 | delete[]y_c; 421 | 422 | return opt; 423 | } 424 | 425 | Scalar BYTETracker::get_color(int idx) 426 | { 427 | idx += 3; 428 | return Scalar(37 * idx % 255, 17 * idx % 255, 29 * idx % 255); 429 | } -------------------------------------------------------------------------------- /configs/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "Server": { 3 | "server_ip": "0.0.0.0" , 4 | "server_port": 9999, 5 | "work_dir": "work_dir", 6 | "ssl_crt_path": "/path/your/server.crt", 7 | "ssl_key_path": "/path/your/server_rsa_private.pem.unsecure" 8 | }, 9 | "Log": { 10 | "log_dir": "log", 11 | "log_file_time_format": "%Y-%m-%d_%H:%M:%S", 12 | "log_add_file_verbosity": "INFO", 13 | "log_all_file_verbosity": "INFO" 14 | }, 15 | "Detector": { 16 | "mode": "TrackerDetector", 17 | "ObjectDetector": { 18 | "model": "/path/to/your/model", 19 | "device": "cuda", 20 | "device_id": 0, 21 | "extra_config": { 22 | "score_thre": 0.3, 23 | "into_contour_time_gap_second": 5, 24 | "out_contour_time_gap_second": 20, 25 | "imshow_result_image": true, 26 | "class_names": ["cat"] 27 | } 28 | }, 29 | "TrackerDetector": { 30 | "model": "/path/your/model", 31 | "device": "cuda", 32 | "device_id": 0, 33 | "extra_config": { 34 | "score_thre": 0.3, 35 | "tracker_buffer": 30, 36 | "into_contour_time_gap_second": 5, 37 | "out_contour_time_gap_second": 20, 38 | "imshow_result_image": true, 39 | "wh_ratio_thre_to_show": 1.6, 40 | "wh_multiply_thre_to_show": 20, 41 | "class_names": ["cat"] 42 | } 43 | } 44 | }, 45 | "LiveGo": { 46 | "camera_push_port": 5544, 47 | "dect_push_port": 1935, 48 | "state_check_port": 8083 49 | }, 50 | "DB": { 51 | "user_name": "root", 52 | "user_password": "9696", 53 | "db_server_ip": "127.0.0.1", 54 | "db_server_port": 3306 55 | }, 56 | "Timer": { 57 | "interval_to_watch_detector_second": 60, 58 | "interval_to_watch_file_second":5, 59 | "max_video_file_save_day": 2, 60 | "max_detector_live_day": 365 61 | } 62 | } -------------------------------------------------------------------------------- /include/common.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMMON_H 2 | #define _COMMON_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | 35 | namespace GLCC { 36 | extern const float color_list[][3]; 37 | enum Key {ESC=27}; 38 | enum CancelMode {NORMAL_CANCEL=0, FORCE_CANCEL=1, WAKE_CANCEL=2}; 39 | enum RegisterMode {Create_Register=0, Judge_Register=1}; 40 | namespace constants { 41 | extern const std::string localhost; 42 | extern const std::string mysql_create_db_command; 43 | extern const long num_millisecond_per_second; 44 | extern const long num_microsecond_per_second; 45 | extern const long num_second_per_minute; 46 | 47 | extern long long interval_to_watch_detector_second; 48 | extern long long interval_to_watch_file_second; 49 | extern long max_detector_live_day; 50 | extern long max_video_file_save_day; 51 | 52 | extern std::string file_time_format; 53 | extern std::string livego_check_stat_template; 54 | extern std::string livego_push_url_template; 55 | extern std::string livego_kick_url; 56 | extern std::string ffmpeg_push_command; 57 | extern std::string ffmpeg_file_push_command; 58 | extern std::string video_path_template; 59 | 60 | extern std::string cover_save_suffix; 61 | extern const std::vector video_suffixes; 62 | extern const std::vector video_prefixes; 63 | 64 | extern std::string mysql_root_url; 65 | extern std::string mysql_glccserver_url; 66 | } 67 | 68 | typedef struct ssl_context { 69 | std::string ssl_crt_path; 70 | std::string ssl_key_path; 71 | } ssl_context_t; 72 | 73 | typedef struct server_dir { 74 | std::string work_dir; 75 | std::string user_dir; 76 | } server_dir_t; 77 | 78 | typedef struct url_context { 79 | std::string ip; 80 | u_int16_t port; 81 | } url_context_t; 82 | 83 | typedef struct video_context { 84 | std::string video_path; 85 | std::string video_name; 86 | bool use_template_path=false; 87 | } video_context_t; 88 | 89 | typedef struct livego_context { 90 | std::string room_name; 91 | std::string livego_push_url; 92 | } livego_context_t; 93 | 94 | typedef struct detector_init_context { 95 | std::string model_path=nullptr; // camera 0 96 | std::string device_name="cpu"; // cuda 97 | int device_id=0; // 0 98 | } detector_init_context_t; 99 | 100 | typedef struct detector_run_context { 101 | std::string video_path; // video to be sampled 102 | std::string upload_path; // rtmp to be load 103 | Json::Value vis_params; 104 | } detector_run_context_t; 105 | 106 | typedef struct glcc_server_context { 107 | int state; 108 | int error; 109 | // Base 110 | url_context_t server_context; 111 | url_context_t client_context; 112 | server_dir_t server_dir; 113 | video_context_t video_context; 114 | livego_context_t livego_context; 115 | // Detector 116 | Json::Value detector_init_context; 117 | detector_run_context_t detector_run_context; 118 | // Extra info 119 | Json::Value extra_info; 120 | } glcc_server_context_t; 121 | 122 | template 123 | class ProductFactory 124 | { 125 | public: 126 | // delete all copy 127 | ProductFactory(const ProductFactory &) = delete; 128 | ProductFactory(const ProductFactory &&) = delete; 129 | const ProductFactory& operator=(const ProductFactory &) = delete; 130 | const ProductFactory& operator=(const ProductFactory &&) = delete; 131 | static std::mutex lock; 132 | 133 | static ProductFactory &Instance() { 134 | static ProductFactory instance; 135 | return instance; 136 | } 137 | 138 | static ProductFactory *Instance_ptr() { 139 | return &Instance(); 140 | } 141 | 142 | void RegisterProduct(const std::string & name, ProductType_t *registrar) 143 | { 144 | std::lock_guard lock_guard(lock); 145 | m_ProductRegistry[name] = registrar; 146 | } 147 | 148 | void RegisterProduct(const std::string & name, std::function init_func, void * init_args) 149 | { 150 | std::lock_guard lock_guard(lock); 151 | m_ProductRegistry[name] = init_func(init_args); 152 | } 153 | 154 | int EraseProductDel(const std::string & name) { 155 | std::lock_guard lock_guard(lock); 156 | if (m_ProductRegistry.find(name) != m_ProductRegistry.end()) { 157 | ProductType_t * elem = m_ProductRegistry[name]; 158 | int n = m_ProductRegistry.erase(name); 159 | delete elem; 160 | return n; 161 | } 162 | return m_ProductRegistry.erase(name); 163 | } 164 | int EraseProduct(const std::string & name) { 165 | std::lock_guard lock_guard(lock); 166 | return m_ProductRegistry.erase(name); 167 | } 168 | 169 | ProductType_t *GetProduct(const std::string & name) 170 | { 171 | std::lock_guard lock_guard(lock); 172 | if (m_ProductRegistry.find(name) != m_ProductRegistry.end()) 173 | { 174 | return m_ProductRegistry[name]; 175 | } 176 | return nullptr; 177 | } 178 | 179 | ProductType_t *GetProduct(const char * name) 180 | { 181 | std::string _name = name; 182 | return GetProduct(_name); 183 | } 184 | 185 | private: 186 | ProductFactory() {} 187 | ~ProductFactory() {} 188 | 189 | std::unordered_map m_ProductRegistry; 190 | }; 191 | 192 | template 193 | std::mutex ProductFactory::lock; 194 | 195 | 196 | std::string get_now_time(const std::string & time_format) noexcept ; 197 | 198 | std::string join(std::vector & strings, std::string delim, 199 | std::function = nullptr); 200 | 201 | int check_dir(const std::string & check_path, const bool is_mkdir=false) noexcept; 202 | 203 | int parse_path(const std::string & path, std::unordered_map & result_map) noexcept; 204 | 205 | int read_file_list(const std::string & base_path, std::vector files) noexcept; 206 | 207 | int check_file(const std::string & check_path, 208 | std::vector * file_set = nullptr, 209 | const std::vector suffix={}) noexcept; 210 | int get_cwd(std::string & file_path) noexcept; 211 | 212 | 213 | } 214 | 215 | #endif -------------------------------------------------------------------------------- /include/dealtor.h: -------------------------------------------------------------------------------- 1 | #ifndef _DEALTOR_H 2 | #define _DEALTOR_H 3 | #include 4 | #include 5 | #include 6 | #include "loguru.hpp" 7 | #include "detector.h" 8 | #include "common.h" 9 | #include "BYTETracker.h" 10 | 11 | 12 | namespace GLCC{ 13 | 14 | class Detector { 15 | public: 16 | std::atomic_int32_t state{0}; 17 | std::atomic_bool is_put_lattice{true}; 18 | std::mutex resource_lock; 19 | std::unordered_map> contour_list; 20 | std::string resource_dir; 21 | virtual int run( 22 | void * args, 23 | std::function cancel_func = nullptr, 24 | std::function deal_func = nullptr) = 0; 25 | virtual ~Detector() {} 26 | protected: 27 | }; 28 | 29 | class ObjectDetector: protected Detector { 30 | public: 31 | ObjectDetector(const char * model_path, 32 | const char * device_name, 33 | const int device_id); 34 | ObjectDetector(const std::string & model_path, 35 | const std::string & device_name, 36 | const int device_id); 37 | ~ObjectDetector(); 38 | cv::Scalar get_color(); 39 | int dect(cv::Mat & img, std::vector & objects, float score_thre); 40 | int run(void * args, 41 | std::function cancel_func = nullptr, 42 | std::function deal_func = nullptr) override; 43 | 44 | static Detector * init_func(void * init_args); 45 | protected: 46 | mm_handle_t detector; 47 | }; 48 | 49 | class TrackerDetector: protected ObjectDetector { 50 | public: 51 | TrackerDetector(const char * model_path, 52 | const char * device_name, 53 | const int device_id); 54 | TrackerDetector(const std::string & model_path, 55 | const std::string & device_name, 56 | const int device_id); 57 | ~TrackerDetector(); 58 | 59 | int dect(cv::Mat & img, std::vector & objects, float score_thre); 60 | 61 | int run(void * args, 62 | std::function cancel_func = nullptr, 63 | std::function deal_func = nullptr) override; 64 | static Detector * init_func(void * init_args); 65 | }; 66 | } 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /include/loguru.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Loguru logging library for C++, by Emil Ernerfeldt. 3 | www.github.com/emilk/loguru 4 | If you find Loguru useful, please let me know on twitter or in a mail! 5 | Twitter: @ernerfeldt 6 | Mail: emil.ernerfeldt@gmail.com 7 | Website: www.ilikebigbits.com 8 | 9 | # License 10 | This software is in the public domain. Where that dedication is not 11 | recognized, you are granted a perpetual, irrevocable license to 12 | copy, modify and distribute it as you see fit. 13 | 14 | # Inspiration 15 | Much of Loguru was inspired by GLOG, https://code.google.com/p/google-glog/. 16 | The choice of public domain is fully due Sean T. Barrett 17 | and his wonderful stb libraries at https://github.com/nothings/stb. 18 | 19 | # Version history 20 | * Version 0.1.0 - 2015-03-22 - Works great on Mac. 21 | * Version 0.2.0 - 2015-09-17 - Removed the only dependency. 22 | * Version 0.3.0 - 2015-10-02 - Drop-in replacement for most of GLOG 23 | * Version 0.4.0 - 2015-10-07 - Single-file! 24 | * Version 0.5.0 - 2015-10-17 - Improved file logging 25 | * Version 0.6.0 - 2015-10-24 - Add stack traces 26 | * Version 0.7.0 - 2015-10-27 - Signals 27 | * Version 0.8.0 - 2015-10-30 - Color logging. 28 | * Version 0.9.0 - 2015-11-26 - ABORT_S and proper handling of FATAL 29 | * Version 1.0.0 - 2016-02-14 - ERROR_CONTEXT 30 | * Version 1.1.0 - 2016-02-19 - -v OFF, -v INFO etc 31 | * Version 1.1.1 - 2016-02-20 - textprintf vs strprintf 32 | * Version 1.1.2 - 2016-02-22 - Remove g_alsologtostderr 33 | * Version 1.1.3 - 2016-02-29 - ERROR_CONTEXT as linked list 34 | * Version 1.2.0 - 2016-03-19 - Add get_thread_name() 35 | * Version 1.2.1 - 2016-03-20 - Minor fixes 36 | * Version 1.2.2 - 2016-03-29 - Fix issues with set_fatal_handler throwing an exception 37 | * Version 1.2.3 - 2016-05-16 - Log current working directory in loguru::init(). 38 | * Version 1.2.4 - 2016-05-18 - Custom replacement for -v in loguru::init() by bjoernpollex 39 | * Version 1.2.5 - 2016-05-18 - Add ability to print ERROR_CONTEXT of parent thread. 40 | * Version 1.2.6 - 2016-05-19 - Bug fix regarding VLOG verbosity argument lacking (). 41 | * Version 1.2.7 - 2016-05-23 - Fix PATH_MAX problem. 42 | * Version 1.2.8 - 2016-05-26 - Add shutdown() and remove_all_callbacks() 43 | * Version 1.2.9 - 2016-06-09 - Use a monotonic clock for uptime. 44 | * Version 1.3.0 - 2016-07-20 - Fix issues with callback flush/close not being called. 45 | * Version 1.3.1 - 2016-07-20 - Add LOGURU_UNSAFE_SIGNAL_HANDLER to toggle stacktrace on signals. 46 | * Version 1.3.2 - 2016-07-20 - Add loguru::arguments() 47 | * Version 1.4.0 - 2016-09-15 - Semantic versioning + add loguru::create_directories 48 | * Version 1.4.1 - 2016-09-29 - Customize formating with LOGURU_FILENAME_WIDTH 49 | * Version 1.5.0 - 2016-12-22 - LOGURU_USE_FMTLIB by kolis and LOGURU_WITH_FILEABS by scinart 50 | * Version 1.5.1 - 2017-08-08 - Terminal colors on Windows 10 thanks to looki 51 | * Version 1.6.0 - 2018-01-03 - Add LOGURU_RTTI and LOGURU_STACKTRACES settings 52 | * Version 1.7.0 - 2018-01-03 - Add ability to turn off the preamble with loguru::g_preamble 53 | * Version 1.7.1 - 2018-04-05 - Add function get_fatal_handler 54 | * Version 1.7.2 - 2018-04-22 - Fix a bug where large file names could cause stack corruption (thanks @ccamporesi) 55 | * Version 1.8.0 - 2018-04-23 - Shorten long file names to keep preamble fixed width 56 | * Version 1.9.0 - 2018-09-22 - Adjust terminal colors, add LOGURU_VERBOSE_SCOPE_ENDINGS, add LOGURU_SCOPE_TIME_PRECISION, add named log levels 57 | * Version 2.0.0 - 2018-09-22 - Split loguru.hpp into loguru.hpp and loguru.cpp 58 | * Version 2.1.0 - 2019-09-23 - Update fmtlib + add option to loguru::init to NOT set main thread name. 59 | * Version 2.2.0 - 2020-07-31 - Replace LOGURU_CATCH_SIGABRT with struct SignalOptions 60 | 61 | # Compiling 62 | Just include where you want to use Loguru. 63 | Then, in one .cpp file #include 64 | Make sure you compile with -std=c++11 -lstdc++ -lpthread -ldl 65 | 66 | # Usage 67 | For details, please see the official documentation at emilk.github.io/loguru 68 | 69 | #include 70 | 71 | int main(int argc, char* argv[]) { 72 | loguru::init(argc, argv); 73 | 74 | // Put every log message in "everything.log": 75 | loguru::add_file("everything.log", loguru::Append, loguru::Verbosity_MAX); 76 | 77 | LOG_F(INFO, "The magic number is %d", 42); 78 | } 79 | 80 | */ 81 | 82 | #if defined(LOGURU_IMPLEMENTATION) 83 | #error "You are defining LOGURU_IMPLEMENTATION. This is for older versions of Loguru. You should now instead include loguru.cpp (or build it and link with it)" 84 | #endif 85 | 86 | // Disable all warnings from gcc/clang: 87 | #if defined(__clang__) 88 | #pragma clang system_header 89 | #elif defined(__GNUC__) 90 | #pragma GCC system_header 91 | #endif 92 | 93 | #ifndef LOGURU_HAS_DECLARED_FORMAT_HEADER 94 | #define LOGURU_HAS_DECLARED_FORMAT_HEADER 95 | 96 | // Semantic versioning. Loguru version can be printed with printf("%d.%d.%d", LOGURU_VERSION_MAJOR, LOGURU_VERSION_MINOR, LOGURU_VERSION_PATCH); 97 | #define LOGURU_VERSION_MAJOR 2 98 | #define LOGURU_VERSION_MINOR 1 99 | #define LOGURU_VERSION_PATCH 0 100 | 101 | #if defined(_MSC_VER) 102 | #include // Needed for _In_z_ etc annotations 103 | #endif 104 | 105 | #if defined(__linux__) || defined(__APPLE__) 106 | #define LOGURU_SYSLOG 1 107 | #else 108 | #define LOGURU_SYSLOG 0 109 | #endif 110 | 111 | // ---------------------------------------------------------------------------- 112 | 113 | #ifndef LOGURU_EXPORT 114 | // Define to your project's export declaration if needed for use in a shared library. 115 | #define LOGURU_EXPORT 116 | #endif 117 | 118 | #ifndef LOGURU_SCOPE_TEXT_SIZE 119 | // Maximum length of text that can be printed by a LOG_SCOPE. 120 | // This should be long enough to get most things, but short enough not to clutter the stack. 121 | #define LOGURU_SCOPE_TEXT_SIZE 196 122 | #endif 123 | 124 | #ifndef LOGURU_FILENAME_WIDTH 125 | // Width of the column containing the file name 126 | #define LOGURU_FILENAME_WIDTH 23 127 | #endif 128 | 129 | #ifndef LOGURU_THREADNAME_WIDTH 130 | // Width of the column containing the thread name 131 | #define LOGURU_THREADNAME_WIDTH 16 132 | #endif 133 | 134 | #ifndef LOGURU_SCOPE_TIME_PRECISION 135 | // Resolution of scope timers. 3=ms, 6=us, 9=ns 136 | #define LOGURU_SCOPE_TIME_PRECISION 3 137 | #endif 138 | 139 | #ifdef LOGURU_CATCH_SIGABRT 140 | #error "You are defining LOGURU_CATCH_SIGABRT. This is for older versions of Loguru. You should now instead set the options passed to loguru::init" 141 | #endif 142 | 143 | #ifndef LOGURU_VERBOSE_SCOPE_ENDINGS 144 | // Show milliseconds and scope name at end of scope. 145 | #define LOGURU_VERBOSE_SCOPE_ENDINGS 1 146 | #endif 147 | 148 | #ifndef LOGURU_REDEFINE_ASSERT 149 | #define LOGURU_REDEFINE_ASSERT 0 150 | #endif 151 | 152 | #ifndef LOGURU_WITH_STREAMS 153 | #define LOGURU_WITH_STREAMS 0 154 | #endif 155 | 156 | #ifndef LOGURU_REPLACE_GLOG 157 | #define LOGURU_REPLACE_GLOG 0 158 | #endif 159 | 160 | #if LOGURU_REPLACE_GLOG 161 | #undef LOGURU_WITH_STREAMS 162 | #define LOGURU_WITH_STREAMS 1 163 | #endif 164 | 165 | #if defined(LOGURU_UNSAFE_SIGNAL_HANDLER) 166 | #error "You are defining LOGURU_UNSAFE_SIGNAL_HANDLER. This is for older versions of Loguru. You should now instead set the unsafe_signal_handler option when you call loguru::init." 167 | #endif 168 | 169 | #if LOGURU_IMPLEMENTATION 170 | #undef LOGURU_WITH_STREAMS 171 | #define LOGURU_WITH_STREAMS 1 172 | #endif 173 | 174 | #ifndef LOGURU_USE_FMTLIB 175 | #define LOGURU_USE_FMTLIB 0 176 | #endif 177 | 178 | #ifndef LOGURU_USE_LOCALE 179 | #define LOGURU_USE_LOCALE 0 180 | #endif 181 | 182 | #ifndef LOGURU_WITH_FILEABS 183 | #define LOGURU_WITH_FILEABS 0 184 | #endif 185 | 186 | #ifndef LOGURU_RTTI 187 | #if defined(__clang__) 188 | #if __has_feature(cxx_rtti) 189 | #define LOGURU_RTTI 1 190 | #endif 191 | #elif defined(__GNUG__) 192 | #if defined(__GXX_RTTI) 193 | #define LOGURU_RTTI 1 194 | #endif 195 | #elif defined(_MSC_VER) 196 | #if defined(_CPPRTTI) 197 | #define LOGURU_RTTI 1 198 | #endif 199 | #endif 200 | #endif 201 | 202 | #ifdef LOGURU_USE_ANONYMOUS_NAMESPACE 203 | #define LOGURU_ANONYMOUS_NAMESPACE_BEGIN namespace { 204 | #define LOGURU_ANONYMOUS_NAMESPACE_END } 205 | #else 206 | #define LOGURU_ANONYMOUS_NAMESPACE_BEGIN 207 | #define LOGURU_ANONYMOUS_NAMESPACE_END 208 | #endif 209 | 210 | // -------------------------------------------------------------------- 211 | // Utility macros 212 | 213 | #define LOGURU_CONCATENATE_IMPL(s1, s2) s1 ## s2 214 | #define LOGURU_CONCATENATE(s1, s2) LOGURU_CONCATENATE_IMPL(s1, s2) 215 | 216 | #ifdef __COUNTER__ 217 | # define LOGURU_ANONYMOUS_VARIABLE(str) LOGURU_CONCATENATE(str, __COUNTER__) 218 | #else 219 | # define LOGURU_ANONYMOUS_VARIABLE(str) LOGURU_CONCATENATE(str, __LINE__) 220 | #endif 221 | 222 | #if defined(__clang__) || defined(__GNUC__) 223 | // Helper macro for declaring functions as having similar signature to printf. 224 | // This allows the compiler to catch format errors at compile-time. 225 | #define LOGURU_PRINTF_LIKE(fmtarg, firstvararg) __attribute__((__format__ (__printf__, fmtarg, firstvararg))) 226 | #define LOGURU_FORMAT_STRING_TYPE const char* 227 | #elif defined(_MSC_VER) 228 | #define LOGURU_PRINTF_LIKE(fmtarg, firstvararg) 229 | #define LOGURU_FORMAT_STRING_TYPE _In_z_ _Printf_format_string_ const char* 230 | #else 231 | #define LOGURU_PRINTF_LIKE(fmtarg, firstvararg) 232 | #define LOGURU_FORMAT_STRING_TYPE const char* 233 | #endif 234 | 235 | // Used to mark log_and_abort for the benefit of the static analyzer and optimizer. 236 | #if defined(_MSC_VER) 237 | #define LOGURU_NORETURN __declspec(noreturn) 238 | #else 239 | #define LOGURU_NORETURN __attribute__((noreturn)) 240 | #endif 241 | 242 | #if defined(_MSC_VER) 243 | #define LOGURU_PREDICT_FALSE(x) (x) 244 | #define LOGURU_PREDICT_TRUE(x) (x) 245 | #else 246 | #define LOGURU_PREDICT_FALSE(x) (__builtin_expect(x, 0)) 247 | #define LOGURU_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) 248 | #endif 249 | 250 | #if LOGURU_USE_FMTLIB 251 | #include 252 | #define LOGURU_FMT(x) "{:" #x "}" 253 | #else 254 | #define LOGURU_FMT(x) "%" #x 255 | #endif 256 | 257 | #ifdef _WIN32 258 | #define STRDUP(str) _strdup(str) 259 | #else 260 | #define STRDUP(str) strdup(str) 261 | #endif 262 | 263 | #include 264 | 265 | // -------------------------------------------------------------------- 266 | LOGURU_ANONYMOUS_NAMESPACE_BEGIN 267 | 268 | namespace loguru 269 | { 270 | // Simple RAII ownership of a char*. 271 | class LOGURU_EXPORT Text 272 | { 273 | public: 274 | explicit Text(char* owned_str) : _str(owned_str) {} 275 | ~Text(); 276 | Text(Text&& t) 277 | { 278 | _str = t._str; 279 | t._str = nullptr; 280 | } 281 | Text(Text& t) = delete; 282 | Text& operator=(Text& t) = delete; 283 | void operator=(Text&& t) = delete; 284 | 285 | const char* c_str() const { return _str; } 286 | bool empty() const { return _str == nullptr || *_str == '\0'; } 287 | 288 | char* release() 289 | { 290 | auto result = _str; 291 | _str = nullptr; 292 | return result; 293 | } 294 | 295 | private: 296 | char* _str; 297 | }; 298 | 299 | // Like printf, but returns the formated text. 300 | #if LOGURU_USE_FMTLIB 301 | LOGURU_EXPORT 302 | Text vtextprintf(const char* format, fmt::format_args args); 303 | 304 | template 305 | LOGURU_EXPORT 306 | Text textprintf(LOGURU_FORMAT_STRING_TYPE format, const Args&... args) { 307 | return vtextprintf(format, fmt::make_format_args(args...)); 308 | } 309 | #else 310 | LOGURU_EXPORT 311 | Text textprintf(LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(1, 2); 312 | #endif 313 | 314 | // Overloaded for variadic template matching. 315 | LOGURU_EXPORT 316 | Text textprintf(); 317 | 318 | using Verbosity = int; 319 | 320 | #undef FATAL 321 | #undef ERROR 322 | #undef WARNING 323 | #undef INFO 324 | #undef MAX 325 | 326 | enum NamedVerbosity : Verbosity 327 | { 328 | // Used to mark an invalid verbosity. Do not log to this level. 329 | Verbosity_INVALID = -10, // Never do LOG_F(INVALID) 330 | 331 | // You may use Verbosity_OFF on g_stderr_verbosity, but for nothing else! 332 | Verbosity_OFF = -9, // Never do LOG_F(OFF) 333 | 334 | // Prefer to use ABORT_F or ABORT_S over LOG_F(FATAL) or LOG_S(FATAL). 335 | Verbosity_FATAL = -3, 336 | Verbosity_ERROR = -2, 337 | Verbosity_WARNING = -1, 338 | 339 | // Normal messages. By default written to stderr. 340 | Verbosity_INFO = 0, 341 | 342 | // Same as Verbosity_INFO in every way. 343 | Verbosity_0 = 0, 344 | 345 | // Verbosity levels 1-9 are generally not written to stderr, but are written to file. 346 | Verbosity_1 = +1, 347 | Verbosity_2 = +2, 348 | Verbosity_3 = +3, 349 | Verbosity_4 = +4, 350 | Verbosity_5 = +5, 351 | Verbosity_6 = +6, 352 | Verbosity_7 = +7, 353 | Verbosity_8 = +8, 354 | Verbosity_9 = +9, 355 | 356 | // Do not use higher verbosity levels, as that will make grepping log files harder. 357 | Verbosity_MAX = +9, 358 | }; 359 | 360 | struct Message 361 | { 362 | // You would generally print a Message by just concatenating the buffers without spacing. 363 | // Optionally, ignore preamble and indentation. 364 | Verbosity verbosity; // Already part of preamble 365 | const char* filename; // Already part of preamble 366 | unsigned line; // Already part of preamble 367 | const char* preamble; // Date, time, uptime, thread, file:line, verbosity. 368 | const char* indentation; // Just a bunch of spacing. 369 | const char* prefix; // Assertion failure info goes here (or ""). 370 | const char* message; // User message goes here. 371 | }; 372 | 373 | /* Everything with a verbosity equal or greater than g_stderr_verbosity will be 374 | written to stderr. You can set this in code or via the -v argument. 375 | Set to loguru::Verbosity_OFF to write nothing to stderr. 376 | Default is 0, i.e. only log ERROR, WARNING and INFO are written to stderr. 377 | */ 378 | LOGURU_EXPORT extern Verbosity g_stderr_verbosity; 379 | LOGURU_EXPORT extern bool g_colorlogtostderr; // True by default. 380 | LOGURU_EXPORT extern unsigned g_flush_interval_ms; // 0 (unbuffered) by default. 381 | LOGURU_EXPORT extern bool g_preamble_header; // Prepend each log start by a descriptions line with all columns name? True by default. 382 | LOGURU_EXPORT extern bool g_preamble; // Prefix each log line with date, time etc? True by default. 383 | 384 | /* Specify the verbosity used by loguru to log its info messages including the header 385 | logged when logged::init() is called or on exit. Default is 0 (INFO). 386 | */ 387 | LOGURU_EXPORT extern Verbosity g_internal_verbosity; 388 | 389 | // Turn off individual parts of the preamble 390 | LOGURU_EXPORT extern bool g_preamble_date; // The date field 391 | LOGURU_EXPORT extern bool g_preamble_time; // The time of the current day 392 | LOGURU_EXPORT extern bool g_preamble_uptime; // The time since init call 393 | LOGURU_EXPORT extern bool g_preamble_thread; // The logging thread 394 | LOGURU_EXPORT extern bool g_preamble_file; // The file from which the log originates from 395 | LOGURU_EXPORT extern bool g_preamble_verbose; // The verbosity field 396 | LOGURU_EXPORT extern bool g_preamble_pipe; // The pipe symbol right before the message 397 | 398 | // May not throw! 399 | typedef void (*log_handler_t)(void* user_data, const Message& message); 400 | typedef void (*close_handler_t)(void* user_data); 401 | typedef void (*flush_handler_t)(void* user_data); 402 | 403 | // May throw if that's how you'd like to handle your errors. 404 | typedef void (*fatal_handler_t)(const Message& message); 405 | 406 | // Given a verbosity level, return the level's name or nullptr. 407 | typedef const char* (*verbosity_to_name_t)(Verbosity verbosity); 408 | 409 | // Given a verbosity level name, return the verbosity level or 410 | // Verbosity_INVALID if name is not recognized. 411 | typedef Verbosity (*name_to_verbosity_t)(const char* name); 412 | 413 | struct SignalOptions 414 | { 415 | /// Make Loguru try to do unsafe but useful things, 416 | /// like printing a stack trace, when catching signals. 417 | /// This may lead to bad things like deadlocks in certain situations. 418 | bool unsafe_signal_handler = true; 419 | 420 | /// Should Loguru catch SIGABRT ? 421 | bool sigabrt = true; 422 | 423 | /// Should Loguru catch SIGBUS ? 424 | bool sigbus = true; 425 | 426 | /// Should Loguru catch SIGFPE ? 427 | bool sigfpe = true; 428 | 429 | /// Should Loguru catch SIGILL ? 430 | bool sigill = true; 431 | 432 | /// Should Loguru catch SIGINT ? 433 | bool sigint = true; 434 | 435 | /// Should Loguru catch SIGSEGV ? 436 | bool sigsegv = true; 437 | 438 | /// Should Loguru catch SIGTERM ? 439 | bool sigterm = true; 440 | 441 | static SignalOptions none() 442 | { 443 | SignalOptions options; 444 | options.unsafe_signal_handler = false; 445 | options.sigabrt = false; 446 | options.sigbus = false; 447 | options.sigfpe = false; 448 | options.sigill = false; 449 | options.sigint = false; 450 | options.sigsegv = false; 451 | options.sigterm = false; 452 | return options; 453 | } 454 | }; 455 | 456 | // Runtime options passed to loguru::init 457 | struct Options 458 | { 459 | // This allows you to use something else instead of "-v" via verbosity_flag. 460 | // Set to nullptr if you don't want Loguru to parse verbosity from the args. 461 | const char* verbosity_flag = "-v"; 462 | 463 | // loguru::init will set the name of the calling thread to this. 464 | // If you don't want Loguru to set the name of the main thread, 465 | // set this to nullptr. 466 | // NOTE: on SOME platforms loguru::init will only overwrite the thread name 467 | // if a thread name has not already been set. 468 | // To always set a thread name, use loguru::set_thread_name instead. 469 | const char* main_thread_name = "main thread"; 470 | 471 | SignalOptions signal_options; 472 | }; 473 | 474 | /* Should be called from the main thread. 475 | You don't *need* to call this, but if you do you get: 476 | * Signal handlers installed 477 | * Program arguments logged 478 | * Working dir logged 479 | * Optional -v verbosity flag parsed 480 | * Main thread name set to "main thread" 481 | * Explanation of the preamble (date, thread name, etc) logged 482 | 483 | loguru::init() will look for arguments meant for loguru and remove them. 484 | Arguments meant for loguru are: 485 | -v n Set loguru::g_stderr_verbosity level. Examples: 486 | -v 3 Show verbosity level 3 and lower. 487 | -v 0 Only show INFO, WARNING, ERROR, FATAL (default). 488 | -v INFO Only show INFO, WARNING, ERROR, FATAL (default). 489 | -v WARNING Only show WARNING, ERROR, FATAL. 490 | -v ERROR Only show ERROR, FATAL. 491 | -v FATAL Only show FATAL. 492 | -v OFF Turn off logging to stderr. 493 | 494 | Tip: You can set g_stderr_verbosity before calling loguru::init. 495 | That way you can set the default but have the user override it with the -v flag. 496 | Note that -v does not affect file logging (see loguru::add_file). 497 | 498 | You can you something other than the -v flag by setting the verbosity_flag option. 499 | */ 500 | LOGURU_EXPORT 501 | void init(int& argc, char* argv[], const Options& options = {}); 502 | 503 | // Will call remove_all_callbacks(). After calling this, logging will still go to stderr. 504 | // You generally don't need to call this. 505 | LOGURU_EXPORT 506 | void shutdown(); 507 | 508 | // What ~ will be replaced with, e.g. "/home/your_user_name/" 509 | LOGURU_EXPORT 510 | const char* home_dir(); 511 | 512 | /* Returns the name of the app as given in argv[0] but without leading path. 513 | That is, if argv[0] is "../foo/app" this will return "app". 514 | */ 515 | LOGURU_EXPORT 516 | const char* argv0_filename(); 517 | 518 | // Returns all arguments given to loguru::init(), but escaped with a single space as separator. 519 | LOGURU_EXPORT 520 | const char* arguments(); 521 | 522 | // Returns the path to the current working dir when loguru::init() was called. 523 | LOGURU_EXPORT 524 | const char* current_dir(); 525 | 526 | // Returns the part of the path after the last / or \ (if any). 527 | LOGURU_EXPORT 528 | const char* filename(const char* path); 529 | 530 | // e.g. "foo/bar/baz.ext" will create the directories "foo/" and "foo/bar/" 531 | LOGURU_EXPORT 532 | bool create_directories(const char* file_path_const); 533 | 534 | // Writes date and time with millisecond precision, e.g. "20151017_161503.123" 535 | LOGURU_EXPORT 536 | void write_date_time(char* buff, unsigned long long buff_size); 537 | 538 | // Helper: thread-safe version strerror 539 | LOGURU_EXPORT 540 | Text errno_as_text(); 541 | 542 | /* Given a prefix of e.g. "~/loguru/" this might return 543 | "/home/your_username/loguru/app_name/20151017_161503.123.log" 544 | 545 | where "app_name" is a sanitized version of argv[0]. 546 | */ 547 | LOGURU_EXPORT 548 | void suggest_log_path(const char* prefix, char* buff, unsigned long long buff_size); 549 | 550 | enum FileMode { Truncate, Append }; 551 | 552 | /* Will log to a file at the given path. 553 | Any logging message with a verbosity lower or equal to 554 | the given verbosity will be included. 555 | The function will create all directories in 'path' if needed. 556 | If path starts with a ~, it will be replaced with loguru::home_dir() 557 | To stop the file logging, just call loguru::remove_callback(path) with the same path. 558 | */ 559 | LOGURU_EXPORT 560 | bool add_file(const char* path, FileMode mode, Verbosity verbosity); 561 | 562 | LOGURU_EXPORT 563 | // Send logs to syslog with LOG_USER facility (see next call) 564 | bool add_syslog(const char* app_name, Verbosity verbosity); 565 | LOGURU_EXPORT 566 | // Send logs to syslog with your own choice of facility (LOG_USER, LOG_AUTH, ...) 567 | // see loguru.cpp: syslog_log() for more details. 568 | bool add_syslog(const char* app_name, Verbosity verbosity, int facility); 569 | 570 | /* Will be called right before abort(). 571 | You can for instance use this to print custom error messages, or throw an exception. 572 | Feel free to call LOG:ing function from this, but not FATAL ones! */ 573 | LOGURU_EXPORT 574 | void set_fatal_handler(fatal_handler_t handler); 575 | 576 | // Get the current fatal handler, if any. Default value is nullptr. 577 | LOGURU_EXPORT 578 | fatal_handler_t get_fatal_handler(); 579 | 580 | /* Will be called on each log messages with a verbosity less or equal to the given one. 581 | Useful for displaying messages on-screen in a game, for example. 582 | The given on_close is also expected to flush (if desired). 583 | */ 584 | LOGURU_EXPORT 585 | void add_callback( 586 | const char* id, 587 | log_handler_t callback, 588 | void* user_data, 589 | Verbosity verbosity, 590 | close_handler_t on_close = nullptr, 591 | flush_handler_t on_flush = nullptr); 592 | 593 | /* Set a callback that returns custom verbosity level names. If callback 594 | is nullptr or returns nullptr, default log names will be used. 595 | */ 596 | LOGURU_EXPORT 597 | void set_verbosity_to_name_callback(verbosity_to_name_t callback); 598 | 599 | /* Set a callback that returns the verbosity level matching a name. The 600 | callback should return Verbosity_INVALID if the name is not 601 | recognized. 602 | */ 603 | LOGURU_EXPORT 604 | void set_name_to_verbosity_callback(name_to_verbosity_t callback); 605 | 606 | /* Get a custom name for a specific verbosity, if one exists, or nullptr. */ 607 | LOGURU_EXPORT 608 | const char* get_verbosity_name(Verbosity verbosity); 609 | 610 | /* Get the verbosity enum value from a custom 4-character level name, if one exists. 611 | If the name does not match a custom level name, Verbosity_INVALID is returned. 612 | */ 613 | LOGURU_EXPORT 614 | Verbosity get_verbosity_from_name(const char* name); 615 | 616 | // Returns true iff the callback was found (and removed). 617 | LOGURU_EXPORT 618 | bool remove_callback(const char* id); 619 | 620 | // Shut down all file logging and any other callback hooks installed. 621 | LOGURU_EXPORT 622 | void remove_all_callbacks(); 623 | 624 | // Returns the maximum of g_stderr_verbosity and all file/custom outputs. 625 | LOGURU_EXPORT 626 | Verbosity current_verbosity_cutoff(); 627 | 628 | #if LOGURU_USE_FMTLIB 629 | // Internal functions 630 | LOGURU_EXPORT 631 | void vlog(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, fmt::format_args args); 632 | LOGURU_EXPORT 633 | void raw_vlog(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, fmt::format_args args); 634 | 635 | // Actual logging function. Use the LOG macro instead of calling this directly. 636 | template 637 | LOGURU_EXPORT 638 | void log(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, const Args &... args) { 639 | vlog(verbosity, file, line, format, fmt::make_format_args(args...)); 640 | } 641 | 642 | // Log without any preamble or indentation. 643 | template 644 | LOGURU_EXPORT 645 | void raw_log(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, const Args &... args) { 646 | raw_vlog(verbosity, file, line, format, fmt::make_format_args(args...)); 647 | } 648 | #else // LOGURU_USE_FMTLIB? 649 | // Actual logging function. Use the LOG macro instead of calling this directly. 650 | LOGURU_EXPORT 651 | void log(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(4, 5); 652 | 653 | // Actual logging function. 654 | LOGURU_EXPORT 655 | void vlog(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, va_list) LOGURU_PRINTF_LIKE(4, 0); 656 | 657 | // Log without any preamble or indentation. 658 | LOGURU_EXPORT 659 | void raw_log(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(4, 5); 660 | #endif // !LOGURU_USE_FMTLIB 661 | 662 | // Helper class for LOG_SCOPE_F 663 | class LOGURU_EXPORT LogScopeRAII 664 | { 665 | public: 666 | LogScopeRAII() : _file(nullptr) {} // No logging 667 | LogScopeRAII(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, va_list vlist) LOGURU_PRINTF_LIKE(5, 0); 668 | LogScopeRAII(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(5, 6); 669 | ~LogScopeRAII(); 670 | 671 | void Init(LOGURU_FORMAT_STRING_TYPE format, va_list vlist) LOGURU_PRINTF_LIKE(2, 0); 672 | 673 | #if defined(_MSC_VER) && _MSC_VER > 1800 674 | // older MSVC default move ctors close the scope on move. See 675 | // issue #43 676 | LogScopeRAII(LogScopeRAII&& other) 677 | : _verbosity(other._verbosity) 678 | , _file(other._file) 679 | , _line(other._line) 680 | , _indent_stderr(other._indent_stderr) 681 | , _start_time_ns(other._start_time_ns) 682 | { 683 | // Make sure the tmp object's destruction doesn't close the scope: 684 | other._file = nullptr; 685 | 686 | for (unsigned int i = 0; i < LOGURU_SCOPE_TEXT_SIZE; ++i) { 687 | _name[i] = other._name[i]; 688 | } 689 | } 690 | #else 691 | LogScopeRAII(LogScopeRAII&&) = default; 692 | #endif 693 | 694 | private: 695 | LogScopeRAII(const LogScopeRAII&) = delete; 696 | LogScopeRAII& operator=(const LogScopeRAII&) = delete; 697 | void operator=(LogScopeRAII&&) = delete; 698 | 699 | Verbosity _verbosity; 700 | const char* _file; // Set to null if we are disabled due to verbosity 701 | unsigned _line; 702 | bool _indent_stderr; // Did we? 703 | long long _start_time_ns; 704 | char _name[LOGURU_SCOPE_TEXT_SIZE]; 705 | }; 706 | 707 | // Marked as 'noreturn' for the benefit of the static analyzer and optimizer. 708 | // stack_trace_skip is the number of extrace stack frames to skip above log_and_abort. 709 | #if LOGURU_USE_FMTLIB 710 | LOGURU_EXPORT 711 | LOGURU_NORETURN void vlog_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, fmt::format_args); 712 | template 713 | LOGURU_EXPORT 714 | LOGURU_NORETURN void log_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, const Args&... args) { 715 | vlog_and_abort(stack_trace_skip, expr, file, line, format, fmt::make_format_args(args...)); 716 | } 717 | #else 718 | LOGURU_EXPORT 719 | LOGURU_NORETURN void log_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(5, 6); 720 | #endif 721 | LOGURU_EXPORT 722 | LOGURU_NORETURN void log_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line); 723 | 724 | // Flush output to stderr and files. 725 | // If g_flush_interval_ms is set to non-zero, this will be called automatically this often. 726 | // If not set, you do not need to call this at all. 727 | LOGURU_EXPORT 728 | void flush(); 729 | 730 | template inline Text format_value(const T&) { return textprintf("N/A"); } 731 | template<> inline Text format_value(const char& v) { return textprintf(LOGURU_FMT(c), v); } 732 | template<> inline Text format_value(const int& v) { return textprintf(LOGURU_FMT(d), v); } 733 | template<> inline Text format_value(const float& v) { return textprintf(LOGURU_FMT(f), v); } 734 | template<> inline Text format_value(const double& v) { return textprintf(LOGURU_FMT(f), v); } 735 | 736 | #if LOGURU_USE_FMTLIB 737 | template<> inline Text format_value(const unsigned int& v) { return textprintf(LOGURU_FMT(d), v); } 738 | template<> inline Text format_value(const long& v) { return textprintf(LOGURU_FMT(d), v); } 739 | template<> inline Text format_value(const unsigned long& v) { return textprintf(LOGURU_FMT(d), v); } 740 | template<> inline Text format_value(const long long& v) { return textprintf(LOGURU_FMT(d), v); } 741 | template<> inline Text format_value(const unsigned long long& v) { return textprintf(LOGURU_FMT(d), v); } 742 | #else 743 | template<> inline Text format_value(const unsigned int& v) { return textprintf(LOGURU_FMT(u), v); } 744 | template<> inline Text format_value(const long& v) { return textprintf(LOGURU_FMT(lu), v); } 745 | template<> inline Text format_value(const unsigned long& v) { return textprintf(LOGURU_FMT(ld), v); } 746 | template<> inline Text format_value(const long long& v) { return textprintf(LOGURU_FMT(llu), v); } 747 | template<> inline Text format_value(const unsigned long long& v) { return textprintf(LOGURU_FMT(lld), v); } 748 | #endif 749 | 750 | /* Thread names can be set for the benefit of readable logs. 751 | If you do not set the thread name, a hex id will be shown instead. 752 | These thread names may or may not be the same as the system thread names, 753 | depending on the system. 754 | Try to limit the thread name to 15 characters or less. */ 755 | LOGURU_EXPORT 756 | void set_thread_name(const char* name); 757 | 758 | /* Returns the thread name for this thread. 759 | On most *nix systems this will return the system thread name (settable from both within and without Loguru). 760 | On other systems it will return whatever you set in `set_thread_name()`; 761 | If no thread name is set, this will return a hexadecimal thread id. 762 | `length` should be the number of bytes available in the buffer. 763 | 17 is a good number for length. 764 | `right_align_hex_id` means any hexadecimal thread id will be written to the end of buffer. 765 | */ 766 | LOGURU_EXPORT 767 | void get_thread_name(char* buffer, unsigned long long length, bool right_align_hex_id); 768 | 769 | /* Generates a readable stacktrace as a string. 770 | 'skip' specifies how many stack frames to skip. 771 | For instance, the default skip (1) means: 772 | don't include the call to loguru::stacktrace in the stack trace. */ 773 | LOGURU_EXPORT 774 | Text stacktrace(int skip = 1); 775 | 776 | /* Add a string to be replaced with something else in the stack output. 777 | 778 | For instance, instead of having a stack trace look like this: 779 | 0x41f541 some_function(std::basic_ofstream >&) 780 | You can clean it up with: 781 | auto verbose_type_name = loguru::demangle(typeid(std::ofstream).name()); 782 | loguru::add_stack_cleanup(verbose_type_name.c_str(); "std::ofstream"); 783 | So the next time you will instead see: 784 | 0x41f541 some_function(std::ofstream&) 785 | 786 | `replace_with_this` must be shorter than `find_this`. 787 | */ 788 | LOGURU_EXPORT 789 | void add_stack_cleanup(const char* find_this, const char* replace_with_this); 790 | 791 | // Example: demangle(typeid(std::ofstream).name()) -> "std::basic_ofstream >" 792 | LOGURU_EXPORT 793 | Text demangle(const char* name); 794 | 795 | // ------------------------------------------------------------------------ 796 | /* 797 | Not all terminals support colors, but if they do, and g_colorlogtostderr 798 | is set, Loguru will write them to stderr to make errors in red, etc. 799 | 800 | You also have the option to manually use them, via the function below. 801 | 802 | Note, however, that if you do, the color codes could end up in your logfile! 803 | 804 | This means if you intend to use them functions you should either: 805 | * Use them on the stderr/stdout directly (bypass Loguru). 806 | * Don't add file outputs to Loguru. 807 | * Expect some \e[1m things in your logfile. 808 | 809 | Usage: 810 | printf("%sRed%sGreen%sBold green%sClear again\n", 811 | loguru::terminal_red(), loguru::terminal_green(), 812 | loguru::terminal_bold(), loguru::terminal_reset()); 813 | 814 | If the terminal at hand does not support colors the above output 815 | will just not have funky \e[1m things showing. 816 | */ 817 | 818 | // Do the output terminal support colors? 819 | LOGURU_EXPORT 820 | bool terminal_has_color(); 821 | 822 | // Colors 823 | LOGURU_EXPORT const char* terminal_black(); 824 | LOGURU_EXPORT const char* terminal_red(); 825 | LOGURU_EXPORT const char* terminal_green(); 826 | LOGURU_EXPORT const char* terminal_yellow(); 827 | LOGURU_EXPORT const char* terminal_blue(); 828 | LOGURU_EXPORT const char* terminal_purple(); 829 | LOGURU_EXPORT const char* terminal_cyan(); 830 | LOGURU_EXPORT const char* terminal_light_gray(); 831 | LOGURU_EXPORT const char* terminal_light_red(); 832 | LOGURU_EXPORT const char* terminal_white(); 833 | 834 | // Formating 835 | LOGURU_EXPORT const char* terminal_bold(); 836 | LOGURU_EXPORT const char* terminal_underline(); 837 | 838 | // You should end each line with this! 839 | LOGURU_EXPORT const char* terminal_reset(); 840 | 841 | // -------------------------------------------------------------------- 842 | // Error context related: 843 | 844 | struct StringStream; 845 | 846 | // Use this in your EcEntryBase::print_value overload. 847 | LOGURU_EXPORT 848 | void stream_print(StringStream& out_string_stream, const char* text); 849 | 850 | class LOGURU_EXPORT EcEntryBase 851 | { 852 | public: 853 | EcEntryBase(const char* file, unsigned line, const char* descr); 854 | ~EcEntryBase(); 855 | EcEntryBase(const EcEntryBase&) = delete; 856 | EcEntryBase(EcEntryBase&&) = delete; 857 | EcEntryBase& operator=(const EcEntryBase&) = delete; 858 | EcEntryBase& operator=(EcEntryBase&&) = delete; 859 | 860 | virtual void print_value(StringStream& out_string_stream) const = 0; 861 | 862 | EcEntryBase* previous() const { return _previous; } 863 | 864 | // private: 865 | const char* _file; 866 | unsigned _line; 867 | const char* _descr; 868 | EcEntryBase* _previous; 869 | }; 870 | 871 | template 872 | class EcEntryData : public EcEntryBase 873 | { 874 | public: 875 | using Printer = Text(*)(T data); 876 | 877 | EcEntryData(const char* file, unsigned line, const char* descr, T data, Printer&& printer) 878 | : EcEntryBase(file, line, descr), _data(data), _printer(printer) {} 879 | 880 | virtual void print_value(StringStream& out_string_stream) const override 881 | { 882 | const auto str = _printer(_data); 883 | stream_print(out_string_stream, str.c_str()); 884 | } 885 | 886 | private: 887 | T _data; 888 | Printer _printer; 889 | }; 890 | 891 | // template 892 | // class EcEntryLambda : public EcEntryBase 893 | // { 894 | // public: 895 | // EcEntryLambda(const char* file, unsigned line, const char* descr, Printer&& printer) 896 | // : EcEntryBase(file, line, descr), _printer(std::move(printer)) {} 897 | 898 | // virtual void print_value(StringStream& out_string_stream) const override 899 | // { 900 | // const auto str = _printer(); 901 | // stream_print(out_string_stream, str.c_str()); 902 | // } 903 | 904 | // private: 905 | // Printer _printer; 906 | // }; 907 | 908 | // template 909 | // EcEntryLambda make_ec_entry_lambda(const char* file, unsigned line, const char* descr, Printer&& printer) 910 | // { 911 | // return {file, line, descr, std::move(printer)}; 912 | // } 913 | 914 | template 915 | struct decay_char_array { using type = T; }; 916 | 917 | template 918 | struct decay_char_array { using type = const char*; }; 919 | 920 | template 921 | struct make_const_ptr { using type = T; }; 922 | 923 | template 924 | struct make_const_ptr { using type = const T*; }; 925 | 926 | template 927 | struct make_ec_type { using type = typename make_const_ptr::type>::type; }; 928 | 929 | /* A stack trace gives you the names of the function at the point of a crash. 930 | With ERROR_CONTEXT, you can also get the values of select local variables. 931 | Usage: 932 | 933 | void process_customers(const std::string& filename) 934 | { 935 | ERROR_CONTEXT("Processing file", filename.c_str()); 936 | for (int customer_index : ...) 937 | { 938 | ERROR_CONTEXT("Customer index", customer_index); 939 | ... 940 | } 941 | } 942 | 943 | The context is in effect during the scope of the ERROR_CONTEXT. 944 | Use loguru::get_error_context() to get the contents of the active error contexts. 945 | 946 | Example result: 947 | 948 | ------------------------------------------------ 949 | [ErrorContext] main.cpp:416 Processing file: "customers.json" 950 | [ErrorContext] main.cpp:417 Customer index: 42 951 | ------------------------------------------------ 952 | 953 | Error contexts are printed automatically on crashes, and only on crashes. 954 | This makes them much faster than logging the value of a variable. 955 | */ 956 | #define ERROR_CONTEXT(descr, data) \ 957 | const loguru::EcEntryData::type> \ 958 | LOGURU_ANONYMOUS_VARIABLE(error_context_scope_)( \ 959 | __FILE__, __LINE__, descr, data, \ 960 | static_cast::type>::Printer>(loguru::ec_to_text) ) // For better error messages 961 | 962 | /* 963 | #define ERROR_CONTEXT(descr, data) \ 964 | const auto LOGURU_ANONYMOUS_VARIABLE(error_context_scope_)( \ 965 | loguru::make_ec_entry_lambda(__FILE__, __LINE__, descr, \ 966 | [=](){ return loguru::ec_to_text(data); })) 967 | */ 968 | 969 | using EcHandle = const EcEntryBase*; 970 | 971 | /* 972 | Get a light-weight handle to the error context stack on this thread. 973 | The handle is valid as long as the current thread has no changes to its error context stack. 974 | You can pass the handle to loguru::get_error_context on another thread. 975 | This can be very useful for when you have a parent thread spawning several working threads, 976 | and you want the error context of the parent thread to get printed (too) when there is an 977 | error on the child thread. You can accomplish this thusly: 978 | 979 | void foo(const char* parameter) 980 | { 981 | ERROR_CONTEXT("parameter", parameter) 982 | const auto parent_ec_handle = loguru::get_thread_ec_handle(); 983 | 984 | std::thread([=]{ 985 | loguru::set_thread_name("child thread"); 986 | ERROR_CONTEXT("parent context", parent_ec_handle); 987 | dangerous_code(); 988 | }.join(); 989 | } 990 | 991 | */ 992 | LOGURU_EXPORT 993 | EcHandle get_thread_ec_handle(); 994 | 995 | // Get a string describing the current stack of error context. Empty string if there is none. 996 | LOGURU_EXPORT 997 | Text get_error_context(); 998 | 999 | // Get a string describing the error context of the given thread handle. 1000 | LOGURU_EXPORT 1001 | Text get_error_context_for(EcHandle ec_handle); 1002 | 1003 | // ------------------------------------------------------------------------ 1004 | 1005 | LOGURU_EXPORT Text ec_to_text(const char* data); 1006 | LOGURU_EXPORT Text ec_to_text(char data); 1007 | LOGURU_EXPORT Text ec_to_text(int data); 1008 | LOGURU_EXPORT Text ec_to_text(unsigned int data); 1009 | LOGURU_EXPORT Text ec_to_text(long data); 1010 | LOGURU_EXPORT Text ec_to_text(unsigned long data); 1011 | LOGURU_EXPORT Text ec_to_text(long long data); 1012 | LOGURU_EXPORT Text ec_to_text(unsigned long long data); 1013 | LOGURU_EXPORT Text ec_to_text(float data); 1014 | LOGURU_EXPORT Text ec_to_text(double data); 1015 | LOGURU_EXPORT Text ec_to_text(long double data); 1016 | LOGURU_EXPORT Text ec_to_text(EcHandle); 1017 | 1018 | /* 1019 | You can add ERROR_CONTEXT support for your own types by overloading ec_to_text. Here's how: 1020 | 1021 | some.hpp: 1022 | namespace loguru { 1023 | Text ec_to_text(MySmallType data) 1024 | Text ec_to_text(const MyBigType* data) 1025 | } // namespace loguru 1026 | 1027 | some.cpp: 1028 | namespace loguru { 1029 | Text ec_to_text(MySmallType small_value) 1030 | { 1031 | // Called only when needed, i.e. on a crash. 1032 | std::string str = small_value.as_string(); // Format 'small_value' here somehow. 1033 | return Text{STRDUP(str.c_str())}; 1034 | } 1035 | 1036 | Text ec_to_text(const MyBigType* big_value) 1037 | { 1038 | // Called only when needed, i.e. on a crash. 1039 | std::string str = big_value->as_string(); // Format 'big_value' here somehow. 1040 | return Text{STRDUP(str.c_str())}; 1041 | } 1042 | } // namespace loguru 1043 | 1044 | Any file that include some.hpp: 1045 | void foo(MySmallType small, const MyBigType& big) 1046 | { 1047 | ERROR_CONTEXT("Small", small); // Copy ´small` by value. 1048 | ERROR_CONTEXT("Big", &big); // `big` should not change during this scope! 1049 | .... 1050 | } 1051 | */ 1052 | } // namespace loguru 1053 | 1054 | LOGURU_ANONYMOUS_NAMESPACE_END 1055 | 1056 | // -------------------------------------------------------------------- 1057 | // Logging macros 1058 | 1059 | // LOG_F(2, "Only logged if verbosity is 2 or higher: %d", some_number); 1060 | #define VLOG_F(verbosity, ...) \ 1061 | ((verbosity) > loguru::current_verbosity_cutoff()) ? (void)0 \ 1062 | : loguru::log(verbosity, __FILE__, __LINE__, __VA_ARGS__) 1063 | 1064 | // LOG_F(INFO, "Foo: %d", some_number); 1065 | #define LOG_F(verbosity_name, ...) VLOG_F(loguru::Verbosity_ ## verbosity_name, __VA_ARGS__) 1066 | 1067 | #define VLOG_IF_F(verbosity, cond, ...) \ 1068 | ((verbosity) > loguru::current_verbosity_cutoff() || (cond) == false) \ 1069 | ? (void)0 \ 1070 | : loguru::log(verbosity, __FILE__, __LINE__, __VA_ARGS__) 1071 | 1072 | #define LOG_IF_F(verbosity_name, cond, ...) \ 1073 | VLOG_IF_F(loguru::Verbosity_ ## verbosity_name, cond, __VA_ARGS__) 1074 | 1075 | #define VLOG_SCOPE_F(verbosity, ...) \ 1076 | loguru::LogScopeRAII LOGURU_ANONYMOUS_VARIABLE(error_context_RAII_) = \ 1077 | ((verbosity) > loguru::current_verbosity_cutoff()) ? loguru::LogScopeRAII() : \ 1078 | loguru::LogScopeRAII(verbosity, __FILE__, __LINE__, __VA_ARGS__) 1079 | 1080 | // Raw logging - no preamble, no indentation. Slightly faster than full logging. 1081 | #define RAW_VLOG_F(verbosity, ...) \ 1082 | ((verbosity) > loguru::current_verbosity_cutoff()) ? (void)0 \ 1083 | : loguru::raw_log(verbosity, __FILE__, __LINE__, __VA_ARGS__) 1084 | 1085 | #define RAW_LOG_F(verbosity_name, ...) RAW_VLOG_F(loguru::Verbosity_ ## verbosity_name, __VA_ARGS__) 1086 | 1087 | // Use to book-end a scope. Affects logging on all threads. 1088 | #define LOG_SCOPE_F(verbosity_name, ...) \ 1089 | VLOG_SCOPE_F(loguru::Verbosity_ ## verbosity_name, __VA_ARGS__) 1090 | 1091 | #define LOG_SCOPE_FUNCTION(verbosity_name) LOG_SCOPE_F(verbosity_name, __func__) 1092 | 1093 | // ----------------------------------------------- 1094 | // ABORT_F macro. Usage: ABORT_F("Cause of error: %s", error_str); 1095 | 1096 | // Message is optional 1097 | #define ABORT_F(...) loguru::log_and_abort(0, "ABORT: ", __FILE__, __LINE__, __VA_ARGS__) 1098 | 1099 | // -------------------------------------------------------------------- 1100 | // CHECK_F macros: 1101 | 1102 | #define CHECK_WITH_INFO_F(test, info, ...) \ 1103 | LOGURU_PREDICT_TRUE((test) == true) ? (void)0 : loguru::log_and_abort(0, "CHECK FAILED: " info " ", __FILE__, \ 1104 | __LINE__, ##__VA_ARGS__) 1105 | 1106 | /* Checked at runtime too. Will print error, then call fatal_handler (if any), then 'abort'. 1107 | Note that the test must be boolean. 1108 | CHECK_F(ptr); will not compile, but CHECK_F(ptr != nullptr); will. */ 1109 | #define CHECK_F(test, ...) CHECK_WITH_INFO_F(test, #test, ##__VA_ARGS__) 1110 | 1111 | #define CHECK_NOTNULL_F(x, ...) CHECK_WITH_INFO_F((x) != nullptr, #x " != nullptr", ##__VA_ARGS__) 1112 | 1113 | #define CHECK_OP_F(expr_left, expr_right, op, ...) \ 1114 | do \ 1115 | { \ 1116 | auto val_left = expr_left; \ 1117 | auto val_right = expr_right; \ 1118 | if (! LOGURU_PREDICT_TRUE(val_left op val_right)) \ 1119 | { \ 1120 | auto str_left = loguru::format_value(val_left); \ 1121 | auto str_right = loguru::format_value(val_right); \ 1122 | auto fail_info = loguru::textprintf("CHECK FAILED: " LOGURU_FMT(s) " " LOGURU_FMT(s) " " LOGURU_FMT(s) " (" LOGURU_FMT(s) " " LOGURU_FMT(s) " " LOGURU_FMT(s) ") ", \ 1123 | #expr_left, #op, #expr_right, str_left.c_str(), #op, str_right.c_str()); \ 1124 | auto user_msg = loguru::textprintf(__VA_ARGS__); \ 1125 | loguru::log_and_abort(0, fail_info.c_str(), __FILE__, __LINE__, \ 1126 | LOGURU_FMT(s), user_msg.c_str()); \ 1127 | } \ 1128 | } while (false) 1129 | 1130 | #ifndef LOGURU_DEBUG_LOGGING 1131 | #ifndef NDEBUG 1132 | #define LOGURU_DEBUG_LOGGING 1 1133 | #else 1134 | #define LOGURU_DEBUG_LOGGING 0 1135 | #endif 1136 | #endif 1137 | 1138 | #if LOGURU_DEBUG_LOGGING 1139 | // Debug logging enabled: 1140 | #define DLOG_F(verbosity_name, ...) LOG_F(verbosity_name, __VA_ARGS__) 1141 | #define DVLOG_F(verbosity, ...) VLOG_F(verbosity, __VA_ARGS__) 1142 | #define DLOG_IF_F(verbosity_name, ...) LOG_IF_F(verbosity_name, __VA_ARGS__) 1143 | #define DVLOG_IF_F(verbosity, ...) VLOG_IF_F(verbosity, __VA_ARGS__) 1144 | #define DRAW_LOG_F(verbosity_name, ...) RAW_LOG_F(verbosity_name, __VA_ARGS__) 1145 | #define DRAW_VLOG_F(verbosity, ...) RAW_VLOG_F(verbosity, __VA_ARGS__) 1146 | #else 1147 | // Debug logging disabled: 1148 | #define DLOG_F(verbosity_name, ...) 1149 | #define DVLOG_F(verbosity, ...) 1150 | #define DLOG_IF_F(verbosity_name, ...) 1151 | #define DVLOG_IF_F(verbosity, ...) 1152 | #define DRAW_LOG_F(verbosity_name, ...) 1153 | #define DRAW_VLOG_F(verbosity, ...) 1154 | #endif 1155 | 1156 | #define CHECK_EQ_F(a, b, ...) CHECK_OP_F(a, b, ==, ##__VA_ARGS__) 1157 | #define CHECK_NE_F(a, b, ...) CHECK_OP_F(a, b, !=, ##__VA_ARGS__) 1158 | #define CHECK_LT_F(a, b, ...) CHECK_OP_F(a, b, < , ##__VA_ARGS__) 1159 | #define CHECK_GT_F(a, b, ...) CHECK_OP_F(a, b, > , ##__VA_ARGS__) 1160 | #define CHECK_LE_F(a, b, ...) CHECK_OP_F(a, b, <=, ##__VA_ARGS__) 1161 | #define CHECK_GE_F(a, b, ...) CHECK_OP_F(a, b, >=, ##__VA_ARGS__) 1162 | 1163 | #ifndef LOGURU_DEBUG_CHECKS 1164 | #ifndef NDEBUG 1165 | #define LOGURU_DEBUG_CHECKS 1 1166 | #else 1167 | #define LOGURU_DEBUG_CHECKS 0 1168 | #endif 1169 | #endif 1170 | 1171 | #if LOGURU_DEBUG_CHECKS 1172 | // Debug checks enabled: 1173 | #define DCHECK_F(test, ...) CHECK_F(test, ##__VA_ARGS__) 1174 | #define DCHECK_NOTNULL_F(x, ...) CHECK_NOTNULL_F(x, ##__VA_ARGS__) 1175 | #define DCHECK_EQ_F(a, b, ...) CHECK_EQ_F(a, b, ##__VA_ARGS__) 1176 | #define DCHECK_NE_F(a, b, ...) CHECK_NE_F(a, b, ##__VA_ARGS__) 1177 | #define DCHECK_LT_F(a, b, ...) CHECK_LT_F(a, b, ##__VA_ARGS__) 1178 | #define DCHECK_LE_F(a, b, ...) CHECK_LE_F(a, b, ##__VA_ARGS__) 1179 | #define DCHECK_GT_F(a, b, ...) CHECK_GT_F(a, b, ##__VA_ARGS__) 1180 | #define DCHECK_GE_F(a, b, ...) CHECK_GE_F(a, b, ##__VA_ARGS__) 1181 | #else 1182 | // Debug checks disabled: 1183 | #define DCHECK_F(test, ...) 1184 | #define DCHECK_NOTNULL_F(x, ...) 1185 | #define DCHECK_EQ_F(a, b, ...) 1186 | #define DCHECK_NE_F(a, b, ...) 1187 | #define DCHECK_LT_F(a, b, ...) 1188 | #define DCHECK_LE_F(a, b, ...) 1189 | #define DCHECK_GT_F(a, b, ...) 1190 | #define DCHECK_GE_F(a, b, ...) 1191 | #endif // NDEBUG 1192 | 1193 | 1194 | #if LOGURU_REDEFINE_ASSERT 1195 | #undef assert 1196 | #ifndef NDEBUG 1197 | // Debug: 1198 | #define assert(test) CHECK_WITH_INFO_F(!!(test), #test) // HACK 1199 | #else 1200 | #define assert(test) 1201 | #endif 1202 | #endif // LOGURU_REDEFINE_ASSERT 1203 | 1204 | #endif // LOGURU_HAS_DECLARED_FORMAT_HEADER 1205 | 1206 | // ---------------------------------------------------------------------------- 1207 | // .dP"Y8 888888 88""Yb 888888 db 8b d8 .dP"Y8 1208 | // `Ybo." 88 88__dP 88__ dPYb 88b d88 `Ybo." 1209 | // o.`Y8b 88 88"Yb 88"" dP__Yb 88YbdP88 o.`Y8b 1210 | // 8bodP' 88 88 Yb 888888 dP""""Yb 88 YY 88 8bodP' 1211 | 1212 | #if LOGURU_WITH_STREAMS 1213 | #ifndef LOGURU_HAS_DECLARED_STREAMS_HEADER 1214 | #define LOGURU_HAS_DECLARED_STREAMS_HEADER 1215 | 1216 | /* This file extends loguru to enable std::stream-style logging, a la Glog. 1217 | It's an optional feature behind the LOGURU_WITH_STREAMS settings 1218 | because including it everywhere will slow down compilation times. 1219 | */ 1220 | 1221 | #include 1222 | #include // Adds about 38 kLoC on clang. 1223 | #include 1224 | 1225 | LOGURU_ANONYMOUS_NAMESPACE_BEGIN 1226 | 1227 | namespace loguru 1228 | { 1229 | // Like sprintf, but returns the formated text. 1230 | LOGURU_EXPORT 1231 | std::string strprintf(LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(1, 2); 1232 | 1233 | // Like vsprintf, but returns the formated text. 1234 | LOGURU_EXPORT 1235 | std::string vstrprintf(LOGURU_FORMAT_STRING_TYPE format, va_list) LOGURU_PRINTF_LIKE(1, 0); 1236 | 1237 | class LOGURU_EXPORT StreamLogger 1238 | { 1239 | public: 1240 | StreamLogger(Verbosity verbosity, const char* file, unsigned line) : _verbosity(verbosity), _file(file), _line(line) {} 1241 | ~StreamLogger() noexcept(false); 1242 | 1243 | template 1244 | StreamLogger& operator<<(const T& t) 1245 | { 1246 | _ss << t; 1247 | return *this; 1248 | } 1249 | 1250 | // std::endl and other iomanip:s. 1251 | StreamLogger& operator<<(std::ostream&(*f)(std::ostream&)) 1252 | { 1253 | f(_ss); 1254 | return *this; 1255 | } 1256 | 1257 | private: 1258 | Verbosity _verbosity; 1259 | const char* _file; 1260 | unsigned _line; 1261 | std::ostringstream _ss; 1262 | }; 1263 | 1264 | class LOGURU_EXPORT AbortLogger 1265 | { 1266 | public: 1267 | AbortLogger(const char* expr, const char* file, unsigned line) : _expr(expr), _file(file), _line(line) { } 1268 | LOGURU_NORETURN ~AbortLogger() noexcept(false); 1269 | 1270 | template 1271 | AbortLogger& operator<<(const T& t) 1272 | { 1273 | _ss << t; 1274 | return *this; 1275 | } 1276 | 1277 | // std::endl and other iomanip:s. 1278 | AbortLogger& operator<<(std::ostream&(*f)(std::ostream&)) 1279 | { 1280 | f(_ss); 1281 | return *this; 1282 | } 1283 | 1284 | private: 1285 | const char* _expr; 1286 | const char* _file; 1287 | unsigned _line; 1288 | std::ostringstream _ss; 1289 | }; 1290 | 1291 | class LOGURU_EXPORT Voidify 1292 | { 1293 | public: 1294 | Voidify() {} 1295 | // This has to be an operator with a precedence lower than << but higher than ?: 1296 | void operator&(const StreamLogger&) { } 1297 | void operator&(const AbortLogger&) { } 1298 | }; 1299 | 1300 | /* Helper functions for CHECK_OP_S macro. 1301 | GLOG trick: The (int, int) specialization works around the issue that the compiler 1302 | will not instantiate the template version of the function on values of unnamed enum type. */ 1303 | #define DEFINE_CHECK_OP_IMPL(name, op) \ 1304 | template \ 1305 | inline std::string* name(const char* expr, const T1& v1, const char* op_str, const T2& v2) \ 1306 | { \ 1307 | if (LOGURU_PREDICT_TRUE(v1 op v2)) { return NULL; } \ 1308 | std::ostringstream ss; \ 1309 | ss << "CHECK FAILED: " << expr << " (" << v1 << " " << op_str << " " << v2 << ") "; \ 1310 | return new std::string(ss.str()); \ 1311 | } \ 1312 | inline std::string* name(const char* expr, int v1, const char* op_str, int v2) \ 1313 | { \ 1314 | return name(expr, v1, op_str, v2); \ 1315 | } 1316 | 1317 | DEFINE_CHECK_OP_IMPL(check_EQ_impl, ==) 1318 | DEFINE_CHECK_OP_IMPL(check_NE_impl, !=) 1319 | DEFINE_CHECK_OP_IMPL(check_LE_impl, <=) 1320 | DEFINE_CHECK_OP_IMPL(check_LT_impl, < ) 1321 | DEFINE_CHECK_OP_IMPL(check_GE_impl, >=) 1322 | DEFINE_CHECK_OP_IMPL(check_GT_impl, > ) 1323 | #undef DEFINE_CHECK_OP_IMPL 1324 | 1325 | /* GLOG trick: Function is overloaded for integral types to allow static const integrals 1326 | declared in classes and not defined to be used as arguments to CHECK* macros. */ 1327 | template 1328 | inline const T& referenceable_value(const T& t) { return t; } 1329 | inline char referenceable_value(char t) { return t; } 1330 | inline unsigned char referenceable_value(unsigned char t) { return t; } 1331 | inline signed char referenceable_value(signed char t) { return t; } 1332 | inline short referenceable_value(short t) { return t; } 1333 | inline unsigned short referenceable_value(unsigned short t) { return t; } 1334 | inline int referenceable_value(int t) { return t; } 1335 | inline unsigned int referenceable_value(unsigned int t) { return t; } 1336 | inline long referenceable_value(long t) { return t; } 1337 | inline unsigned long referenceable_value(unsigned long t) { return t; } 1338 | inline long long referenceable_value(long long t) { return t; } 1339 | inline unsigned long long referenceable_value(unsigned long long t) { return t; } 1340 | } // namespace loguru 1341 | 1342 | LOGURU_ANONYMOUS_NAMESPACE_END 1343 | 1344 | // ----------------------------------------------- 1345 | // Logging macros: 1346 | 1347 | // usage: LOG_STREAM(INFO) << "Foo " << std::setprecision(10) << some_value; 1348 | #define VLOG_IF_S(verbosity, cond) \ 1349 | ((verbosity) > loguru::current_verbosity_cutoff() || (cond) == false) \ 1350 | ? (void)0 \ 1351 | : loguru::Voidify() & loguru::StreamLogger(verbosity, __FILE__, __LINE__) 1352 | #define LOG_IF_S(verbosity_name, cond) VLOG_IF_S(loguru::Verbosity_ ## verbosity_name, cond) 1353 | #define VLOG_S(verbosity) VLOG_IF_S(verbosity, true) 1354 | #define LOG_S(verbosity_name) VLOG_S(loguru::Verbosity_ ## verbosity_name) 1355 | 1356 | // ----------------------------------------------- 1357 | // ABORT_S macro. Usage: ABORT_S() << "Causo of error: " << details; 1358 | 1359 | #define ABORT_S() loguru::Voidify() & loguru::AbortLogger("ABORT: ", __FILE__, __LINE__) 1360 | 1361 | // ----------------------------------------------- 1362 | // CHECK_S macros: 1363 | 1364 | #define CHECK_WITH_INFO_S(cond, info) \ 1365 | LOGURU_PREDICT_TRUE((cond) == true) \ 1366 | ? (void)0 \ 1367 | : loguru::Voidify() & loguru::AbortLogger("CHECK FAILED: " info " ", __FILE__, __LINE__) 1368 | 1369 | #define CHECK_S(cond) CHECK_WITH_INFO_S(cond, #cond) 1370 | #define CHECK_NOTNULL_S(x) CHECK_WITH_INFO_S((x) != nullptr, #x " != nullptr") 1371 | 1372 | #define CHECK_OP_S(function_name, expr1, op, expr2) \ 1373 | while (auto error_string = loguru::function_name(#expr1 " " #op " " #expr2, \ 1374 | loguru::referenceable_value(expr1), #op, \ 1375 | loguru::referenceable_value(expr2))) \ 1376 | loguru::AbortLogger(error_string->c_str(), __FILE__, __LINE__) 1377 | 1378 | #define CHECK_EQ_S(expr1, expr2) CHECK_OP_S(check_EQ_impl, expr1, ==, expr2) 1379 | #define CHECK_NE_S(expr1, expr2) CHECK_OP_S(check_NE_impl, expr1, !=, expr2) 1380 | #define CHECK_LE_S(expr1, expr2) CHECK_OP_S(check_LE_impl, expr1, <=, expr2) 1381 | #define CHECK_LT_S(expr1, expr2) CHECK_OP_S(check_LT_impl, expr1, < , expr2) 1382 | #define CHECK_GE_S(expr1, expr2) CHECK_OP_S(check_GE_impl, expr1, >=, expr2) 1383 | #define CHECK_GT_S(expr1, expr2) CHECK_OP_S(check_GT_impl, expr1, > , expr2) 1384 | 1385 | #if LOGURU_DEBUG_LOGGING 1386 | // Debug logging enabled: 1387 | #define DVLOG_IF_S(verbosity, cond) VLOG_IF_S(verbosity, cond) 1388 | #define DLOG_IF_S(verbosity_name, cond) LOG_IF_S(verbosity_name, cond) 1389 | #define DVLOG_S(verbosity) VLOG_S(verbosity) 1390 | #define DLOG_S(verbosity_name) LOG_S(verbosity_name) 1391 | #else 1392 | // Debug logging disabled: 1393 | #define DVLOG_IF_S(verbosity, cond) \ 1394 | (true || (verbosity) > loguru::current_verbosity_cutoff() || (cond) == false) \ 1395 | ? (void)0 \ 1396 | : loguru::Voidify() & loguru::StreamLogger(verbosity, __FILE__, __LINE__) 1397 | 1398 | #define DLOG_IF_S(verbosity_name, cond) DVLOG_IF_S(loguru::Verbosity_ ## verbosity_name, cond) 1399 | #define DVLOG_S(verbosity) DVLOG_IF_S(verbosity, true) 1400 | #define DLOG_S(verbosity_name) DVLOG_S(loguru::Verbosity_ ## verbosity_name) 1401 | #endif 1402 | 1403 | #if LOGURU_DEBUG_CHECKS 1404 | // Debug checks enabled: 1405 | #define DCHECK_S(cond) CHECK_S(cond) 1406 | #define DCHECK_NOTNULL_S(x) CHECK_NOTNULL_S(x) 1407 | #define DCHECK_EQ_S(a, b) CHECK_EQ_S(a, b) 1408 | #define DCHECK_NE_S(a, b) CHECK_NE_S(a, b) 1409 | #define DCHECK_LT_S(a, b) CHECK_LT_S(a, b) 1410 | #define DCHECK_LE_S(a, b) CHECK_LE_S(a, b) 1411 | #define DCHECK_GT_S(a, b) CHECK_GT_S(a, b) 1412 | #define DCHECK_GE_S(a, b) CHECK_GE_S(a, b) 1413 | #else 1414 | // Debug checks disabled: 1415 | #define DCHECK_S(cond) CHECK_S(true || (cond)) 1416 | #define DCHECK_NOTNULL_S(x) CHECK_S(true || (x) != nullptr) 1417 | #define DCHECK_EQ_S(a, b) CHECK_S(true || (a) == (b)) 1418 | #define DCHECK_NE_S(a, b) CHECK_S(true || (a) != (b)) 1419 | #define DCHECK_LT_S(a, b) CHECK_S(true || (a) < (b)) 1420 | #define DCHECK_LE_S(a, b) CHECK_S(true || (a) <= (b)) 1421 | #define DCHECK_GT_S(a, b) CHECK_S(true || (a) > (b)) 1422 | #define DCHECK_GE_S(a, b) CHECK_S(true || (a) >= (b)) 1423 | #endif 1424 | 1425 | #if LOGURU_REPLACE_GLOG 1426 | #undef LOG 1427 | #undef VLOG 1428 | #undef LOG_IF 1429 | #undef VLOG_IF 1430 | #undef CHECK 1431 | #undef CHECK_NOTNULL 1432 | #undef CHECK_EQ 1433 | #undef CHECK_NE 1434 | #undef CHECK_LT 1435 | #undef CHECK_LE 1436 | #undef CHECK_GT 1437 | #undef CHECK_GE 1438 | #undef DLOG 1439 | #undef DVLOG 1440 | #undef DLOG_IF 1441 | #undef DVLOG_IF 1442 | #undef DCHECK 1443 | #undef DCHECK_NOTNULL 1444 | #undef DCHECK_EQ 1445 | #undef DCHECK_NE 1446 | #undef DCHECK_LT 1447 | #undef DCHECK_LE 1448 | #undef DCHECK_GT 1449 | #undef DCHECK_GE 1450 | #undef VLOG_IS_ON 1451 | 1452 | #define LOG LOG_S 1453 | #define VLOG VLOG_S 1454 | #define LOG_IF LOG_IF_S 1455 | #define VLOG_IF VLOG_IF_S 1456 | #define CHECK(cond) CHECK_S(!!(cond)) 1457 | #define CHECK_NOTNULL CHECK_NOTNULL_S 1458 | #define CHECK_EQ CHECK_EQ_S 1459 | #define CHECK_NE CHECK_NE_S 1460 | #define CHECK_LT CHECK_LT_S 1461 | #define CHECK_LE CHECK_LE_S 1462 | #define CHECK_GT CHECK_GT_S 1463 | #define CHECK_GE CHECK_GE_S 1464 | #define DLOG DLOG_S 1465 | #define DVLOG DVLOG_S 1466 | #define DLOG_IF DLOG_IF_S 1467 | #define DVLOG_IF DVLOG_IF_S 1468 | #define DCHECK DCHECK_S 1469 | #define DCHECK_NOTNULL DCHECK_NOTNULL_S 1470 | #define DCHECK_EQ DCHECK_EQ_S 1471 | #define DCHECK_NE DCHECK_NE_S 1472 | #define DCHECK_LT DCHECK_LT_S 1473 | #define DCHECK_LE DCHECK_LE_S 1474 | #define DCHECK_GT DCHECK_GT_S 1475 | #define DCHECK_GE DCHECK_GE_S 1476 | #define VLOG_IS_ON(verbosity) ((verbosity) <= loguru::current_verbosity_cutoff()) 1477 | 1478 | #endif // LOGURU_REPLACE_GLOG 1479 | 1480 | #endif // LOGURU_WITH_STREAMS 1481 | 1482 | #endif // LOGURU_HAS_DECLARED_STREAMS_HEADER 1483 | -------------------------------------------------------------------------------- /include/server.h: -------------------------------------------------------------------------------- 1 | #ifndef _SERVER_H 2 | #define _SERVER_H 3 | 4 | #include "common.h" 5 | #include "dealtor.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace GLCC { 15 | class GLCCServer 16 | { 17 | public: 18 | 19 | int server_state = 0; 20 | int run(); 21 | GLCCServer(const std::string config_path); 22 | static WFFacilities::WaitGroup server_wait_group; 23 | static WFFacilities::WaitGroup mysql_wait_group; 24 | static void sig_handler(int signo); 25 | static bool parse_request(WFHttpTask * task, 26 | std::string method, 27 | std::string rex); 28 | static void get_connection_infos(WFHttpTask * task, std::iostream & context); 29 | static void get_connection_infos(WFHttpTask * task, Json::Value & context); 30 | static void get_server_infos(WFHttpServer * server, std::iostream & context); 31 | static void get_server_infos(WFHttpServer * server, Json::Value & context); 32 | static int parse_mysql_response(WFMySQLTask * task, std::unordered_map> & results); 33 | static int parse_mysql_response(WFMySQLTask * task); 34 | 35 | static void run_detector(const std::string & room_name, std::shared_ptr context); 36 | static Detector * register_detector(const std::string & room_name, std::shared_ptr context, int mode=Create_Register); 37 | static int cancel_detector(const std::string & room_name, int mode=WAKE_CANCEL); 38 | static int parse_url(const std::string & url, std::unordered_map & results_map); 39 | static int parse_url(const char * url, std::unordered_map & results_map); 40 | static int parse_room_url(const std::string & url, std::unordered_map & results_map); 41 | static int parse_room_url(const char * url, std::unordered_map & results_map); 42 | protected: 43 | glcc_server_context_t glcc_server_context; 44 | ssl_context_t glcc_server_ssl_context; 45 | 46 | static void login_activity(WFHttpTask * task, void * context); 47 | // base 48 | static void main_callback(WFHttpTask * task, void * context); 49 | static void login_callback(WFHttpTask * task, void * context); 50 | static void user_register_callback(WFHttpTask * task, void * context); 51 | static void hello_world_callback(WFHttpTask * task); 52 | // mysql 53 | static void create_db_callbck(WFMySQLTask * task, void * context); 54 | // detector 55 | static void dect_video_callback(WFHttpTask * task, void * context); 56 | static void dect_video_file_callback(WFHttpTask * task, void * context); 57 | static void kick_dect_video_file_callback(WFHttpTask * task, void * context); 58 | static void disdect_video_callback(WFHttpTask * task, void * context); 59 | static void video_put_lattice_callback(WFHttpTask * task, void * context); 60 | static void video_disput_lattice_callback(WFHttpTask * task, void * context); 61 | static void fetch_video_file_callback(WFHttpTask * task, void * context); 62 | static void delete_video_file_callback(WFHttpTask * task, void * context); 63 | static void transmiss_video_file_callback(WFHttpTask * task, void * context); 64 | static void register_map(const std::string & mode, const std::string & room_name, void * context); 65 | // timer 66 | static void detector_timer_callback(WFTimerTask * timer); 67 | static void file_timer_callback(WFTimerTask * timer); 68 | // video 69 | static void register_video_callback(WFHttpTask * task, void * context); 70 | static void delete_video_callback(WFHttpTask * task, void * context); 71 | // common 72 | static protocol::HttpResponse * set_common_resp(protocol::HttpResponse * resp, 73 | std::string code="200", std::string phrase="OK", 74 | std::string verion="HTTP/1.1", std::string content_type="text/html"); 75 | static protocol::HttpRequest * set_common_req(protocol::HttpRequest * req, 76 | std::string accept="*/*", std::string status="close", std::string method=HttpMethodGet); 77 | }; 78 | 79 | } 80 | 81 | 82 | #endif -------------------------------------------------------------------------------- /models/yolox_tiny_cat_dynamic_fp16_trt/deploy.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.5.0", 3 | "task": "Detector", 4 | "models": [ 5 | { 6 | "name": "yolox", 7 | "net": "end2end.engine", 8 | "weights": "", 9 | "backend": "tensorrt", 10 | "precision": "FP16", 11 | "batch_size": 1, 12 | "dynamic_shape": true 13 | } 14 | ], 15 | "customs": [] 16 | } -------------------------------------------------------------------------------- /models/yolox_tiny_cat_dynamic_fp16_trt/detail.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.5.0", 3 | "codebase": { 4 | "task": "ObjectDetection", 5 | "codebase": "mmdet", 6 | "version": "2.20.0", 7 | "pth": "/home/r/Scripts/Python/mmdetection/work_dirs/yolox_tiny_8x8_300e_cat_finetune_v2/best_bbox_mAP_epoch_297.pth", 8 | "config": "/home/r/Scripts/Python/mmdetection/work_dirs/yolox_tiny_8x8_300e_cat_finetune_v2/yolox_tiny_8x8_300e_cat_finetune_trainval.py" 9 | }, 10 | "codebase_config": { 11 | "type": "mmdet", 12 | "task": "ObjectDetection", 13 | "model_type": "end2end", 14 | "post_processing": { 15 | "score_threshold": 0.05, 16 | "confidence_threshold": 0.005, 17 | "iou_threshold": 0.5, 18 | "max_output_boxes_per_class": 200, 19 | "pre_top_k": 5000, 20 | "keep_top_k": 100, 21 | "background_label_id": -1 22 | } 23 | }, 24 | "onnx_config": { 25 | "type": "onnx", 26 | "export_params": true, 27 | "keep_initializers_as_inputs": false, 28 | "opset_version": 11, 29 | "save_file": "end2end.onnx", 30 | "input_names": [ 31 | "input" 32 | ], 33 | "output_names": [ 34 | "dets", 35 | "labels" 36 | ], 37 | "input_shape": null, 38 | "dynamic_axes": { 39 | "input": { 40 | "0": "batch", 41 | "2": "height", 42 | "3": "width" 43 | }, 44 | "dets": { 45 | "0": "batch", 46 | "1": "num_dets" 47 | }, 48 | "labels": { 49 | "0": "batch", 50 | "1": "num_dets" 51 | } 52 | } 53 | }, 54 | "backend_config": { 55 | "type": "tensorrt", 56 | "common_config": { 57 | "fp16_mode": true, 58 | "max_workspace_size": 1073741824 59 | }, 60 | "model_inputs": [ 61 | { 62 | "input_shapes": { 63 | "input": { 64 | "min_shape": [ 65 | 1, 66 | 3, 67 | 320, 68 | 320 69 | ], 70 | "opt_shape": [ 71 | 1, 72 | 3, 73 | 800, 74 | 1344 75 | ], 76 | "max_shape": [ 77 | 1, 78 | 3, 79 | 1344, 80 | 1344 81 | ] 82 | } 83 | } 84 | } 85 | ] 86 | }, 87 | "calib_config": {} 88 | } -------------------------------------------------------------------------------- /models/yolox_tiny_cat_dynamic_fp16_trt/end2end.engine: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDGRCF/GLCC_Server/8aa41355b01830b1a6003ea222a4dfd8794c6396/models/yolox_tiny_cat_dynamic_fp16_trt/end2end.engine -------------------------------------------------------------------------------- /models/yolox_tiny_cat_dynamic_fp16_trt/end2end.onnx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDGRCF/GLCC_Server/8aa41355b01830b1a6003ea222a4dfd8794c6396/models/yolox_tiny_cat_dynamic_fp16_trt/end2end.onnx -------------------------------------------------------------------------------- /models/yolox_tiny_cat_dynamic_fp16_trt/output_pytorch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDGRCF/GLCC_Server/8aa41355b01830b1a6003ea222a4dfd8794c6396/models/yolox_tiny_cat_dynamic_fp16_trt/output_pytorch.jpg -------------------------------------------------------------------------------- /models/yolox_tiny_cat_dynamic_fp16_trt/output_tensorrt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DDGRCF/GLCC_Server/8aa41355b01830b1a6003ea222a4dfd8794c6396/models/yolox_tiny_cat_dynamic_fp16_trt/output_tensorrt.jpg -------------------------------------------------------------------------------- /models/yolox_tiny_cat_dynamic_fp16_trt/pipeline.json: -------------------------------------------------------------------------------- 1 | { 2 | "pipeline": { 3 | "input": [ 4 | "img" 5 | ], 6 | "output": [ 7 | "post_output" 8 | ], 9 | "tasks": [ 10 | { 11 | "type": "Task", 12 | "module": "Transform", 13 | "name": "Preprocess", 14 | "input": [ 15 | "img" 16 | ], 17 | "output": [ 18 | "prep_output" 19 | ], 20 | "transforms": [ 21 | { 22 | "type": "LoadImageFromFile" 23 | }, 24 | { 25 | "type": "Resize", 26 | "keep_ratio": true, 27 | "size": [ 28 | 416, 29 | 416 30 | ] 31 | }, 32 | { 33 | "type": "Pad", 34 | "pad_to_square": true, 35 | "pad_val": { 36 | "img": [ 37 | 114.0, 38 | 114.0, 39 | 114.0 40 | ] 41 | } 42 | }, 43 | { 44 | "type": "DefaultFormatBundle" 45 | }, 46 | { 47 | "type": "Collect", 48 | "keys": [ 49 | "img" 50 | ], 51 | "meta_keys": [ 52 | "flip", 53 | "pad_shape", 54 | "filename", 55 | "ori_filename", 56 | "img_shape", 57 | "valid_ratio", 58 | "img_norm_cfg", 59 | "ori_shape", 60 | "scale_factor", 61 | "flip_direction" 62 | ] 63 | } 64 | ] 65 | }, 66 | { 67 | "name": "yolox", 68 | "type": "Task", 69 | "module": "Net", 70 | "input": [ 71 | "prep_output" 72 | ], 73 | "output": [ 74 | "infer_output" 75 | ], 76 | "input_map": { 77 | "img": "input" 78 | } 79 | }, 80 | { 81 | "type": "Task", 82 | "module": "mmdet", 83 | "name": "postprocess", 84 | "component": "ResizeBBox", 85 | "params": { 86 | "score_thr": 0.01, 87 | "nms": { 88 | "type": "nms", 89 | "iou_threshold": 0.65 90 | } 91 | }, 92 | "output": [ 93 | "post_output" 94 | ], 95 | "input": [ 96 | "prep_output", 97 | "infer_output" 98 | ] 99 | } 100 | ] 101 | } 102 | } -------------------------------------------------------------------------------- /src/common.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | 4 | namespace GLCC { 5 | namespace constants { 6 | const std::string localhost="127.0.0.1"; 7 | // time TODO: more reasonable 8 | const long num_millisecond_per_second = 1000; 9 | const long num_microsecond_per_second = num_millisecond_per_second * 1000; 10 | const long num_second_per_minute = num_microsecond_per_second * 60; 11 | // timer 12 | long long interval_to_watch_detector_second = 60 * num_second_per_minute; 13 | long long interval_to_watch_file_second = 5 * num_second_per_minute; 14 | long max_detector_live_day = 365; 15 | long max_video_file_save_day = 2; 16 | 17 | // format 18 | std::string file_time_format = "%Y-%m-%d_%H:%M:%S"; 19 | // livego 20 | std::string video_path_template = "rtsp://127.0.0.1:5544/live/%s"; 21 | std::string livego_push_url_template = "rtmp://127.0.0.1:1935/live/%s"; 22 | std::string livego_check_stat_template = "http://127.0.0.1:8083/api/stat/group?stream_name=%s"; 23 | std::string livego_kick_url = "http://127.0.0.1:8083/api/ctrl/kick_session"; 24 | 25 | std::string ffmpeg_file_push_command = "ffmpeg -stream_loop -1 -i %s -vcodec libx264 -f flv %s"; 26 | std::string ffmpeg_push_command = "ffmpeg -y -an -f rawvideo -vcodec rawvideo -pix_fmt bgr24 -s %dx%d -r %d -i - -c:v libx264 -pix_fmt yuv420p -preset ultrafast -f flv %s"; 27 | std::string cover_save_suffix = "jpg"; 28 | 29 | std::string mysql_root_url = "mysql://root:9696@127.0.0.1:3306"; 30 | std::string mysql_glccserver_url = mysql_root_url + "/glccserver"; 31 | 32 | const std::vector video_suffixes = {"mp4", "flv", "wmv", "mpeg", "avi"}; 33 | const std::vector video_prefixes = {"http", "https", "rtmp", "rtsp"}; 34 | 35 | const std::string mysql_create_db_command = R"( 36 | SET global log_bin_trust_function_creators = 1; 37 | CREATE DATABASE IF NOT EXISTS glccserver; 38 | CREATE TABLE IF NOT EXISTS glccserver.User(username VARCHAR(20) NOT NULL UNIQUE, password VARCHAR(20) NOT NULL, nickname VARCHAR(20) NOT NULL, 39 | PRIMARY KEY (username)); 40 | CREATE TABLE IF NOT EXISTS glccserver.Video(video_name VARCHAR(20) NOT NULL, username VARCHAR(20) NOT NULL, video_url VARCHAR(256) NOT NULL, 41 | PRIMARY KEY (video_name, username), 42 | FOREIGN KEY (username) REFERENCES glccserver.User(username)); 43 | CREATE TABLE IF NOT EXISTS glccserver.Room(room_name VARCHAR(50) NOT NULL, username VARCHAR(20) NOT NULL, 44 | video_name VARCHAR(20) NOT NULL, start_time TIMESTAMP NOT NULL, end_time TIMESTAMP NOT NULL, 45 | PRIMARY KEY (username, video_name, room_name), 46 | FOREIGN KEY (username) REFERENCES glccserver.User(username), 47 | FOREIGN KEY (video_name) REFERENCES glccserver.Video(video_name)); 48 | 49 | CREATE TABLE IF NOT EXISTS glccserver.Room(room_name VARCHAR(50) NOT NULL UNIQUE, username VARCHAR(20) NOT NULL, 50 | video_name VARCHAR(20) NOT NULL, start_time TIMESTAMP NOT NULL, end_time TIMESTAMP NOT NULL, 51 | PRIMARY KEY (room_name), 52 | FOREIGN KEY (username) REFERENCES glccserver.User(username), 53 | FOREIGN KEY (video_name) REFERENCES glccserver.Video(video_name)); 54 | 55 | CREATE TABLE IF NOT EXISTS glccserver.Contour(contour_name VARCHAR(20) NOT NULL, username VARCHAR(20) NOT NULL, video_name VARCHAR(20) NOT NULL, contour_path JSON NOT NULL, 56 | PRIMARY KEY (username, video_name, contour_name), FOREIGN KEY (username) REFERENCES glccserver.User(username), 57 | FOREIGN KEY (video_name) REFERENCES glccserver.Video(video_name)); 58 | 59 | CREATE TABLE IF NOT EXISTS glccserver.File(file_path VARCHAR(256) NOT NULL, video_name VARCHAR(20) NOT NULL, 60 | username VARCHAR(20) NOT NULL, start_time TIMESTAMP NOT NULL, end_time TIMESTAMP NOT NULL, 61 | PRIMARY KEY (username, video_name, file_path), 62 | FOREIGN KEY (video_name) references glccserver.Video(video_name), 63 | FOREIGN KEY (username) REFERENCES glccserver.User(username)); 64 | 65 | DROP PROCEDURE IF EXISTS glccserver.proc_time_compare; 66 | CREATE PROCEDURE glccserver.proc_time_compare( 67 | IN start_time TIMESTAMP, 68 | IN end_time TIMESTAMP, 69 | OUT result TINYINT 70 | )BEGIN 71 | DECLARE ctime INT; 72 | set ctime = TIMESTAMPDIFF(SECOND, start_time, end_time); 73 | set result = IF(ctime >= 0, 1, -1); 74 | END; 75 | 76 | DROP FUNCTION IF EXISTS glccserver.func_time_compare; 77 | CREATE 78 | FUNCTION glccserver.func_time_compare(start_time TIMESTAMP, end_time TIMESTAMP) 79 | RETURNS INT 80 | BEGIN 81 | DECLARE ctime INT; 82 | DECLARE res INT; 83 | set ctime = TIMESTAMPDIFF(SECOND, start_time, end_time); 84 | set res = IF(ctime > 0, 1, -1); 85 | return res; 86 | END; 87 | 88 | DROP TRIGGER if exists glccserver.before_file_insert; 89 | create trigger glccserver.before_file_insert 90 | before insert on glccserver.File FOR EACH ROW 91 | begin 92 | DECLARE num INT DEFAULT 0; 93 | DECLARE msg VARCHAR(100); 94 | SELECT COUNT(*) INTO num FROM glccserver.Video WHERE video_name=NEW.video_name AND username=NEW.username; 95 | IF num <= 0 THEN 96 | set msg=concat("Video: Find the ", NEW.video_name, " failed!"); 97 | signal sqlstate '45000' set message_text=msg; 98 | END IF; 99 | END; 100 | 101 | 102 | DROP TRIGGER IF EXISTS glccserver.before_room_insert; 103 | CREATE TRIGGER glccserver.before_room_insert 104 | BEFORE INSERT ON glccserver.Room FOR EACH ROW 105 | BEGIN 106 | DECLARE num INT DEFAULT 0; 107 | DECLARE compare INT DEFAULT 0; 108 | DECLARE msg VARCHAR(100); 109 | SELECT COUNT(*) INTO num FROM glccserver.Video WHERE video_name=NEW.video_name AND username=NEW.username; 110 | IF num <= 0 THEN 111 | set msg=concat("Room: Find ", "username: ", NEW.username, ", video name: ", NEW.video_name, " failed!"); 112 | signal sqlstate "45000" set message_text=msg; 113 | END IF; 114 | CALL glccserver.proc_time_compare(NEW.start_time, NEW.end_time, compare); 115 | IF compare < 0 THEN 116 | SET msg=concat("Room: Insert ", "start_time: ", NEW.start_time, " end_time: ", NEW.end_time, " failed!"); 117 | signal sqlstate "45000" set message_text=msg; 118 | END IF; 119 | END; 120 | 121 | DROP TRIGGER IF EXISTS glccserver.before_video_insert; 122 | CREATE TRIGGER glccserver.before_video_insert 123 | BEFORE INSERT ON glccserver.Video FOR EACH ROW 124 | BEGIN 125 | DECLARE num INT DEFAULT 0; 126 | DECLARE msg VARCHAR(100); 127 | SELECT COUNT(*) INTO num FROM glccserver.User WHERE username=NEW.username; 128 | IF num <= 0 THEN 129 | set msg=concat("Video: Find the ", NEW.username, " failed!"); 130 | signal sqlstate '45000' set message_text=msg; 131 | END IF; 132 | END; 133 | 134 | DROP TRIGGER IF EXISTS glccserver.before_contour_insert; 135 | CREATE TRIGGER glccserver.before_contour_insert 136 | BEFORE INSERT ON glccserver.Contour FOR EACH ROW 137 | BEGIN 138 | DECLARE num INT DEFAULT 0; 139 | DECLARE msg VARCHAR(100); 140 | SELECT COUNT(*) INTO num FROM glccserver.Video WHERE video_name=NEW.video_name; 141 | IF num <= 0 THEN 142 | set msg=concat("Video: Find the ", NEW.video_name, " failed!"); 143 | signal sqlstate '45000' set message_text=msg; 144 | END IF; 145 | END; 146 | 147 | DROP TRIGGER IF EXISTS glccserver.before_user_delete; 148 | CREATE TRIGGER glccserver.before_user_delete 149 | BEFORE DELETE ON glccserver.User FOR EACH ROW 150 | BEGIN 151 | DELETE FROM glccserver.Video WHERE username=OLD.username; 152 | END; 153 | 154 | DROP TRIGGER IF EXISTS glccserver.before_video_delete; 155 | CREATE TRIGGER glccserver.before_video_delete 156 | BEFORE DELETE ON glccserver.Video FOR EACH ROW 157 | BEGIN 158 | DELETE FROM glccserver.Room WHERE video_name=OLD.video_name and username=OLD.username; 159 | DELETE FROM glccserver.Contour WHERE video_name=OLD.video_name and username=OLD.username; 160 | DELETE from glccserver.File where video_name=OLD.video_name and username=OLD.username; 161 | END; 162 | 163 | )"; 164 | } 165 | 166 | 167 | std::string get_now_time(const std::string & time_format) noexcept { 168 | std::stringstream ss; 169 | auto time_now = std::chrono::system_clock::now(); 170 | time_t now = std::chrono::system_clock::to_time_t(time_now); 171 | ss << std::put_time(localtime(&now), time_format.c_str()); 172 | return ss.str(); 173 | } 174 | 175 | 176 | int get_cwd(std::string & file_path) noexcept { 177 | char * buffer; 178 | if ((buffer = getcwd(NULL, 0)) == NULL) { 179 | return -1; 180 | } else { 181 | file_path = buffer; 182 | free(buffer); 183 | return 0; 184 | } 185 | } 186 | 187 | int parse_path(const std::string & path, std::unordered_map & result_map) noexcept { 188 | static std::vector fields{"path", "dirname", "basename", "stem", "suffix"}; 189 | static std::regex pattern{"^(.*)/((.*)\\.(.*))"}; 190 | struct stat st; 191 | if (stat(path.c_str(), &st) == 0) { 192 | if (S_ISDIR(st.st_mode)) { 193 | result_map["path"] = path; 194 | result_map["dirname"] = path; 195 | return 0; 196 | } else { 197 | std::smatch results; 198 | if (std::regex_match(path, results, pattern)) { 199 | for (int i = 0; i < (int)fields.size(); i++) { 200 | auto & key = fields[i]; 201 | result_map[key] = results[i]; 202 | } 203 | return 0; 204 | } else { 205 | return -1; 206 | } 207 | } 208 | } else { 209 | return -1; 210 | } 211 | } 212 | 213 | int read_file_list(const std::string & base_path, std::vector files) noexcept { 214 | DIR * dir; 215 | struct dirent * ptr; 216 | 217 | if ((dir = opendir(base_path.c_str())) == nullptr) { 218 | return -1; 219 | } 220 | 221 | while ((ptr = readdir(dir)) != nullptr) { 222 | if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) { 223 | continue; 224 | } else { 225 | std::stringstream ss; 226 | ss << base_path << "/" << ptr->d_name; 227 | files.emplace_back(ss.str()); 228 | } 229 | } 230 | return 0; 231 | } 232 | 233 | 234 | std::string join(std::vector &strings, std::string delim, 235 | std::function func) { 236 | func = func==nullptr ? [&delim](std::string &x, std::string &y) { 237 | return x.empty() ? y : x + delim + y;} : func; 238 | return std::accumulate( 239 | strings.begin(), strings.end(), std::string(), func 240 | ); 241 | } 242 | 243 | 244 | int check_dir(const std::string & check_path, const bool is_mkdir) noexcept { 245 | int ret; 246 | struct stat st; 247 | if (stat(check_path.c_str(), &st) == 0) { 248 | if (S_ISDIR(st.st_mode)) { 249 | return 0; 250 | } else { 251 | return -1; 252 | } 253 | } else { 254 | if (is_mkdir) { 255 | ret = mkdir(check_path.c_str(), 00700); 256 | return ret; 257 | } else { 258 | return -1; 259 | } 260 | } 261 | } 262 | 263 | 264 | int check_file(const std::string & check_path, 265 | std::vector * file_set, 266 | const std::vector suffix) noexcept { 267 | struct stat st; 268 | char buf[BUFSIZ] = {0}; 269 | if (stat(check_path.c_str(), &st) == 0) { 270 | if (S_ISDIR(st.st_mode)) { 271 | size_t total_file = 0; 272 | if (suffix.size()) { 273 | for (auto sfx: suffix) { 274 | std::snprintf(buf, sizeof(buf), "%s%s%s", check_path.c_str(), "/*.", sfx.c_str()); 275 | glob_t gl; 276 | /* `errno' value from the failing call; if it returns non-zero 277 | `glob' returns GLOB_ABEND; if it returns zero, the error is ignored. */ 278 | int ret = glob(buf, GLOB_ERR, nullptr, &gl); 279 | if (ret != 0) { 280 | return -2; 281 | } 282 | for (size_t i = 0; i < gl.gl_pathc; i++) { 283 | total_file ++; 284 | const char * subpath = gl.gl_pathv[i]; 285 | if (file_set != nullptr) { 286 | file_set->emplace_back(subpath); 287 | } 288 | } 289 | globfree(&gl); 290 | } 291 | } else { 292 | if (file_set != nullptr) { 293 | file_set->emplace_back(check_path); 294 | } 295 | } 296 | return 1 + total_file; 297 | } else if (S_ISREG(st.st_mode)) { 298 | if (file_set != nullptr) { 299 | file_set->emplace_back(check_path); 300 | } 301 | return 0; 302 | } 303 | } else { 304 | return -1; 305 | } 306 | return 0; 307 | } 308 | } -------------------------------------------------------------------------------- /src/dealtor.cpp: -------------------------------------------------------------------------------- 1 | #include "dealtor.h" 2 | 3 | namespace GLCC{ 4 | ObjectDetector::ObjectDetector(const char * model_path, 5 | const char * device_name, 6 | const int device_id) { 7 | int ret = mmdeploy_detector_create_by_path(model_path, device_name, device_id, &detector); 8 | if (ret != MM_SUCCESS) { 9 | LOG_F(ERROR, "Create detector failed! Code: %d", (int)ret); 10 | state = -1; 11 | return; 12 | } 13 | } 14 | 15 | ObjectDetector::ObjectDetector(const std::string & model_path, 16 | const std::string & device_name, 17 | const int device_id) { 18 | int ret = mmdeploy_detector_create_by_path(model_path.c_str(), device_name.c_str(), device_id, &detector); 19 | if (ret != MM_SUCCESS) { 20 | LOG_F(ERROR, "Create detector failed! Code: %d", (int)ret); 21 | state = -1; 22 | return; 23 | } 24 | } 25 | 26 | ObjectDetector::~ObjectDetector() { 27 | LOG_F(INFO, "[ObjectDetector] Release ObjectDetector"); 28 | } 29 | 30 | int ObjectDetector::dect(cv::Mat & img, std::vector & objects, float score_thre) { 31 | int ret; 32 | mm_mat_t mat{img.data, img.rows, img.cols, 3, MM_BGR, MM_INT8}; 33 | mm_detect_t * bboxes; 34 | int * res_count; 35 | ret = mmdeploy_detector_apply(detector, &mat, 1, & bboxes, &res_count); 36 | if (ret != MM_SUCCESS) { 37 | LOG_F(ERROR, "Apply detector failed! Code: %d", (int)ret); 38 | return -1; 39 | } 40 | for (int obj_id = 0; obj_id < *res_count; obj_id++) { 41 | const auto & score = bboxes[obj_id].score; 42 | if (score < score_thre) { 43 | continue; 44 | 45 | } 46 | const auto & box = bboxes[obj_id].bbox; 47 | if ((box.right - box.left) < 1 || (box.bottom - box.top) < 1) { 48 | continue; 49 | } 50 | const auto & label_id = bboxes[obj_id].label_id; 51 | objects.emplace_back( 52 | (cv::Rect_){ 53 | box.left, box.top, 54 | box.right - box.left, 55 | box.bottom - box.top, 56 | }, 57 | label_id, 58 | score 59 | ); 60 | } 61 | mmdeploy_detector_release_result(bboxes, res_count, 1); 62 | return 0; 63 | } 64 | 65 | cv::Scalar ObjectDetector::get_color() { 66 | return Scalar(rand() % 255, rand() % 255, rand() % 255); 67 | } 68 | 69 | int ObjectDetector::run(void * args, 70 | std::function cancel_func, 71 | std::function deal_func) { 72 | int ret, state; 73 | detector_run_context_t * context = (detector_run_context_t *) args; 74 | const std::string video_path = context->video_path; 75 | const std::string upload_path = context->upload_path; 76 | const Json::Value extra_config = context->vis_params; 77 | const float score_thre = extra_config["score_thre"].asFloat(); 78 | const int into_contour_time_gap_second = extra_config["into_contour_time_gap_second"].asInt(); 79 | const int out_contour_time_gap_second = extra_config["out_contour_time_gap_second"].asInt(); 80 | const bool imshow_result_image = extra_config["imshow_result_image"].asBool(); 81 | std::vector class_names; 82 | for (int i = 0; i < (int)extra_config["class_names"].size(); i++) { 83 | class_names.emplace_back(extra_config["class_names"].asString()); 84 | } 85 | // video 86 | cv::Mat frame; 87 | cv::VideoCapture capture; 88 | ret = capture.open(video_path); 89 | if (!ret) { 90 | LOG_F(ERROR, "[ObjectDetector][Runner] Open %s failed!", video_path.c_str()); 91 | if (cancel_func != nullptr) { 92 | cancel_func(nullptr); 93 | } 94 | capture.release(); 95 | cv::destroyAllWindows(); 96 | return -1; 97 | } 98 | 99 | const int width = capture.get(cv::CAP_PROP_FRAME_WIDTH); 100 | const int height = capture.get(cv::CAP_PROP_FRAME_HEIGHT); 101 | const int fps = capture.get(cv::CAP_PROP_FPS); 102 | 103 | // command 104 | char command[512] = {0}; 105 | std::snprintf(command, sizeof(command), \ 106 | constants::ffmpeg_push_command.c_str(), \ 107 | width, height, fps, upload_path.c_str()); 108 | FILE* fp = popen(command, "w"); 109 | 110 | if (fp == nullptr) { 111 | LOG_F(ERROR, "[ObjectDetector][Runner] Couldn't open process pipe with command: %s", command); 112 | state = -1; 113 | if (cancel_func != nullptr) { 114 | cancel_func(nullptr); 115 | } 116 | capture.release(); 117 | cv::destroyAllWindows(); 118 | pclose(fp); 119 | return -1; 120 | } 121 | 122 | LOG_F(INFO, "\n[ObjectDetector][Runner]\n" 123 | "Read the video from %s: \n" 124 | "width: %d | height: %d | fps: %d.\n" 125 | "Push the video to %s" 126 | "Extra config: %s", 127 | video_path.c_str(), 128 | width, height, fps, 129 | upload_path.c_str(), 130 | extra_config.toStyledString().c_str()); 131 | 132 | // time to recorder 133 | int into_recoder_time_gap = into_contour_time_gap_second * 1000; 134 | int out_recoder_time_gap = out_contour_time_gap_second * 1000; 135 | std::unordered_map is_in_contour = {}; 136 | std::unordered_map into_contour_time_point = {}; 137 | std::unordered_map out_contour_time_point = {}; 138 | 139 | std::stringstream video_save_path; 140 | cv::VideoWriter video_writer; 141 | int video_type = (int)capture.get(CAP_PROP_FOURCC); 142 | 143 | for(;;) { 144 | ret = capture.read(frame); 145 | 146 | if (ret == 0) { 147 | break; 148 | } 149 | if (frame.empty()) { 150 | continue; 151 | } 152 | 153 | if (this->state < 1) { 154 | break; 155 | }; 156 | 157 | std::vector objects; 158 | ret = dect(frame, objects, score_thre); 159 | if (ret == -1) { 160 | LOG_F(ERROR, "[ObjectDetector][Runner] Dect image failed!"); 161 | state = -1; 162 | break; 163 | } 164 | 165 | for (auto & object : objects) { 166 | Scalar color = get_color(); 167 | auto tl = object.rect.tl(); auto br = object.rect.br(); 168 | auto ctr = (tl + br) / 2; 169 | cv::putText(frame, cv::format("%s: %.3f", class_names[object.label].c_str(), object.prob), cv::Point(tl.x, tl.y - 5), 170 | 0, 0.6, cv::Scalar(0, 0, 255), 2, LINE_AA); 171 | cv::rectangle(frame, object.rect, color, 2); 172 | cv::circle(frame, cv::Point(ctr.x, ctr.y), 10, color, -1); 173 | } 174 | 175 | if (is_put_lattice) { 176 | auto time_now = std::chrono::system_clock::now(); 177 | for (auto & item: contour_list) { 178 | auto & name = item.first; 179 | auto & contour = item.second; 180 | int ret = -1; 181 | for (auto & object : objects) { 182 | auto tl = object.rect.tl(); auto br = object.rect.br(); 183 | auto ctr = (tl + br) / 2; 184 | ret = cv::pointPolygonTest(contour, ctr, false); 185 | if (ret >= 0) { 186 | break; 187 | } 188 | } 189 | 190 | if (ret >= 0) { 191 | if (is_in_contour.size() == 0) { 192 | if (into_contour_time_point.find(name) == into_contour_time_point.end()) { 193 | into_contour_time_point[name] = time_now; 194 | } 195 | } 196 | out_contour_time_point.erase(name); 197 | } else { 198 | if (out_contour_time_point.find(name) == out_contour_time_point.end()) { 199 | out_contour_time_point[name] = time_now; 200 | } 201 | } 202 | } 203 | 204 | std::vector into_erase_key = {}; 205 | for (auto & item : into_contour_time_point) { 206 | auto & name = item.first; 207 | auto & time_point = item.second; 208 | if (contour_list.find(name) != contour_list.end()) { 209 | auto time_gap = std::chrono::duration_cast(time_now - time_point); 210 | if (time_gap.count() > into_recoder_time_gap) { 211 | if (!video_writer.isOpened()) { 212 | if (resource_dir != "") { 213 | time_t now = std::chrono::system_clock::to_time_t(time_now); 214 | video_save_path.clear(); 215 | video_save_path.str(""); 216 | video_save_path << resource_dir << "/" 217 | << std::put_time(localtime(&now), constants::file_time_format.c_str()) 218 | << ".mp4"; 219 | video_writer.open(video_save_path.str(), video_type, fps, frame.size()); 220 | if (deal_func != nullptr) { 221 | deal_func(&video_save_path); 222 | } 223 | } 224 | } 225 | is_in_contour[name] = true; 226 | into_erase_key.emplace_back(name); 227 | } 228 | } else { 229 | out_contour_time_point.erase(name); 230 | is_in_contour.erase(name); 231 | } 232 | } 233 | 234 | for (auto & name : into_erase_key) { 235 | into_contour_time_point.erase(name); 236 | } 237 | 238 | std::vector out_erase_key = {}; 239 | for (auto & item : out_contour_time_point) { 240 | auto & name = item.first; 241 | auto & time_point = item.second; 242 | auto time_gap = std::chrono::duration_cast(time_now - time_point); 243 | if (time_gap.count() > out_recoder_time_gap) { 244 | is_in_contour.erase(name); 245 | into_contour_time_point.erase(name); 246 | out_erase_key.emplace_back(name); 247 | } 248 | } 249 | 250 | for (auto & name : out_erase_key) { 251 | out_contour_time_point.erase(name); 252 | } 253 | 254 | for (auto & item : contour_list) { 255 | cv::Scalar color = {0, 0, 255}; 256 | auto & name = item.first; 257 | auto & contour = item.second; 258 | if (is_in_contour.find(name) != is_in_contour.end()) { 259 | cv::Mat tmp{frame.rows, frame.cols, CV_8UC3, cv::Scalar(0)}; 260 | cv::fillPoly(tmp, contour, color, 8); 261 | cv::addWeighted(frame, 0.9, tmp, 0.1, 0, frame); 262 | } else { 263 | cv::polylines(frame, contour, true, color, 3); 264 | } 265 | } 266 | 267 | if (video_writer.isOpened()) { 268 | video_writer.write(frame); 269 | } 270 | 271 | if (is_in_contour.size() == 0) { 272 | if (video_writer.isOpened()) { 273 | video_writer.release(); 274 | std::unordered_map path_parse_results = {}; 275 | int ret = parse_path(video_save_path.str(), path_parse_results); 276 | if (ret == -1) { 277 | LOG_F(WARNING, "[ObjectDetector][Runner] Save cover path fail!"); 278 | } else { 279 | auto & dirname = path_parse_results["dirname"]; 280 | auto & stem = path_parse_results["stem"]; 281 | std::string cover_save_path = dirname + "/" + stem + "." + constants::cover_save_suffix; 282 | std::string command = "ffmpeg -y -i " + video_save_path.str() + " -ss 1 -frames:v 1 " + cover_save_path; 283 | system(command.c_str()); 284 | } 285 | } 286 | } 287 | } 288 | 289 | ret = fwrite(frame.data, sizeof(char), frame.total() * frame.elemSize(), fp); 290 | if (ret <= 0) { 291 | LOG_F(ERROR, "[ObjectDetector][Runner] Write push pipe failed"); 292 | state = -1; 293 | break; 294 | } 295 | if (imshow_result_image) { 296 | cv::imshow(video_path, frame); 297 | if (cv::waitKey(10) == ESC) break ; 298 | } 299 | } 300 | 301 | if (cancel_func != nullptr) { 302 | cancel_func(nullptr); 303 | } 304 | capture.release(); 305 | cv::destroyAllWindows(); 306 | 307 | if (video_writer.isOpened()) { 308 | video_writer.release(); 309 | std::unordered_map path_parse_results = {}; 310 | int ret = parse_path(video_save_path.str(), path_parse_results); 311 | if (ret == -1) { 312 | LOG_F(WARNING, "[TrackerDetector][Runner] Save cover path fail!"); 313 | } else { 314 | auto & dirname = path_parse_results["dirname"]; 315 | auto & stem = path_parse_results["stem"]; 316 | std::string cover_save_path = dirname + "/" + stem + "." + constants::cover_save_suffix; 317 | std::string command = "ffmpeg -y -i " + video_save_path.str() + " -ss 1 -frames:v 1 " + cover_save_path; 318 | system(command.c_str()); 319 | } 320 | } 321 | pclose(fp); 322 | return state; 323 | } 324 | 325 | Detector * ObjectDetector::init_func(void * args) { 326 | Json::Value params = *(Json::Value *)args; 327 | std::string model_path = params["model"].asString(); 328 | std::string device_name = params["device"].asString(); 329 | const int device_id = params["device_id"].asInt(); 330 | std::string resource_dir = params["resource_dir"].asString(); 331 | return new ObjectDetector(model_path, device_name, device_id); 332 | } 333 | 334 | 335 | TrackerDetector::TrackerDetector(const char * model_path, 336 | const char * device_name, 337 | const int device_id): ObjectDetector(model_path, device_name, device_id) { 338 | } 339 | 340 | TrackerDetector::TrackerDetector(const std::string & model_path, 341 | const std::string & device_name, 342 | const int device_id): ObjectDetector(model_path, device_name, device_id) { 343 | } 344 | 345 | TrackerDetector::~TrackerDetector() { 346 | LOG_F(INFO, "[TrackerDetector][Runner] Release TrackerDetector"); 347 | } 348 | 349 | int TrackerDetector::dect(cv::Mat & img, std::vector & objects, float score_thre) { 350 | int ret; 351 | mm_mat_t mat{img.data, img.rows, img.cols, 3, MM_BGR, MM_INT8}; 352 | mm_detect_t * bboxes; 353 | int * res_count; 354 | ret = mmdeploy_detector_apply(detector, &mat, 1, & bboxes, &res_count); 355 | if (ret != MM_SUCCESS) { 356 | LOG_F(ERROR, "[TrackerDetector][DECT] Apply detector failed! Code: %d", (int)ret); 357 | return -1; 358 | } 359 | for (int obj_id = 0; obj_id < *res_count; obj_id++) { 360 | const auto & score = bboxes[obj_id].score; 361 | if (score < score_thre) { 362 | continue; 363 | 364 | } 365 | const auto & box = bboxes[obj_id].bbox; 366 | if ((box.right - box.left) < 1 || (box.bottom - box.top) < 1) { 367 | continue; 368 | } 369 | const auto & label_id = bboxes[obj_id].label_id; 370 | objects.emplace_back( 371 | (cv::Rect_){ 372 | box.left, box.top, 373 | box.right - box.left, 374 | box.bottom - box.top, 375 | }, 376 | label_id, 377 | score 378 | ); 379 | } 380 | mmdeploy_detector_release_result(bboxes, res_count, 1); 381 | return 0; 382 | } 383 | 384 | int TrackerDetector::run(void * args, 385 | std::function cancel_func, 386 | std::function deal_func) { 387 | int ret, state; 388 | detector_run_context_t * context = (detector_run_context_t *) args; 389 | const std::string video_path = context->video_path; 390 | const std::string upload_path = context->upload_path; 391 | const Json::Value extra_config = context->vis_params; 392 | const float score_thre = extra_config["score_thre"].asFloat(); 393 | const int tracker_buffer = extra_config["tracker_buffer"].asInt(); 394 | const int into_contour_time_gap_second = extra_config["into_contour_time_gap_second"].asInt(); 395 | const int out_contour_time_gap_second = extra_config["out_contour_time_gap_second"].asInt(); 396 | const bool imshow_result_image = extra_config["imshow_result_image"].asBool(); 397 | const float wh_ratio_thre_to_show = extra_config["wh_ratio_thre_to_show"].asFloat(); 398 | const float wh_multiply_thre_to_show = extra_config["wh_multiply_thre_to_show"].asFloat(); 399 | std::vector class_names; 400 | for (int i = 0; i < (int)extra_config["class_names"].size(); i++) { 401 | class_names.emplace_back(extra_config["class_names"][i].asString()); 402 | } 403 | 404 | // video 405 | cv::Mat frame; 406 | cv::VideoCapture capture; 407 | ret = capture.open(video_path); 408 | if (!ret) { 409 | LOG_F(ERROR, "[TrackerDetector][Runner] Open %s failed!", video_path.c_str()); 410 | if (cancel_func != nullptr) { 411 | cancel_func(nullptr); 412 | } 413 | capture.release(); 414 | cv::destroyAllWindows(); 415 | return -1; 416 | } 417 | 418 | const int width = capture.get(cv::CAP_PROP_FRAME_WIDTH); 419 | const int height = capture.get(cv::CAP_PROP_FRAME_HEIGHT); 420 | const int fps = capture.get(cv::CAP_PROP_FPS); 421 | 422 | // command 423 | char command[512] = {0}; 424 | std::snprintf(command, sizeof(command), \ 425 | constants::ffmpeg_push_command.c_str(), \ 426 | width, height, fps, upload_path.c_str()); 427 | FILE* fp = popen(command, "w"); 428 | 429 | if (fp == nullptr) { 430 | LOG_F(ERROR, "[TrackerDetector][Runner] Couldn't open process pipe with command: %s", command); 431 | state = -1; 432 | if (cancel_func != nullptr) { 433 | cancel_func(nullptr); 434 | } 435 | capture.release(); 436 | cv::destroyAllWindows(); 437 | pclose(fp); 438 | return -1; 439 | } 440 | 441 | LOG_F(INFO, "\n[TrackerDetector][Runner]\n" 442 | "Read the video from %s: \n" 443 | "width: %d | height: %d | fps: %d.\n" 444 | "Push the video to %s\n" 445 | "Extra config: %s", 446 | video_path.c_str(), 447 | width, height, fps, 448 | upload_path.c_str(), 449 | extra_config.toStyledString().c_str()); 450 | 451 | // byteTracker 452 | BYTETracker tracker(fps, tracker_buffer); 453 | int num_frames = 0; 454 | 455 | // time to recorder 456 | int into_recoder_time_gap = into_contour_time_gap_second * 1000; 457 | int out_recoder_time_gap = out_contour_time_gap_second * 1000; 458 | std::unordered_map is_in_contour = {}; 459 | std::unordered_map into_contour_time_point = {}; 460 | std::unordered_map out_contour_time_point = {}; 461 | 462 | std::stringstream video_save_path; 463 | cv::VideoWriter video_writer; 464 | int video_type = (int)capture.get(CAP_PROP_FOURCC); 465 | 466 | for(;;) { 467 | ret = capture.read(frame); 468 | num_frames++; 469 | 470 | if (ret == 0) { 471 | break; 472 | } 473 | if (frame.empty()) { 474 | continue; 475 | } 476 | 477 | if (this->state < 1) { 478 | break; 479 | }; 480 | 481 | std::vector objects; 482 | ret = dect(frame, objects, score_thre); 483 | if (ret == -1) { 484 | LOG_F(ERROR, "[TrackerDetector][Runner] Dect image failed!"); 485 | state = -1; 486 | break; 487 | } 488 | 489 | std::vector stracks = tracker.update(objects); 490 | std::vector stracks_show; 491 | 492 | for (auto & strack : stracks) { 493 | auto & tlwh = strack.tlwh; 494 | auto xyah = strack.to_xyah(); 495 | bool wh_ratio = tlwh[2] / tlwh[3] > wh_ratio_thre_to_show; 496 | if (tlwh[2] * tlwh[3] > wh_multiply_thre_to_show && !wh_ratio) { 497 | stracks_show.emplace_back(strack); 498 | Scalar color = tracker.get_color(strack.track_id); 499 | cv::putText(frame, cv::format("id:%d: %.3f", strack.track_id, strack.score), cv::Point(tlwh[0], tlwh[1] - 5), 500 | 0, 0.6, cv::Scalar(0, 0, 255), 2, LINE_AA); 501 | cv::rectangle(frame, cv::Rect(tlwh[0], tlwh[1], tlwh[2], tlwh[3]), color, 2); 502 | cv::circle(frame, cv::Point(xyah[0], xyah[1]), 10, color, -1); 503 | } 504 | } 505 | 506 | if (is_put_lattice) { 507 | auto time_now = std::chrono::system_clock::now(); 508 | for (auto & item: contour_list) { 509 | auto & name = item.first; 510 | auto & contour = item.second; 511 | int ret = -1; 512 | for (auto & strack : stracks_show) { 513 | auto xyah = strack.to_xyah(); 514 | cv::Point ctr(xyah[0], xyah[1]); 515 | ret = cv::pointPolygonTest(contour, ctr, false); 516 | if (ret >= 0) { 517 | break; 518 | } 519 | } 520 | 521 | if (ret >= 0) { 522 | if (is_in_contour.size() == 0) { 523 | if (into_contour_time_point.find(name) == into_contour_time_point.end()) { 524 | into_contour_time_point[name] = time_now; 525 | } 526 | } 527 | out_contour_time_point.erase(name); 528 | } else { 529 | if (out_contour_time_point.find(name) == out_contour_time_point.end()) { 530 | out_contour_time_point[name] = time_now; 531 | } 532 | } 533 | } 534 | 535 | std::vector into_erase_key = {}; 536 | for (auto & item : into_contour_time_point) { 537 | auto & name = item.first; 538 | auto & time_point = item.second; 539 | if (contour_list.find(name) != contour_list.end()) { 540 | auto time_gap = std::chrono::duration_cast(time_now - time_point); 541 | if (time_gap.count() > into_recoder_time_gap) { 542 | if (!video_writer.isOpened()) { 543 | if (resource_dir != "") { 544 | time_t now = std::chrono::system_clock::to_time_t(time_now); 545 | video_save_path.clear(); 546 | video_save_path.str(""); 547 | video_save_path << resource_dir << "/" 548 | << std::put_time(localtime(&now), constants::file_time_format.c_str()) 549 | << ".mp4"; 550 | video_writer.open(video_save_path.str(), video_type, fps, frame.size()); 551 | if (deal_func != nullptr && video_writer.isOpened()) { 552 | deal_func(&video_save_path); 553 | } 554 | } 555 | } 556 | is_in_contour[name] = true; 557 | into_erase_key.emplace_back(name); 558 | } 559 | } else { 560 | out_contour_time_point.erase(name); 561 | is_in_contour.erase(name); 562 | } 563 | } 564 | 565 | for (auto & name : into_erase_key) { 566 | into_contour_time_point.erase(name); 567 | } 568 | 569 | std::vector out_erase_key = {}; 570 | for (auto & item : out_contour_time_point) { 571 | auto & name = item.first; 572 | auto & time_point = item.second; 573 | auto time_gap = std::chrono::duration_cast(time_now - time_point); 574 | if (time_gap.count() > out_recoder_time_gap) { 575 | is_in_contour.erase(name); 576 | into_contour_time_point.erase(name); 577 | out_erase_key.emplace_back(name); 578 | } 579 | } 580 | 581 | for (auto & name : out_erase_key) { 582 | out_contour_time_point.erase(name); 583 | } 584 | 585 | for (auto & item : contour_list) { 586 | cv::Scalar color = {0, 0, 255}; 587 | auto & name = item.first; 588 | auto & contour = item.second; 589 | if (is_in_contour.find(name) != is_in_contour.end()) { 590 | cv::Mat tmp{frame.rows, frame.cols, CV_8UC3, cv::Scalar(0)}; 591 | cv::fillPoly(tmp, contour, color, 8); 592 | cv::addWeighted(frame, 0.9, tmp, 0.1, 0, frame); 593 | } else { 594 | cv::polylines(frame, contour, true, color, 3); 595 | } 596 | } 597 | 598 | if (video_writer.isOpened()) { 599 | video_writer.write(frame); 600 | } 601 | 602 | if (is_in_contour.size() == 0) { 603 | if (video_writer.isOpened()) { 604 | video_writer.release(); 605 | std::unordered_map path_parse_results = {}; 606 | int ret = parse_path(video_save_path.str(), path_parse_results); 607 | if (ret == -1) { 608 | LOG_F(WARNING, "[TrackerDetector][Runner] Save cover path fail!"); 609 | } else { 610 | auto & dirname = path_parse_results["dirname"]; 611 | auto & stem = path_parse_results["stem"]; 612 | std::string cover_save_path = dirname + "/" + stem + "." + constants::cover_save_suffix; 613 | std::string command = "ffmpeg -y -i " + video_save_path.str() + " -ss 1 -frames:v 1 " + cover_save_path; 614 | system(command.c_str()); 615 | } 616 | } 617 | } 618 | } 619 | 620 | ret = fwrite(frame.data, sizeof(char), frame.total() * frame.elemSize(), fp); 621 | if (ret <= 0) { 622 | LOG_F(ERROR, "[TrackerDetector][Runner] Write push pipe failed"); 623 | state = -1; 624 | break; 625 | } 626 | if (imshow_result_image) { 627 | cv::imshow(video_path, frame); 628 | if (cv::waitKey(10) == ESC) break ; 629 | } 630 | } 631 | 632 | if (cancel_func != nullptr) { 633 | cancel_func(nullptr); 634 | } 635 | capture.release(); 636 | cv::destroyAllWindows(); 637 | 638 | if (video_writer.isOpened()) { 639 | video_writer.release(); 640 | std::unordered_map path_parse_results = {}; 641 | int ret = parse_path(video_save_path.str(), path_parse_results); 642 | if (ret == -1) { 643 | LOG_F(WARNING, "[TrackerDetector][Runner] Save cover path fail!"); 644 | } else { 645 | auto & dirname = path_parse_results["dirname"]; 646 | auto & stem = path_parse_results["stem"]; 647 | std::string cover_save_path = dirname + "/" + stem + "." + constants::cover_save_suffix; 648 | std::string command = "ffmpeg -y -i " + video_save_path.str() + " -ss 1 -frames:v 1 " + cover_save_path; 649 | system(command.c_str()); 650 | } 651 | } 652 | pclose(fp); 653 | return state; 654 | } 655 | 656 | Detector * TrackerDetector::init_func(void * args) { 657 | Json::Value params = *(Json::Value *)args; 658 | std::string model_path = params["model"].asString(); 659 | std::string device_name = params["device"].asString(); 660 | const int device_id = params["device_id"].asInt(); 661 | return new TrackerDetector(model_path, device_name, device_id); 662 | } 663 | 664 | } -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "server.h" 5 | #include "common.h" 6 | 7 | int main(int argc, char ** argv) 8 | { 9 | std::string config_path = argv[1]; 10 | loguru::init(argc, argv); 11 | Json::Value config_root; Json::Reader reader; 12 | 13 | std::ifstream ifs; 14 | ifs.open(config_path); 15 | auto ret = reader.parse(ifs, config_root); 16 | if (!ret) { 17 | perror("Parse the log failed"); 18 | exit(1); 19 | } 20 | Json::Value log_root = config_root["Log"]; 21 | const std::string log_dir = log_root["log_dir"].asString(); 22 | const std::string log_file_time_format = log_root["log_file_time_format"].asString(); 23 | const std::string log_add_file_verbosity_s = log_root["log_add_file_verbosity"].asString(); 24 | const std::string log_all_file_verbosity_s = log_root["log_all_file_verbosity"].asString(); 25 | 26 | const std::string all_log_file = log_dir + "/" + "all.log"; 27 | const std::string local_log_file = log_dir + "/" + GLCC::get_now_time(log_file_time_format) + ".log"; 28 | 29 | ret = loguru::create_directories(log_dir.c_str()); 30 | if (!ret) { 31 | perror("Create the log dir failed"); 32 | exit(1); 33 | } 34 | loguru::add_file(all_log_file.c_str(), loguru::Append, loguru::get_verbosity_from_name(log_all_file_verbosity_s.c_str())); 35 | loguru::add_file(local_log_file.c_str(), loguru::Truncate, loguru::get_verbosity_from_name(log_add_file_verbosity_s.c_str()));; 36 | 37 | Json::Value livego_root = config_root["LiveGo"]; 38 | const int camera_push_port = livego_root["camera_push_port"].asInt(); 39 | const int dect_push_port = livego_root["dect_push_port"].asInt(); 40 | const int state_check_port = livego_root["state_check_port"].asInt(); 41 | GLCC::constants::video_path_template = "rtsp://" + GLCC::constants::localhost \ 42 | + ":" + std::to_string(camera_push_port) + "/live/%s"; 43 | GLCC::constants::livego_push_url_template = "rtmp://" + GLCC::constants::localhost \ 44 | + ":" + std::to_string(dect_push_port) + "/live/%s"; 45 | GLCC::constants::livego_check_stat_template = "http://" + GLCC::constants::localhost \ 46 | + ":" + std::to_string(state_check_port) + "/api/stat/group?stream_name=%s"; 47 | GLCC::constants::livego_kick_url = "http://" + GLCC::constants::localhost \ 48 | + ":" + std::to_string(state_check_port) + "/api/ctrl/kick_session"; 49 | 50 | Json::Value db_root = config_root["DB"]; 51 | const std::string user_name = db_root["user_name"].asString(); 52 | const std::string user_password = db_root["user_password"].asString(); 53 | const std::string db_server_ip = db_root["db_server_ip"].asString(); 54 | const int db_server_port = db_root["db_server_port"].asInt(); 55 | GLCC::constants::mysql_root_url = "mysql://" + user_name + ":" \ 56 | + user_password + "@" + db_server_ip + ":" + std::to_string(db_server_port); 57 | 58 | Json::Value timer_root = config_root["Timer"]; 59 | GLCC::constants::interval_to_watch_detector_second = timer_root["interval_to_watch_detector_second"].asInt() \ 60 | * GLCC::constants::num_second_per_minute; 61 | GLCC::constants::interval_to_watch_file_second = timer_root["interval_to_watch_file_second"].asInt() \ 62 | * GLCC::constants::num_second_per_minute; 63 | GLCC::constants::max_detector_live_day = timer_root["max_detector_live_day"].asInt(); 64 | GLCC::constants::max_video_file_save_day = timer_root["max_video_file_save_day"].asInt(); 65 | 66 | GLCC::GLCCServer server{config_path}; 67 | if (server.server_state == -1) { 68 | LOG_F(INFO, "Init GLCCServer fail!"); 69 | return -1; 70 | } 71 | ret = server.run(); 72 | if (server.server_state == -1) { 73 | LOG_F(INFO, "Run GLCCServer fail!"); 74 | return -1; 75 | } 76 | return 0; 77 | } 78 | --------------------------------------------------------------------------------