├── LICENSE ├── README.md ├── client ├── darknet_client.sln └── darknet_client │ ├── Makefile │ ├── darknet_client.vcxproj │ ├── darknet_client.vcxproj.filters │ ├── darknet_client.vcxproj.user │ └── src │ ├── args.cpp │ ├── args.hpp │ ├── base64.cpp │ ├── base64.hpp │ ├── frame.cpp │ ├── frame.hpp │ ├── main.cpp │ ├── mem_pool.cpp │ ├── mem_pool.hpp │ ├── share_queue.hpp │ ├── util.cpp │ └── util.hpp ├── server ├── Makefile ├── action.py ├── action_train.py ├── cfg │ └── openpose.cfg ├── src │ ├── DetectorInterface.hpp │ ├── Hungarian.cpp │ ├── Hungarian.h │ ├── KalmanTracker.cpp │ ├── KalmanTracker.h │ ├── Tracker.cpp │ ├── Tracker.hpp │ ├── args.cpp │ ├── args.hpp │ ├── base64.cpp │ ├── base64.h │ ├── frame.cpp │ ├── frame.hpp │ ├── mem_pool.cpp │ ├── mem_pool.hpp │ ├── people.cpp │ ├── people.hpp │ ├── pose_detector.cpp │ ├── pose_detector.hpp │ ├── share_queue.h │ ├── sink.cpp │ ├── ventilator.cpp │ ├── worker.cpp │ └── yolo_v2_class.hpp ├── train │ └── train.txt └── weights │ ├── action.h5 │ └── action2.h5 └── util ├── 171204_pose3_info.txt ├── 171204_pose5_info.txt ├── 171204_pose6_info.txt ├── concat.py └── cut.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Seungsu Lim 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 | ### Overview 2 | Real time Fight Detection Based on 2D Pose Estimation and RNN Action Recognition. 3 | 4 | This project is based on [darknet_server](https://github.com/imsoo/darknet_server). if you want to run this experiment take a look how to build [here](https://github.com/imsoo/darknet_server#how-to-build). 5 | 6 | 7 | | ```Fight Detection System Pipeline``` | 8 | |:---:| 9 | || 10 | 11 | ### Pose Estimation and Object Tracking 12 | Made pipeline to get 2D pose time series data in video sequence. 13 | 14 | In worker process, Pose Estimation is performed using ***OpenPose***. Input image pass through the Pose_detector, and get the people object which packed up people joint coordinates. People object serialized and send to sink process. 15 | 16 | In Sink process, people object convert to person objects. and every person object are send to Tracker. Tracker receive person object and produces object identities Using ***SORT***(simple online and realtime tracking algorithm). 17 | 18 | Finally, can get the joint time series data per each person. each person's Time series data is managed by queue container. so person object always maintain recent 32 frame. 19 | 20 | | ```Tracking Pipeline``` | ```Time series data``` | 21 | |:---:|:---:| 22 | ||| 23 | 24 | * #### Examples of Result (Pose Estimation and Object Tracking) 25 | 26 | ||| 27 | |:---:|:---:| 28 | 29 | 30 | ### Collect action video 31 | Collected Four Action type (Standing, Walking, Punching, Kicking) video. 32 | 33 | Punching Action type video is a subset of the ***Berkeley Multimodal Human Action Database (MHAD) dataset***. 34 | This video data is comprised of 12 subjects doing the punching actions for 5 repetitions, filmed from 4 angles. (http://tele-immersion.citris-uc.org/berkeley_mhad) 35 | 36 | Others (Standing, Walking, Kicking) are subsets of the ***CMU Panoptic Dataset***. In Range of Motion videos(171204_pose3, 171204_pose5, 171204_pose6), cut out 3 action type. I recorded timestamp per action type and cut out the video using python script(util/concat.py). This video data is comprised of 13 subjects doing the three actions, filmed from 31 angles. (http://domedb.perception.cs.cmu.edu/index.html) 37 | 38 | ``` jsonc 39 | 0, 1, 10 // 0 : Standing, 1 : begin timestamp, 10: end timestamp 40 | 1, 11, 15 // 1 : Walking, 11 : begin timestamp, 15: end timestamp 41 | 3, 39, 46 // 3 : Kicking, 39 : begin timestamp, 46: end timestamp 42 | ``` 43 | 44 | * #### Examples of Dataset (Stand & Walk) 45 | 46 | 47 | 48 | 54 | 60 | 61 |
Stand (CMU Panoptic Dataset)Walk (CMU Panoptic Dataset)
49 | 50 | ||| 51 | |:---:|:---:| 52 | ||| 53 | 55 | 56 | ||| 57 | |:---:|:---:| 58 | ||| 59 |
62 | 63 | * #### Examples of Dataset (Punch & Kick) 64 | 65 | 66 | 67 | 74 | 81 | 82 |
Punch (Berkeley MHAD Dataset)Kick (CMU Panoptic Dataset)
68 | 69 | ||| 70 | |:---:|:---:| 71 | ||| 72 | 73 | 75 | 76 | ||| 77 | |:---:|:---:| 78 | ||| 79 | 80 |
83 | 84 | ### Make training dataset 85 | Put action video data to tracking pipeline and get joint time series data per each person. this results (joint position) are processed to feature vector. 86 | 87 | * ***Angle*** : current frame joint angle 88 | * ***ΔPoint*** : A distance of prior frame joint point and current frame 89 | * ***ΔAngle*** : A change of prior frame joint angle and current frame. 90 | 91 | * #### Examples of feature vector (***ΔPoint*** & ***ΔAngle***) 92 | 93 | | ***ΔPoint*** | ***ΔAngle*** | 94 | |:---:|:---:| 95 | ||| 96 | 97 | * #### Overview of feature vector 98 | 99 | 100 | 101 | 102 | 112 | 118 | 119 |
Feature VectorOpenPose COCO output format
103 | 104 | | *IDX* | *0* | *1* | *2* | *3* | *4* | *5* | *6* | *7* | 105 | |:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| 106 | | ***Angle*** | 2-3 | 3-4 | 5-6 | 6-7 | 8-9 | 9-10 | 11-12 | 12-13 | 107 | | ***ΔAngle*** | 2-3 | 3-4 | 5-6 | 6-7 | 8-9 | 9-10 | 11-12 | 12-13 | 108 | | ***ΔPoint*** | 3 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 109 | 110 | ###### *※ 2 : RShoulder, 3 : RElbow, 4 : RWrist, 5 : LShoulder, 6 : LElbow, 7 : LWrist, 8 : RHip, 9 : RKnee, 10 : RAnkle, 11 : LHip, 12 : LKnee, 13 : LAnkle* 111 | 113 | 114 | || 115 | |:---:| 116 | 117 |
120 | 121 | Finally get each frame feature vector and then make action training data which consist of 32 frames feature vector. training datas are overlapped by 26 frames. so we got the four type action data set. 122 | A summary of the dataset is: 123 | * Standing : 7474 (7474 : pose3_stand) * 32 frame 124 | * Walking : 4213 (854 : pose3_walk, 3359 : pose6_walk) * 32 frame 125 | * Punching : 2187 (1115 : mhad_punch. 1072 : mhad_punch_flip) * 32 frame 126 | * Kicking : 4694 (2558 : pose3_kick, 2136 : pose6_kick) * 32 frame 127 | * total : 18573 * 32 frames (https://drive.google.com/open?id=1ZNJDzQUjo2lDPwGoVkRLg77eA57dKUqx) 128 | 129 | || 130 | |:---:| 131 | 132 | ### RNN Training and Result 133 | The network used in this experiment is based on that of : 134 | * Guillaume Chevalier, 'LSTMs for Human Activity Recognition, 2016' https://github.com/guillaume-chevalier/LSTM-Human-Activity-Recognition 135 | * stuarteiffert, 'RNN-for-Human-Activity-Recognition-using-2D-Pose-Input' https://github.com/stuarteiffert/RNN-for-Human-Activity-Recognition-using-2D-Pose-Input 136 | 137 | training was run for 300 epochs with a batch size of 1024. (weights/action.h5) 138 | 139 | After training, To get action recognition result in real time, made action recognition pipeline. Sink process send each person's time series feature vector to action process as string. Action process put received data into RNN network and send back results of prediction. (0 : Standing, 1 : Walking, 2 : Punching, 3 : Kicking) 140 | 141 | | ```Action Recognition Pipeline``` | ```RNN Model``` | 142 | |:---:|:---:| 143 | ||| 144 | 145 | 146 | * #### Examples of Result (RNN Action Recognition) 147 | | [```standing```](https://www.youtube.com/watch?v=Orc0Eq9bWOs) | [```Walking```](https://www.youtube.com/watch?v=Orc0Eq9bWOs) | [```Punching```](https://www.youtube.com/watch?v=kbgkeTTSau8) | [```Kicking```](https://www.youtube.com/watch?v=R1UWcG9N6tI) | 148 | |:---:|:---:|:---:|:---:| 149 | ||||| 150 | 151 | ### Fight Detection 152 | 153 | This stage check that person who kick or punch is hitting someone. if some person has hit other, those people set enemy each other. 154 | System count it as fight and then track them until they exist in frame. 155 | 156 | | ```Fight Detection Pipeline``` | 157 | |:---:| 158 | || 159 | 160 | * #### Examples of Result 161 | | [```Fighting Championship```](https://www.youtube.com/watch?v=cIhoK4cPbC4) | [```CCTV Video```](https://www.youtube.com/watch?v=stJPOb6zW7U) | 162 | |:---:|:---:| 163 | ||| 164 | 165 | | [```Sparring video A```](https://www.youtube.com/watch?v=x0kJmieuFzI) | [```Sparring video B```](https://www.youtube.com/watch?v=x0kJmieuFzI) | 166 | |:---:|:---:| 167 | ||| 168 | 169 | * #### Examples of Result (Failure case) 170 | | [```Fake Person```](https://www.youtube.com/watch?v=kbgkeTTSau8) | [```Small Person```](https://www.youtube.com/watch?v=aUdKzb4LGJI) | 171 | |:---:|:---:| 172 | ||| 173 | 174 | ### References 175 | * #### Darknet : https://github.com/AlexeyAB/darknet 176 | * #### OpenCV : https://github.com/opencv/opencv 177 | * #### ZeroMQ : https://github.com/zeromq/libzmq 178 | * #### json-c : https://github.com/json-c/json-c 179 | * #### openpose-darknet : https://github.com/lincolnhard/openpose-darknet 180 | * #### sort-cpp : https://github.com/mcximing/sort-cpp 181 | * #### cpp-base64 : https://github.com/ReneNyffenegger/cpp-base64 182 | * #### mem_pool : https://www.codeproject.com/Articles/27487/Why-to-use-memory-pool-and-how-to-implement-it 183 | * #### share_queue : https://stackoverflow.com/questions/36762248/why-is-stdqueue-not-thread-safe 184 | * #### RNN-for-Human-Activity-Recognition-using-2D-Pose-Input : https://github.com/stuarteiffert/RNN-for-Human-Activity-Recognition-using-2D-Pose-Input 185 | * #### LSTM-Human-Activity-Recognition : https://github.com/guillaume-chevalier/LSTM-Human-Activity-Recognition 186 | -------------------------------------------------------------------------------- /client/darknet_client.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.852 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "darknet_client", "darknet_client\darknet_client.vcxproj", "{D8EF8C1B-C4C1-4C68-A033-31AD741BE59A}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {D8EF8C1B-C4C1-4C68-A033-31AD741BE59A}.Debug|x64.ActiveCfg = Debug|x64 17 | {D8EF8C1B-C4C1-4C68-A033-31AD741BE59A}.Debug|x64.Build.0 = Debug|x64 18 | {D8EF8C1B-C4C1-4C68-A033-31AD741BE59A}.Debug|x86.ActiveCfg = Debug|Win32 19 | {D8EF8C1B-C4C1-4C68-A033-31AD741BE59A}.Debug|x86.Build.0 = Debug|Win32 20 | {D8EF8C1B-C4C1-4C68-A033-31AD741BE59A}.Release|x64.ActiveCfg = Release|x64 21 | {D8EF8C1B-C4C1-4C68-A033-31AD741BE59A}.Release|x64.Build.0 = Release|x64 22 | {D8EF8C1B-C4C1-4C68-A033-31AD741BE59A}.Release|x86.ActiveCfg = Release|Win32 23 | {D8EF8C1B-C4C1-4C68-A033-31AD741BE59A}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {FA14131C-5C1C-41BF-AEB9-201B8E8E0B1E} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /client/darknet_client/Makefile: -------------------------------------------------------------------------------- 1 | DEBUG = 1 2 | 3 | CPP = g++ 4 | COMMON = -DOPENCV 5 | CXXFLAGS = -g -Wall -O2 -std=c++11 -DOPENCV 6 | LDFLAGS = -lstdc++ -lpthread -lzmq -lrt -ltbb 7 | 8 | CXXFLAGS += `pkg-config --cflags json-c` 9 | CXXFLAGS += `pkg-config --cflags opencv` 10 | 11 | LDFLAGS += `pkg-config --libs json-c` 12 | LDFLAGS += `pkg-config --libs opencv` 13 | 14 | ifeq ($(DEBUG), 1) 15 | COMMON += -DDEBUG 16 | endif 17 | 18 | VPATH = ./src/ 19 | OBJDIR = ./obj/ 20 | DEPS = $(wildcard src/*.h*) 21 | 22 | EXEC1 = darknet_client 23 | EXEC1_OBJ = main.o frame.o mem_pool.o base64.o args.o util.o 24 | EXEC1_OBJS = $(addprefix $(OBJDIR), $(EXEC1_OBJ)) 25 | 26 | OBJS = $(EXEC1_OBJS) 27 | EXECS = $(EXEC1) 28 | 29 | all: $(OBJDIR) $(EXECS) 30 | 31 | $(EXEC1): $(EXEC1_OBJS) 32 | $(CPP) $(COMMON) $(CXXFLAGS) $^ -o $@ $(LDFLAGS) 33 | 34 | $(OBJDIR)%.o: %.cpp $(DEPS) 35 | $(CPP) $(COMMON) $(CXXFLAGS) -c $< -o $@ 36 | 37 | $(OBJDIR): 38 | mkdir -p $(OBJDIR) res 39 | 40 | clean: 41 | rm -rf $(OBJS) $(EXECS) 42 | -------------------------------------------------------------------------------- /client/darknet_client/darknet_client.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 15.0 39 | {D8EF8C1B-C4C1-4C68-A033-31AD741BE59A} 40 | darknetclient 41 | 10.0.17763.0 42 | darknet_client 43 | 44 | 45 | 46 | Application 47 | true 48 | v141 49 | MultiByte 50 | 51 | 52 | Application 53 | false 54 | v141 55 | true 56 | MultiByte 57 | 58 | 59 | Application 60 | true 61 | v141 62 | MultiByte 63 | 64 | 65 | Application 66 | false 67 | v141 68 | true 69 | MultiByte 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | Level3 93 | Disabled 94 | true 95 | true 96 | 97 | 98 | 99 | 100 | Level3 101 | Disabled 102 | true 103 | true 104 | C:\opencv\build\include;C:\git\vcpkg\packages\zeromq_x64-windows-static\include 105 | ZMQ_STATIC;_MBCS;%(PreprocessorDefinitions) 106 | MultiThreadedDebug 107 | 108 | 109 | opencv_world340d.lib;C:\git\vcpkg\packages\zeromq_x64-windows-static\debug\lib\libzmq-mt-sgd-4_3_3.lib;Ws2_32.lib;Iphlpapi.lib;%(AdditionalDependencies) 110 | C:\opencv\build\x64\vc15\lib; 111 | 112 | 113 | 114 | 115 | Level3 116 | MaxSpeed 117 | true 118 | true 119 | true 120 | true 121 | 122 | 123 | true 124 | true 125 | 126 | 127 | 128 | 129 | Level3 130 | MaxSpeed 131 | true 132 | true 133 | true 134 | true 135 | C:\opencv\build\include;C:\git\vcpkg\packages\zeromq_x64-windows-static\include 136 | ZMQ_STATIC;_MBCS;%(PreprocessorDefinitions) 137 | MultiThreaded 138 | 139 | 140 | true 141 | true 142 | C:\opencv\build\x64\vc15\lib\opencv_world340.lib;C:\git\vcpkg\packages\zeromq_x64-windows-static\lib\libzmq-mt-s-4_3_3.lib;Ws2_32.lib;Iphlpapi.lib;%(AdditionalDependencies) 143 | C:\opencv\build\x64\vc15\lib; 144 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /client/darknet_client/darknet_client.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 소스 파일 20 | 21 | 22 | 소스 파일 23 | 24 | 25 | 소스 파일 26 | 27 | 28 | 소스 파일 29 | 30 | 31 | 소스 파일 32 | 33 | 34 | 소스 파일 35 | 36 | 37 | 38 | 39 | 헤더 파일 40 | 41 | 42 | 헤더 파일 43 | 44 | 45 | 헤더 파일 46 | 47 | 48 | 헤더 파일 49 | 50 | 51 | 헤더 파일 52 | 53 | 54 | 헤더 파일 55 | 56 | 57 | -------------------------------------------------------------------------------- /client/darknet_client/darknet_client.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | PATH=C:\opencv\build\x64\vc15\bin;%PATH% 5 | WindowsLocalDebugger 6 | 7 | 8 | 9 | 10 | WindowsLocalDebugger 11 | 12 | -------------------------------------------------------------------------------- /client/darknet_client/src/args.cpp: -------------------------------------------------------------------------------- 1 | // https://github.com/pjreddie/template 2 | 3 | #include 4 | #include 5 | #include 6 | #include "args.hpp" 7 | 8 | void del_arg(int argc, char **argv, int index) 9 | { 10 | int i; 11 | for (i = index; i < argc - 1; ++i) argv[i] = argv[i + 1]; 12 | argv[i] = 0; 13 | } 14 | 15 | int find_arg(int argc, char* argv[], const char *arg) 16 | { 17 | int i; 18 | for (i = 0; i < argc; ++i) { 19 | if (!argv[i]) continue; 20 | if (0 == strcmp(argv[i], arg)) { 21 | del_arg(argc, argv, i); 22 | return 1; 23 | } 24 | } 25 | return 0; 26 | } 27 | 28 | int find_int_arg(int argc, char **argv, const char *arg, int def) 29 | { 30 | int i; 31 | for (i = 0; i < argc - 1; ++i) { 32 | if (!argv[i]) continue; 33 | if (0 == strcmp(argv[i], arg)) { 34 | def = atoi(argv[i + 1]); 35 | del_arg(argc, argv, i); 36 | del_arg(argc, argv, i); 37 | break; 38 | } 39 | } 40 | return def; 41 | } 42 | 43 | float find_float_arg(int argc, char **argv, const char *arg, float def) 44 | { 45 | int i; 46 | for (i = 0; i < argc - 1; ++i) { 47 | if (!argv[i]) continue; 48 | if (0 == strcmp(argv[i], arg)) { 49 | def = atof(argv[i + 1]); 50 | del_arg(argc, argv, i); 51 | del_arg(argc, argv, i); 52 | break; 53 | } 54 | } 55 | return def; 56 | } 57 | 58 | const char *find_char_arg(int argc, char **argv, const char *arg, const char *def) 59 | { 60 | int i; 61 | for (i = 0; i < argc - 1; ++i) { 62 | if (!argv[i]) continue; 63 | if (0 == strcmp(argv[i], arg)) { 64 | def = argv[i + 1]; 65 | del_arg(argc, argv, i); 66 | del_arg(argc, argv, i); 67 | break; 68 | } 69 | } 70 | return def; 71 | } 72 | -------------------------------------------------------------------------------- /client/darknet_client/src/args.hpp: -------------------------------------------------------------------------------- 1 | // https://github.com/pjreddie/template 2 | 3 | #ifndef ARGS_H 4 | #define ARGS_H 5 | 6 | int find_arg(int argc, char* argv[], const char *arg); 7 | int find_int_arg(int argc, char **argv, const char *arg, int def); 8 | float find_float_arg(int argc, char **argv, const char *arg, float def); 9 | const char *find_char_arg(int argc, char **argv, const char *arg, const char *def); 10 | 11 | #endif -------------------------------------------------------------------------------- /client/darknet_client/src/base64.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | base64.cpp and base64.h 3 | base64 encoding and decoding with C++. 4 | Version: 1.01.00 5 | Copyright (C) 2004-2017 René Nyffenegger 6 | This source code is provided 'as-is', without any express or implied 7 | warranty. In no event will the author be held liable for any damages 8 | arising from the use of this software. 9 | Permission is granted to anyone to use this software for any purpose, 10 | including commercial applications, and to alter it and redistribute it 11 | freely, subject to the following restrictions: 12 | 1. The origin of this source code must not be misrepresented; you must not 13 | claim that you wrote the original source code. If you use this source code 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 2. Altered source versions must be plainly marked as such, and must not be 17 | misrepresented as being the original source code. 18 | 3. This notice may not be removed or altered from any source distribution. 19 | René Nyffenegger rene.nyffenegger@adp-gmbh.ch 20 | */ 21 | 22 | #include "base64.hpp" 23 | #include 24 | 25 | static const std::string base64_chars = 26 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 27 | "abcdefghijklmnopqrstuvwxyz" 28 | "0123456789+/"; 29 | 30 | 31 | static inline bool is_base64(unsigned char c) { 32 | return (isalnum(c) || (c == '+') || (c == '/')); 33 | } 34 | 35 | std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { 36 | std::string ret; 37 | int i = 0; 38 | int j = 0; 39 | unsigned char char_array_3[3]; 40 | unsigned char char_array_4[4]; 41 | 42 | while (in_len--) { 43 | char_array_3[i++] = *(bytes_to_encode++); 44 | if (i == 3) { 45 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 46 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 47 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 48 | char_array_4[3] = char_array_3[2] & 0x3f; 49 | 50 | for (i = 0; (i < 4); i++) 51 | ret += base64_chars[char_array_4[i]]; 52 | i = 0; 53 | } 54 | } 55 | 56 | if (i) 57 | { 58 | for (j = i; j < 3; j++) 59 | char_array_3[j] = '\0'; 60 | 61 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 62 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 63 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 64 | 65 | for (j = 0; (j < i + 1); j++) 66 | ret += base64_chars[char_array_4[j]]; 67 | 68 | while ((i++ < 3)) 69 | ret += '='; 70 | 71 | } 72 | 73 | return ret; 74 | 75 | } 76 | 77 | std::string base64_decode(std::string const& encoded_string) { 78 | size_t in_len = encoded_string.size(); 79 | int i = 0; 80 | int j = 0; 81 | int in_ = 0; 82 | unsigned char char_array_4[4], char_array_3[3]; 83 | std::string ret; 84 | 85 | while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { 86 | char_array_4[i++] = encoded_string[in_]; in_++; 87 | if (i == 4) { 88 | for (i = 0; i < 4; i++) 89 | char_array_4[i] = base64_chars.find(char_array_4[i]) & 0xff; 90 | 91 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 92 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 93 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 94 | 95 | for (i = 0; (i < 3); i++) 96 | ret += char_array_3[i]; 97 | i = 0; 98 | } 99 | } 100 | 101 | if (i) { 102 | for (j = 0; j < i; j++) 103 | char_array_4[j] = base64_chars.find(char_array_4[j]) & 0xff; 104 | 105 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 106 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 107 | 108 | for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; 109 | } 110 | 111 | return ret; 112 | } -------------------------------------------------------------------------------- /client/darknet_client/src/base64.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // base64 encoding and decoding with C++. 3 | // Version: 1.01.00 4 | // 5 | 6 | #ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A 7 | #define BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A 8 | 9 | #include 10 | 11 | std::string base64_encode(unsigned char const*, unsigned int len); 12 | std::string base64_decode(std::string const& s); 13 | 14 | #endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */ -------------------------------------------------------------------------------- /client/darknet_client/src/frame.cpp: -------------------------------------------------------------------------------- 1 | #include // for stringstream 2 | #include // for json 3 | #include // for memcpy 4 | #include "frame.hpp" 5 | #include "base64.hpp" 6 | 7 | Frame_pool::Frame_pool() 8 | { 9 | mem_pool_msg = new CMemPool(MEM_POOL_UNIT_NUM, SEQ_BUF_LEN); 10 | mem_pool_seq = new CMemPool(MEM_POOL_UNIT_NUM, MSG_BUF_LEN); 11 | mem_pool_det = new CMemPool(MEM_POOL_UNIT_NUM, DET_BUF_LEN); 12 | }; 13 | 14 | Frame_pool::Frame_pool(int unit_num) 15 | { 16 | mem_pool_msg = new CMemPool(unit_num, SEQ_BUF_LEN); 17 | mem_pool_seq = new CMemPool(unit_num, MSG_BUF_LEN); 18 | mem_pool_det = new CMemPool(unit_num, DET_BUF_LEN); 19 | }; 20 | 21 | Frame_pool::~Frame_pool() 22 | { 23 | 24 | }; 25 | 26 | Frame Frame_pool::alloc_frame(void) { 27 | Frame frame; 28 | frame_init(frame); 29 | return frame; 30 | }; 31 | 32 | void Frame_pool::free_frame(Frame& frame) { 33 | mem_pool_seq->Free((void *)frame.seq_buf); 34 | mem_pool_msg->Free((void *)frame.msg_buf); 35 | mem_pool_det->Free((void *)frame.det_buf); 36 | } 37 | 38 | void Frame_pool::frame_init(Frame& frame) { 39 | frame.seq_len = frame.msg_len = frame.det_len = 0; 40 | frame.seq_buf = (unsigned char *)(mem_pool_seq->Alloc(SEQ_BUF_LEN, true)); 41 | frame.msg_buf = (unsigned char *)(mem_pool_msg->Alloc(MSG_BUF_LEN, true)); 42 | frame.det_buf = (unsigned char *)(mem_pool_det->Alloc(DET_BUF_LEN, true)); 43 | }; 44 | 45 | int frame_to_json(void* buf, const Frame& frame) { 46 | std::stringstream ss; 47 | ss << "{\n\"seq\":\"" << base64_encode((unsigned char *)frame.seq_buf, frame.seq_len) << "\",\n" 48 | << "\"msg\": \"" << base64_encode((unsigned char*)(frame.msg_buf), frame.msg_len) << "\",\n" 49 | << "\"det\": \"" << base64_encode((unsigned char*)(frame.det_buf), frame.det_len) 50 | << "\"\n}"; 51 | 52 | std::memcpy(buf, ss.str().c_str(), ss.str().size()); 53 | ((unsigned char*)buf)[ss.str().size()] = '\0'; 54 | return ss.str().size(); 55 | }; 56 | 57 | void json_to_frame(void* buf, Frame& frame) { 58 | json_object *raw_obj; 59 | raw_obj = json_tokener_parse((const char*)buf); 60 | 61 | json_object *seq_obj = json_object_object_get(raw_obj, "seq"); 62 | json_object *msg_obj = json_object_object_get(raw_obj, "msg"); 63 | json_object *det_obj = json_object_object_get(raw_obj, "det"); 64 | 65 | 66 | std::string seq(base64_decode(json_object_get_string(seq_obj))); 67 | std::string msg(base64_decode(json_object_get_string(msg_obj))); 68 | std::string det(base64_decode(json_object_get_string(det_obj))); 69 | 70 | frame.seq_len = seq.size(); 71 | frame.msg_len = msg.size(); 72 | frame.det_len = det.size(); 73 | 74 | 75 | std::memcpy(frame.seq_buf, seq.c_str(), frame.seq_len); 76 | ((unsigned char*)frame.seq_buf)[frame.seq_len] = '\0'; 77 | 78 | std::memcpy(frame.msg_buf, msg.c_str(), frame.msg_len); 79 | ((unsigned char*)frame.msg_buf)[frame.msg_len] = '\0'; 80 | 81 | std::memcpy(frame.det_buf, det.c_str(), frame.det_len); 82 | ((unsigned char*)frame.det_buf)[frame.det_len] = '\0'; 83 | 84 | //free 85 | json_object_put(seq_obj); 86 | json_object_put(msg_obj); 87 | json_object_put(det_obj); 88 | }; -------------------------------------------------------------------------------- /client/darknet_client/src/frame.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __FRAME_HPP 2 | #define __FRAME_HPP 3 | #include "mem_pool.hpp" 4 | 5 | struct Frame { 6 | int seq_len; 7 | int msg_len; 8 | int det_len; 9 | void *seq_buf; 10 | void *msg_buf; 11 | void *det_buf; 12 | }; 13 | 14 | const int SEQ_BUF_LEN = 100; 15 | const int MSG_BUF_LEN = 76800; 16 | const int DET_BUF_LEN = 25600; 17 | const int JSON_BUF_LEN = MSG_BUF_LEN * 2; 18 | class Frame_pool 19 | { 20 | private: 21 | CMemPool *mem_pool_msg; 22 | CMemPool *mem_pool_seq; 23 | CMemPool *mem_pool_det; 24 | const int MEM_POOL_UNIT_NUM = 5000; 25 | 26 | public: 27 | Frame_pool(); 28 | Frame_pool(int unit_num); 29 | Frame alloc_frame(void); 30 | void free_frame(Frame& frame); 31 | void frame_init(Frame& frame); 32 | ~Frame_pool(); 33 | }; 34 | 35 | int frame_to_json(void* buf, const Frame& frame); 36 | void json_to_frame(void* buf, Frame& frame); 37 | #endif -------------------------------------------------------------------------------- /client/darknet_client/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #ifdef __linux__ 10 | #include 11 | #elif _WIN32 12 | #include 13 | #endif 14 | #include 15 | #include "share_queue.hpp" 16 | #include "frame.hpp" 17 | #include "util.hpp" 18 | #include "args.hpp" 19 | 20 | #ifdef __linux__ 21 | #define FD_SETSIZE 4096 22 | using namespace tbb; 23 | #elif _WIN32 24 | using namespace concurrency; 25 | #endif 26 | using namespace cv; 27 | using namespace std; 28 | 29 | // thread 30 | void fetch_thread(void); 31 | void capture_thread(void); 32 | void recv_thread(void); 33 | void output_show_thread(void); 34 | void input_show_thread(void); 35 | 36 | volatile bool fetch_flag = false; 37 | volatile bool exit_flag = false; 38 | volatile int final_exit_flag = 0; 39 | 40 | // ZMQ 41 | void *context; 42 | void *sock_push; 43 | void *sock_sub; 44 | 45 | // pair 46 | class ComparePair 47 | { 48 | public: 49 | bool operator()(pair n1, pair n2) { 50 | return n1.first > n2.first; 51 | } 52 | }; 53 | Frame_pool *frame_pool; 54 | concurrent_priority_queue, ComparePair> recv_queue; 55 | 56 | // Queue 57 | SharedQueue cap_queue; 58 | SharedQueue fetch_queue; 59 | 60 | // opencv 61 | VideoCapture cap; 62 | VideoWriter writer; 63 | static Mat mat_show_output; 64 | static Mat mat_show_input; 65 | static Mat mat_recv; 66 | static Mat mat_cap; 67 | static Mat mat_fetch; 68 | 69 | const int cap_width = 640; 70 | const int cap_height = 480; 71 | double delay; 72 | long volatile show_frame = 1; 73 | double end_frame; 74 | 75 | // option 76 | bool cam_input_flag; 77 | bool vid_input_flag; 78 | bool dont_show_flag; 79 | bool json_output_flag; 80 | bool vid_output_flag; 81 | 82 | // output 83 | string out_json_path; 84 | string out_vid_path; 85 | 86 | ofstream out_json_file; 87 | 88 | void sig_handler(int s) 89 | { 90 | exit_flag = true; 91 | } 92 | 93 | int main(int argc, char *argv[]) 94 | { 95 | if (argc < 2) { 96 | std::cerr << "Usage: " << argv[0] << " <-addr ADDR> <-cam CAM_NUM | -vid VIDEO_PATH> [-dont_show] [-out_json] [-out_vid] \n" << std::endl; 97 | return 0; 98 | } 99 | 100 | // install signal 101 | std::signal(SIGINT, sig_handler); 102 | 103 | // option init 104 | int cam_num = find_int_arg(argc, argv, "-cam", -1); 105 | if (cam_num != -1) 106 | cam_input_flag = true; 107 | 108 | const char *vid_def_path = "./test.mp4"; 109 | const char *vid_path = find_char_arg(argc, argv, "-vid", vid_def_path); 110 | if (vid_path != vid_def_path) 111 | vid_input_flag = true; 112 | 113 | dont_show_flag = find_arg(argc, argv, "-dont_show"); 114 | json_output_flag = find_arg(argc, argv, "-out_json"); 115 | vid_output_flag = find_arg(argc, argv, "-out_vid"); 116 | 117 | // frame_pool init 118 | frame_pool = new Frame_pool(5000); 119 | 120 | // ZMQ 121 | const char *addr = find_char_arg(argc, argv, "-addr", "127.0.0.1"); 122 | context = zmq_ctx_new(); 123 | 124 | sock_push = zmq_socket(context, ZMQ_PUSH); 125 | zmq_connect(sock_push, ((std::string("tcp://") + addr) + ":5575").c_str()); 126 | 127 | sock_sub = zmq_socket(context, ZMQ_SUB); 128 | zmq_connect(sock_sub, ((std::string("tcp://") + addr) + ":5570").c_str()); 129 | 130 | zmq_setsockopt(sock_sub, ZMQ_SUBSCRIBE, "", 0); 131 | 132 | if (vid_input_flag) { 133 | // VideoCaputre video 134 | cap = VideoCapture(vid_path); 135 | out_json_path = string(vid_path, strrchr(vid_path, '.')) + "_output.json"; 136 | out_vid_path = string(vid_path, strrchr(vid_path, '.')) + "_output.mp4"; 137 | } 138 | else if (cam_input_flag) { 139 | // VideoCapture cam 140 | cap = VideoCapture(cam_num); 141 | cap.set(CAP_PROP_FPS, 20); 142 | cap.set(CAP_PROP_BUFFERSIZE, 3); 143 | fetch_flag = true; 144 | out_json_path = "./cam_output.json"; 145 | out_vid_path = "./cam_output.mp4"; 146 | } 147 | else { 148 | // error 149 | std::cerr << "Usage: " << argv[0] << " <-addr ADDR> <-cam CAM_NUM | -vid VIDEO_PATH> [-dont_show] [-out_json] [-out_vid] \n" << std::endl; 150 | return 0; 151 | } 152 | 153 | if (!cap.isOpened()) { 154 | cerr << "Erro VideoCapture...\n"; 155 | return -1; 156 | } 157 | 158 | double fps = cap.get(CAP_PROP_FPS); 159 | end_frame = cap.get(CAP_PROP_FRAME_COUNT); 160 | delay = cam_input_flag ? 1 : (1000.0 / fps); 161 | 162 | // read frame 163 | cap.read(mat_fetch); 164 | 165 | if (mat_fetch.empty()) { 166 | cerr << "Empty Mat Captured...\n"; 167 | return 0; 168 | } 169 | 170 | mat_show_output = mat_fetch.clone(); 171 | mat_show_input = mat_fetch.clone(); 172 | 173 | // output init 174 | if (json_output_flag) { 175 | out_json_file = ofstream(out_json_path); 176 | if (!out_json_file.is_open()) { 177 | cerr << "output file : " << out_json_path << " open error \n"; 178 | return 0; 179 | } 180 | out_json_file << "{\n \"det\": [\n"; 181 | } 182 | 183 | if (vid_output_flag) { 184 | writer.open(out_vid_path, VideoWriter::fourcc('M', 'P', '4', 'V'), fps, Size(cap_width, cap_height), true); 185 | if (!writer.isOpened()) { 186 | cerr << "Erro VideoWriter...\n"; 187 | return -1; 188 | } 189 | } 190 | 191 | // thread init 192 | thread thread_fetch(fetch_thread); 193 | thread_fetch.detach(); 194 | 195 | while (!fetch_flag); 196 | 197 | thread thread_show_input(output_show_thread); 198 | thread thread_show_output(input_show_thread); 199 | thread thread_recv(recv_thread); 200 | thread thread_capture(capture_thread); 201 | 202 | thread_show_input.detach(); 203 | thread_show_output.detach(); 204 | thread_recv.detach(); 205 | thread_capture.detach(); 206 | 207 | while (final_exit_flag) 208 | { 209 | // for debug 210 | cout << "R : " << recv_queue.size() << " | C : " << cap_queue.size() << " | F : " << fetch_queue.size() << " | T : " << end_frame << " : " << show_frame << endl; 211 | } 212 | 213 | cap.release(); 214 | 215 | if (json_output_flag) { 216 | out_json_file << "\n ]\n}"; 217 | out_json_file.close(); 218 | } 219 | 220 | if (vid_output_flag) { 221 | writer.release(); 222 | } 223 | 224 | delete frame_pool; 225 | zmq_close(sock_sub); 226 | zmq_close(sock_push); 227 | zmq_ctx_destroy(context); 228 | 229 | return 0; 230 | } 231 | 232 | #define FETCH_THRESH 100 233 | #define FETCH_WAIT_THRESH 30 234 | #define FETCH_STATE 0 235 | #define FETCH_WAIT 1 236 | void fetch_thread(void) { 237 | volatile int fetch_state = FETCH_STATE; 238 | final_exit_flag += 1; 239 | while (!exit_flag) { 240 | 241 | switch (fetch_state) { 242 | case FETCH_STATE: 243 | if (cap.grab()) { 244 | 245 | cap.retrieve(mat_fetch); 246 | 247 | // push fetch queue 248 | fetch_queue.push_back(mat_fetch.clone()); 249 | 250 | // if cam dont wait 251 | if (!cam_input_flag && (fetch_queue.size() > FETCH_THRESH)) { 252 | fetch_state = FETCH_WAIT; 253 | } 254 | } 255 | // if fetch end 256 | else { 257 | final_exit_flag -= 1; 258 | return; 259 | } 260 | break; 261 | case FETCH_WAIT: 262 | fetch_flag = true; 263 | if (fetch_queue.size() < FETCH_WAIT_THRESH) { 264 | fetch_state = FETCH_STATE; 265 | } 266 | break; 267 | } 268 | } 269 | final_exit_flag -= 1; 270 | } 271 | 272 | void capture_thread(void) { 273 | static vector param = { IMWRITE_JPEG_QUALITY, 50 }; 274 | static vector encode_buf(JSON_BUF_LEN); 275 | 276 | volatile int frame_seq_num = 1; 277 | string frame_seq; 278 | 279 | // for json 280 | unsigned char json_buf[JSON_BUF_LEN]; 281 | int send_json_len; 282 | 283 | Frame frame = frame_pool->alloc_frame(); 284 | 285 | final_exit_flag += 1; 286 | while (!exit_flag) { 287 | if (fetch_queue.size() < 1) 288 | continue; 289 | 290 | // get input mat 291 | mat_cap = fetch_queue.front().clone(); 292 | fetch_queue.pop_front(); 293 | 294 | if (mat_cap.empty()) { 295 | cerr << "Empty Mat Captured...\n"; 296 | continue; 297 | } 298 | 299 | // resize 300 | resize(mat_cap, mat_cap, Size(cap_width, cap_height)); 301 | 302 | // push to cap queue (for display input) 303 | cap_queue.push_back(mat_cap.clone()); 304 | 305 | // mat to jpg 306 | imencode(".jpg", mat_cap, encode_buf, param); 307 | 308 | // jpg to json (seq + msg) 309 | frame_seq = to_string(frame_seq_num); 310 | frame.seq_len = frame_seq.size(); 311 | memcpy(frame.seq_buf, frame_seq.c_str(), frame.seq_len); 312 | 313 | frame.msg_len = encode_buf.size(); 314 | memcpy(frame.msg_buf, &encode_buf[0], frame.msg_len); 315 | 316 | send_json_len = frame_to_json(json_buf, frame); 317 | 318 | // send json to server 319 | zmq_send(sock_push, json_buf, send_json_len, 0); 320 | 321 | frame_seq_num++; 322 | } 323 | frame_pool->free_frame(frame); 324 | final_exit_flag -= 1; 325 | } 326 | 327 | void recv_thread(void) { 328 | int recv_json_len; 329 | int frame_seq_num = 1; 330 | Frame frame; 331 | unsigned char json_buf[JSON_BUF_LEN]; 332 | 333 | final_exit_flag += 1; 334 | while (!exit_flag) { 335 | recv_json_len = zmq_recv(sock_sub, json_buf, JSON_BUF_LEN, ZMQ_NOBLOCK); 336 | 337 | if (recv_json_len > 0) { 338 | frame = frame_pool->alloc_frame(); 339 | json_buf[recv_json_len] = '\0'; 340 | json_to_frame(json_buf, frame); 341 | 342 | frame_seq_num = str_to_int((const char *)frame.seq_buf, frame.seq_len); 343 | 344 | // push to recv_queue (for display output) 345 | pair p = make_pair(frame_seq_num, frame); 346 | recv_queue.push(p); 347 | } 348 | } 349 | final_exit_flag -= 1; 350 | } 351 | 352 | #define DONT_SHOW 0 353 | #define SHOW_START 1 354 | #define DONT_SHOW_THRESH 2 // for buffering 355 | #define SHOW_START_THRESH 1 // for buffering 356 | 357 | int volatile show_state = DONT_SHOW; 358 | void input_show_thread(void) { 359 | 360 | if (!dont_show_flag) { 361 | cvNamedWindow("INPUT"); 362 | moveWindow("INPUT", 30, 130); 363 | cv::imshow("INPUT", mat_show_input); 364 | } 365 | 366 | final_exit_flag += 1; 367 | while (!exit_flag) { 368 | switch (show_state) { 369 | case DONT_SHOW: 370 | break; 371 | case SHOW_START: 372 | if (cap_queue.size() >= DONT_SHOW_THRESH) { 373 | mat_show_input = cap_queue.front().clone(); 374 | cap_queue.pop_front(); 375 | } 376 | break; 377 | } 378 | 379 | if (!dont_show_flag) { 380 | 381 | // draw text (INPUT) Left Upper corner 382 | putText(mat_show_input, "INPUT", Point(10, 25), 383 | FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255), 2); 384 | 385 | cv::imshow("INPUT", mat_show_input); 386 | 387 | // wait key for exit 388 | if (waitKey(delay) >= 0) 389 | exit_flag = true; 390 | } 391 | } 392 | final_exit_flag -= 1; 393 | } 394 | 395 | void output_show_thread(void) { 396 | Frame frame; 397 | 398 | if (!dont_show_flag) { 399 | cvNamedWindow("OUTPUT"); 400 | moveWindow("OUTPUT", 670, 130); 401 | cv::imshow("OUTPUT", mat_show_output); 402 | } 403 | 404 | final_exit_flag += 1; 405 | while (!exit_flag) { 406 | 407 | switch (show_state) { 408 | case DONT_SHOW: 409 | if (recv_queue.size() >= SHOW_START_THRESH) { 410 | show_state = SHOW_START; 411 | } 412 | break; 413 | case SHOW_START: 414 | if (recv_queue.size() >= DONT_SHOW_THRESH || (end_frame - show_frame) == 1) { 415 | pair p; 416 | // try pop success 417 | while (1) { 418 | if (recv_queue.try_pop(p)) { 419 | // if right sequence 420 | if (p.first == show_frame) { 421 | 422 | frame = ((Frame)p.second); 423 | vector decode_buf((unsigned char*)(frame.msg_buf), (unsigned char*)(frame.msg_buf) + frame.msg_len); 424 | 425 | // jpg to mat 426 | mat_show_output = imdecode(decode_buf, IMREAD_COLOR); 427 | 428 | // resize 429 | resize(mat_show_output, mat_recv, Size(cap_width, cap_height)); 430 | 431 | // wirte out_json 432 | if (json_output_flag) { 433 | if (show_frame != 1) 434 | out_json_file << ",\n"; 435 | out_json_file.write((const char*)frame.det_buf, frame.det_len); 436 | } 437 | 438 | // write out_vid 439 | if (vid_output_flag) 440 | writer.write(mat_show_output); 441 | 442 | // free frame 443 | frame_pool->free_frame(frame); 444 | show_frame++; 445 | } 446 | // wrong sequence 447 | else { 448 | recv_queue.push(p); 449 | } 450 | break; 451 | } 452 | } 453 | } 454 | else { 455 | show_state = DONT_SHOW; 456 | } 457 | break; 458 | } 459 | 460 | if (show_frame == end_frame) 461 | exit_flag = true; 462 | 463 | if (!dont_show_flag) { 464 | // draw text (OUTPUT) Left Upper corner 465 | putText(mat_show_output, "OUTPUT", Point(10, 25), 466 | FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0), 2); 467 | 468 | cv::imshow("OUTPUT", mat_show_output); 469 | 470 | // wait key for exit 471 | if (waitKey(delay) >= 0) 472 | exit_flag = true; 473 | } 474 | } 475 | final_exit_flag -= 1; 476 | } 477 | -------------------------------------------------------------------------------- /client/darknet_client/src/mem_pool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "mem_pool.hpp" 4 | /*========================================================== 5 | CMemPool: 6 | Constructor of this class. It allocate memory block from system and create 7 | a static double linked list to manage all memory unit. 8 | 9 | Parameters: 10 | [in]ulUnitNum 11 | The number of unit which is a part of memory block. 12 | 13 | [in]ulUnitSize 14 | The size of unit. 15 | //========================================================= 16 | */ 17 | CMemPool::CMemPool(unsigned long ulUnitNum, unsigned long ulUnitSize) : 18 | m_pMemBlock(NULL), m_pAllocatedMemBlock(NULL), m_pFreeMemBlock(NULL), 19 | m_ulBlockSize(ulUnitNum * (ulUnitSize + sizeof(struct _Unit))), 20 | m_ulUnitSize(ulUnitSize) 21 | { 22 | m_pMemBlock = malloc(m_ulBlockSize); //Allocate a memory block. 23 | 24 | if (NULL != m_pMemBlock) 25 | { 26 | for (unsigned long i = 0; i < ulUnitNum; i++) //Link all mem unit . Create linked list. 27 | { 28 | struct _Unit *pCurUnit = (struct _Unit *)((char *)m_pMemBlock + i * (ulUnitSize + sizeof(struct _Unit))); 29 | 30 | pCurUnit->pPrev = NULL; 31 | pCurUnit->pNext = m_pFreeMemBlock; //Insert the new unit at head. 32 | 33 | if (NULL != m_pFreeMemBlock) 34 | { 35 | m_pFreeMemBlock->pPrev = pCurUnit; 36 | } 37 | m_pFreeMemBlock = pCurUnit; 38 | } 39 | } 40 | } 41 | 42 | /*=============================================================== 43 | ~CMemPool(): 44 | Destructor of this class. Its task is to free memory block. 45 | //=============================================================== 46 | */ 47 | CMemPool::~CMemPool() 48 | { 49 | free(m_pMemBlock); 50 | } 51 | 52 | /*================================================================ 53 | Alloc: 54 | To allocate a memory unit. If memory pool can`t provide proper memory unit, 55 | It will call system function. 56 | 57 | Parameters: 58 | [in]ulSize 59 | Memory unit size. 60 | 61 | [in]bUseMemPool 62 | Whether use memory pool. 63 | 64 | Return Values: 65 | Return a pointer to a memory unit. 66 | //================================================================= 67 | */ 68 | void* CMemPool::Alloc(unsigned long ulSize, bool bUseMemPool) 69 | { 70 | if (ulSize > m_ulUnitSize || false == bUseMemPool || 71 | NULL == m_pMemBlock || NULL == m_pFreeMemBlock) 72 | { 73 | return malloc(ulSize); 74 | } 75 | 76 | //Now FreeList isn`t empty 77 | struct _Unit *pCurUnit = m_pFreeMemBlock; 78 | m_pFreeMemBlock = pCurUnit->pNext; //Get a unit from free linkedlist. 79 | if (NULL != m_pFreeMemBlock) 80 | { 81 | m_pFreeMemBlock->pPrev = NULL; 82 | } 83 | 84 | pCurUnit->pNext = m_pAllocatedMemBlock; 85 | 86 | if (NULL != m_pAllocatedMemBlock) 87 | { 88 | m_pAllocatedMemBlock->pPrev = pCurUnit; 89 | } 90 | m_pAllocatedMemBlock = pCurUnit; 91 | 92 | return (void *)((char *)pCurUnit + sizeof(struct _Unit)); 93 | } 94 | 95 | /*================================================================ 96 | Free: 97 | To free a memory unit. If the pointer of parameter point to a memory unit, 98 | then insert it to "Free linked list". Otherwise, call system function "free". 99 | 100 | Parameters: 101 | [in]p 102 | It point to a memory unit and prepare to free it. 103 | 104 | Return Values: 105 | none 106 | //================================================================ 107 | */ 108 | void CMemPool::Free(void* p) 109 | { 110 | if (m_pMemBlock < p && p < (void *)((char *)m_pMemBlock + m_ulBlockSize)) 111 | { 112 | struct _Unit *pCurUnit = (struct _Unit *)((char *)p - sizeof(struct _Unit)); 113 | 114 | m_pAllocatedMemBlock = pCurUnit->pNext; 115 | if (NULL != m_pAllocatedMemBlock) 116 | { 117 | m_pAllocatedMemBlock->pPrev = NULL; 118 | } 119 | 120 | pCurUnit->pNext = m_pFreeMemBlock; 121 | if (NULL != m_pFreeMemBlock) 122 | { 123 | m_pFreeMemBlock->pPrev = pCurUnit; 124 | } 125 | 126 | m_pFreeMemBlock = pCurUnit; 127 | } 128 | else 129 | { 130 | free(p); 131 | } 132 | } -------------------------------------------------------------------------------- /client/darknet_client/src/mem_pool.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __MEMPOOL_H__ 2 | #define __MEMPOOL_H__ 3 | // https://www.codeproject.com/Articles/27487/Why-to-use-memory-pool-and-how-to-implement-it 4 | class CMemPool 5 | { 6 | private: 7 | //The purpose of the structure`s definition is that we can operate linkedlist conveniently 8 | struct _Unit //The type of the node of linkedlist. 9 | { 10 | struct _Unit *pPrev, *pNext; 11 | }; 12 | 13 | void* m_pMemBlock; //The address of memory pool. 14 | 15 | //Manage all unit with two linkedlist. 16 | struct _Unit* m_pAllocatedMemBlock; //Head pointer to Allocated linkedlist. 17 | struct _Unit* m_pFreeMemBlock; //Head pointer to Free linkedlist. 18 | 19 | unsigned long m_ulUnitSize; //Memory unit size. There are much unit in memory pool. 20 | unsigned long m_ulBlockSize;//Memory pool size. Memory pool is make of memory unit. 21 | 22 | public: 23 | CMemPool(unsigned long lUnitNum = 50, unsigned long lUnitSize = 1024); 24 | ~CMemPool(); 25 | 26 | void* Alloc(unsigned long ulSize, bool bUseMemPool = true); //Allocate memory unit 27 | void Free(void* p); //Free memory unit 28 | }; 29 | 30 | #endif //__MEMPOOL_H__ 31 | -------------------------------------------------------------------------------- /client/darknet_client/src/share_queue.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __SHARE_QUEUE_HPP 2 | #define __SHARE_QUEUE_HPP 3 | 4 | // https://stackoverflow.com/questions/36762248/why-is-stdqueue-not-thread-safe 5 | #include 6 | #include 7 | #include 8 | 9 | template 10 | class SharedQueue 11 | { 12 | public: 13 | SharedQueue(); 14 | ~SharedQueue(); 15 | 16 | T& front(); 17 | void pop_front(); 18 | 19 | void push_back(const T& item); 20 | void push_back(T&& item); 21 | 22 | int size(); 23 | bool empty(); 24 | 25 | private: 26 | std::deque queue_; 27 | std::mutex mutex_; 28 | std::condition_variable cond_; 29 | }; 30 | 31 | template 32 | SharedQueue::SharedQueue() {} 33 | 34 | template 35 | SharedQueue::~SharedQueue() {} 36 | 37 | template 38 | T& SharedQueue::front() 39 | { 40 | std::unique_lock mlock(mutex_); 41 | while (queue_.empty()) 42 | { 43 | cond_.wait(mlock); 44 | } 45 | return queue_.front(); 46 | } 47 | 48 | template 49 | void SharedQueue::pop_front() 50 | { 51 | std::unique_lock mlock(mutex_); 52 | while (queue_.empty()) 53 | { 54 | cond_.wait(mlock); 55 | } 56 | queue_.pop_front(); 57 | } 58 | 59 | template 60 | void SharedQueue::push_back(const T& item) 61 | { 62 | std::unique_lock mlock(mutex_); 63 | queue_.push_back(item); 64 | mlock.unlock(); // unlock before notificiation to minimize mutex con 65 | cond_.notify_one(); // notify one waiting thread 66 | 67 | } 68 | 69 | template 70 | void SharedQueue::push_back(T&& item) 71 | { 72 | std::unique_lock mlock(mutex_); 73 | queue_.push_back(std::move(item)); 74 | mlock.unlock(); // unlock before notificiation to minimize mutex con 75 | cond_.notify_one(); // notify one waiting thread 76 | 77 | } 78 | 79 | template 80 | int SharedQueue::size() 81 | { 82 | std::unique_lock mlock(mutex_); 83 | int size = queue_.size(); 84 | mlock.unlock(); 85 | return size; 86 | } 87 | #endif -------------------------------------------------------------------------------- /client/darknet_client/src/util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.hpp" 2 | 3 | // utility 4 | int str_to_int(const char* str, int len) 5 | { 6 | int i; 7 | int ret = 0; 8 | for (i = 0; i < len; ++i) 9 | { 10 | ret = ret * 10 + (str[i] - '0'); 11 | } 12 | return ret; 13 | } -------------------------------------------------------------------------------- /client/darknet_client/src/util.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __UTIL_HPP 2 | #define __UTIL_HPP 3 | 4 | int str_to_int(const char* str, int len); 5 | 6 | #endif -------------------------------------------------------------------------------- /server/Makefile: -------------------------------------------------------------------------------- 1 | DEBUG = 1 2 | 3 | CPP = g++ 4 | COMMON = -DOPENCV 5 | CXXFLAGS = -g -Wall -O2 -std=c++11 -DOPENCV 6 | LDFLAGS = -lstdc++ -lpthread -lzmq -lrt -ltbb -ldarknet -lboost_serialization 7 | 8 | CXXFLAGS += `pkg-config --cflags json-c` 9 | CXXFLAGS += `pkg-config --cflags opencv` 10 | 11 | LDFLAGS += `pkg-config --libs json-c` 12 | LDFLAGS += `pkg-config --libs opencv` 13 | 14 | ifeq ($(DEBUG), 1) 15 | COMMON += -DDEBUG 16 | endif 17 | 18 | VPATH = ./src/ 19 | OBJDIR = ./obj/ 20 | DEPS = $(wildcard src/*.h*) 21 | 22 | EXEC1 = ventilator 23 | EXEC1_OBJ = ventilator.o frame.o mem_pool.o base64.o 24 | EXEC1_OBJS = $(addprefix $(OBJDIR), $(EXEC1_OBJ)) 25 | 26 | EXEC2 = worker 27 | EXEC2_OBJ = worker.o people.o pose_detector.o frame.o mem_pool.o base64.o args.o 28 | EXEC2_OBJS = $(addprefix $(OBJDIR), $(EXEC2_OBJ)) 29 | 30 | EXEC3 = sink 31 | EXEC3_OBJ = sink.o people.o Tracker.o Hungarian.o KalmanTracker.o frame.o mem_pool.o base64.o 32 | EXEC3_OBJS = $(addprefix $(OBJDIR), $(EXEC3_OBJ)) 33 | 34 | OBJS = $(EXEC1_OBJS) $(EXEC2_OBJS) $(EXEC3_OBJS) 35 | EXECS = $(EXEC1) $(EXEC2) $(EXEC3) 36 | INPROCS = processed unprocessed action 37 | 38 | all: $(OBJDIR) $(EXECS) 39 | 40 | $(EXEC1): $(EXEC1_OBJS) 41 | $(CPP) $(COMMON) $(CXXFLAGS) $^ -o $@ $(LDFLAGS) 42 | 43 | $(EXEC2): $(EXEC2_OBJS) 44 | $(CPP) $(COMMON) $(CXXFLAGS) $^ -o $@ $(LDFLAGS) 45 | 46 | $(EXEC3): $(EXEC3_OBJS) 47 | $(CPP) $(COMMON) $(CXXFLAGS) $^ -o $@ $(LDFLAGS) 48 | 49 | $(OBJDIR)%.o: %.cpp $(DEPS) 50 | $(CPP) $(COMMON) $(CXXFLAGS) -c $< -o $@ 51 | 52 | $(OBJDIR): 53 | mkdir -p $(OBJDIR) cfg weights names train 54 | 55 | clean: 56 | rm -rf $(OBJS) $(EXECS) $(INPROCS) 57 | -------------------------------------------------------------------------------- /server/action.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import numpy as np 3 | import zmq 4 | import io 5 | import time 6 | from tensorflow import keras 7 | 8 | from tensorflow.keras.backend import set_session 9 | config = tf.ConfigProto() 10 | config.gpu_options.allow_growth = True 11 | sess = tf.Session(config=config) 12 | set_session(sess) 13 | sess.run(tf.global_variables_initializer()) 14 | 15 | ''' 16 | ## TF 2.0 17 | from tensorflow.compat.v1.keras.backend import set_session 18 | tf.compat.v1.disable_eager_execution() 19 | config = tf.compat.v1.ConfigProto() 20 | config.gpu_options.allow_growth = True 21 | sess = tf.compat.v1.Session(config=config) 22 | set_session(sess) 23 | sess.run(tf.compat.v1.global_variables_initializer()) 24 | ''' 25 | 26 | model = keras.models.load_model("weights/action.h5") 27 | 28 | n_input = 24 # num input parameters per timestep 29 | n_steps = 32 30 | n_hidden = 34 # Hidden layer num of features 31 | n_classes = 4 32 | batch_size = 1024 33 | 34 | def load_X(msg): 35 | buf = io.StringIO(msg) 36 | X_ = np.array( 37 | [elem for elem in [ 38 | row.split(',') for row in buf 39 | ]], 40 | dtype=np.float32 41 | ) 42 | blocks = int(len(X_) / 32) 43 | X_ = np.array(np.split(X_,blocks)) 44 | return X_ 45 | 46 | 47 | # load 48 | input_ = np.zeros((batch_size, n_steps, n_input), dtype=np.float32) 49 | print("model loaded ...") 50 | 51 | context = zmq.Context() 52 | socket = context.socket(zmq.REP) 53 | socket.bind("ipc://action") 54 | 55 | while True: 56 | msg = socket.recv() 57 | msg = msg.decode("utf-8") 58 | recv_ = load_X(msg) 59 | 60 | for i in range(len(recv_)): 61 | input_[i] = recv_[i] 62 | startTime = time.time() 63 | pred = model.predict_classes(input_, batch_size = batch_size) 64 | 65 | endTime = time.time() - startTime 66 | print("time : ", endTime) 67 | pred_str = "" 68 | 69 | for i in range(len(recv_)): 70 | pred_str += str(pred[i]) 71 | print("result : ", pred_str) 72 | socket.send_string(pred_str) 73 | -------------------------------------------------------------------------------- /server/action_train.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import numpy as np 3 | from tensorflow import keras 4 | from tensorflow.keras import layers 5 | config = tf.ConfigProto() 6 | config.gpu_options.allow_growth = True 7 | sess = tf.Session(config=config) 8 | 9 | # Set parameter 10 | n_input = 24 # num input parameters per timestep 11 | n_steps = 32 12 | n_hidden = 34 # Hidden layer num of features 13 | n_classes = 4 14 | batch_size = 1024 15 | lambda_loss_amount = 0.0015 16 | learning_rate = 0.0025 17 | decay_rate = 0.02 18 | training_epochs = 300 19 | 20 | ### 21 | def load_X(X_path): 22 | file = open(X_path, 'r') 23 | X_ = np.array( 24 | [elem for elem in [ 25 | row.split(',') for row in file 26 | ]], 27 | dtype=np.float32 28 | ) 29 | file.close() 30 | blocks = int(len(X_) / n_steps) 31 | X_ = np.array(np.split(X_,blocks)) 32 | return X_ 33 | 34 | def load_y(y_path): 35 | file = open(y_path, 'r') 36 | y_ = np.array( 37 | [elem for elem in [ 38 | row.replace(' ', ' ').strip().split(' ') for row in file 39 | ]], 40 | dtype=np.int32 41 | ) 42 | file.close() 43 | # for 0-based indexing 44 | return y_ - 1 45 | 46 | 47 | DATASET_PATH = "train/" 48 | 49 | X_train_path = DATASET_PATH + "pose36.txt" 50 | X_test_path = DATASET_PATH + "pose36_test.txt" 51 | 52 | y_train_path = DATASET_PATH + "pose36_c.txt" 53 | y_test_path = DATASET_PATH + "pose36_test_c.txt" 54 | 55 | 56 | X_train = load_X(X_train_path) 57 | X_test = load_X(X_test_path) 58 | #print X_test 59 | 60 | y_train = load_y(y_train_path) 61 | y_test = load_y(y_test_path) 62 | ### 63 | 64 | model = tf.keras.Sequential([ 65 | # relu activation 66 | layers.Dense(n_hidden, activation='relu', 67 | kernel_initializer='random_normal', 68 | bias_initializer='random_normal', 69 | batch_input_shape=(batch_size, n_steps, n_input) 70 | ), 71 | 72 | # cuDNN 73 | layers.CuDNNLSTM(n_hidden, return_sequences=True, unit_forget_bias=1.0), 74 | layers.CuDNNLSTM(n_hidden, unit_forget_bias=1.0), 75 | 76 | # layers.LSTM(n_hidden, return_sequences=True, unit_forget_bias=1.0), 77 | # layers.LSTM(n_hidden, unit_forget_bias=1.0), 78 | 79 | layers.Dense(n_classes, kernel_initializer='random_normal', 80 | bias_initializer='random_normal', 81 | kernel_regularizer=tf.keras.regularizers.l2(lambda_loss_amount), 82 | bias_regularizer=tf.keras.regularizers.l2(lambda_loss_amount), 83 | activation='softmax' 84 | ) 85 | ]) 86 | 87 | model.compile( 88 | optimizer=tf.keras.optimizers.Adam(lr=learning_rate, decay=decay_rate), 89 | metrics=['accuracy'], 90 | loss='categorical_crossentropy' 91 | ) 92 | 93 | y_train_one_hot = keras.utils.to_categorical(y_train, 4) 94 | y_test_one_hot = keras.utils.to_categorical(y_test, 4) 95 | 96 | train_size = X_train.shape[0] - X_train.shape[0] % batch_size 97 | test_size = X_test.shape[0] - X_test.shape[0] % batch_size 98 | 99 | history = model.fit( 100 | X_train[:train_size,:,:], 101 | y_train_one_hot[:train_size,:], 102 | epochs=training_epochs, 103 | batch_size=batch_size, 104 | validation_data=(X_test[:test_size,:,:], y_test_one_hot[:test_size,:]) 105 | ) 106 | 107 | from tensorflow.keras.models import load_model 108 | model.save('weights/action.h5') 109 | -------------------------------------------------------------------------------- /server/cfg/openpose.cfg: -------------------------------------------------------------------------------- 1 | [net] 2 | width=200 3 | height=200 4 | channels=3 5 | 6 | [convolutional] 7 | batch_normalize=0 8 | filters=64 9 | size=3 10 | stride=1 11 | pad=1 12 | activation=relu 13 | 14 | [convolutional] 15 | batch_normalize=0 16 | filters=64 17 | size=3 18 | stride=1 19 | pad=1 20 | activation=relu 21 | 22 | [maxpool] 23 | size=2 24 | stride=2 25 | 26 | [convolutional] 27 | batch_normalize=0 28 | filters=128 29 | size=3 30 | stride=1 31 | pad=1 32 | activation=relu 33 | 34 | [convolutional] 35 | batch_normalize=0 36 | filters=128 37 | size=3 38 | stride=1 39 | pad=1 40 | activation=relu 41 | 42 | [maxpool] 43 | size=2 44 | stride=2 45 | 46 | [convolutional] 47 | batch_normalize=0 48 | filters=256 49 | size=3 50 | stride=1 51 | pad=1 52 | activation=relu 53 | 54 | [convolutional] 55 | batch_normalize=0 56 | filters=256 57 | size=3 58 | stride=1 59 | pad=1 60 | activation=relu 61 | 62 | [convolutional] 63 | batch_normalize=0 64 | filters=256 65 | size=3 66 | stride=1 67 | pad=1 68 | activation=relu 69 | 70 | [convolutional] 71 | batch_normalize=0 72 | filters=256 73 | size=3 74 | stride=1 75 | pad=1 76 | activation=relu 77 | 78 | [maxpool] 79 | size=2 80 | stride=2 81 | 82 | [convolutional] 83 | batch_normalize=0 84 | filters=512 85 | size=3 86 | stride=1 87 | pad=1 88 | activation=relu 89 | 90 | [convolutional] 91 | batch_normalize=0 92 | filters=512 93 | size=3 94 | stride=1 95 | pad=1 96 | activation=relu 97 | 98 | [convolutional] 99 | batch_normalize=0 100 | filters=256 101 | size=3 102 | stride=1 103 | pad=1 104 | activation=relu 105 | 106 | [convolutional] 107 | batch_normalize=0 108 | filters=128 109 | size=3 110 | stride=1 111 | pad=1 112 | activation=relu 113 | 114 | ####### 115 | 116 | [convolutional] 117 | batch_normalize=0 118 | filters=128 119 | size=3 120 | stride=1 121 | pad=1 122 | activation=relu 123 | 124 | [convolutional] 125 | batch_normalize=0 126 | filters=128 127 | size=3 128 | stride=1 129 | pad=1 130 | activation=relu 131 | 132 | [convolutional] 133 | batch_normalize=0 134 | filters=128 135 | size=3 136 | stride=1 137 | pad=1 138 | activation=relu 139 | 140 | [convolutional] 141 | batch_normalize=0 142 | filters=512 143 | size=1 144 | stride=1 145 | pad=0 146 | activation=relu 147 | 148 | [convolutional] 149 | batch_normalize=0 150 | filters=38 151 | size=1 152 | stride=1 153 | pad=0 154 | activation=linear 155 | 156 | [route] 157 | layers=-6 158 | 159 | [convolutional] 160 | batch_normalize=0 161 | filters=128 162 | size=3 163 | stride=1 164 | pad=1 165 | activation=relu 166 | 167 | [convolutional] 168 | batch_normalize=0 169 | filters=128 170 | size=3 171 | stride=1 172 | pad=1 173 | activation=relu 174 | 175 | [convolutional] 176 | batch_normalize=0 177 | filters=128 178 | size=3 179 | stride=1 180 | pad=1 181 | activation=relu 182 | 183 | [convolutional] 184 | batch_normalize=0 185 | filters=512 186 | size=1 187 | stride=1 188 | pad=0 189 | activation=relu 190 | 191 | [convolutional] 192 | batch_normalize=0 193 | filters=19 194 | size=1 195 | stride=1 196 | pad=0 197 | activation=linear 198 | 199 | [route] 200 | layers=-7,-1,-12 201 | 202 | ###concat_stage2### 203 | 204 | [convolutional] 205 | batch_normalize=0 206 | filters=128 207 | size=7 208 | stride=1 209 | pad=3 210 | activation=relu 211 | 212 | [convolutional] 213 | batch_normalize=0 214 | filters=128 215 | size=7 216 | stride=1 217 | pad=3 218 | activation=relu 219 | 220 | [convolutional] 221 | batch_normalize=0 222 | filters=128 223 | size=7 224 | stride=1 225 | pad=3 226 | activation=relu 227 | 228 | [convolutional] 229 | batch_normalize=0 230 | filters=128 231 | size=7 232 | stride=1 233 | pad=3 234 | activation=relu 235 | 236 | [convolutional] 237 | batch_normalize=0 238 | filters=128 239 | size=7 240 | stride=1 241 | pad=3 242 | activation=relu 243 | 244 | [convolutional] 245 | batch_normalize=0 246 | filters=128 247 | size=1 248 | stride=1 249 | pad=0 250 | activation=relu 251 | 252 | [convolutional] 253 | batch_normalize=0 254 | filters=38 255 | size=1 256 | stride=1 257 | pad=0 258 | activation=linear 259 | 260 | [route] 261 | layers=-8 262 | 263 | [convolutional] 264 | batch_normalize=0 265 | filters=128 266 | size=7 267 | stride=1 268 | pad=3 269 | activation=relu 270 | 271 | [convolutional] 272 | batch_normalize=0 273 | filters=128 274 | size=7 275 | stride=1 276 | pad=3 277 | activation=relu 278 | 279 | [convolutional] 280 | batch_normalize=0 281 | filters=128 282 | size=7 283 | stride=1 284 | pad=3 285 | activation=relu 286 | 287 | [convolutional] 288 | batch_normalize=0 289 | filters=128 290 | size=7 291 | stride=1 292 | pad=3 293 | activation=relu 294 | 295 | [convolutional] 296 | batch_normalize=0 297 | filters=128 298 | size=7 299 | stride=1 300 | pad=3 301 | activation=relu 302 | 303 | [convolutional] 304 | batch_normalize=0 305 | filters=128 306 | size=1 307 | stride=1 308 | pad=0 309 | activation=relu 310 | 311 | [convolutional] 312 | batch_normalize=0 313 | filters=19 314 | size=1 315 | stride=1 316 | pad=0 317 | activation=linear 318 | 319 | [route] 320 | layers=-9,-1,-28 321 | 322 | ###concat_stage3### 323 | 324 | [convolutional] 325 | batch_normalize=0 326 | filters=128 327 | size=7 328 | stride=1 329 | pad=3 330 | activation=relu 331 | 332 | [convolutional] 333 | batch_normalize=0 334 | filters=128 335 | size=7 336 | stride=1 337 | pad=3 338 | activation=relu 339 | 340 | [convolutional] 341 | batch_normalize=0 342 | filters=128 343 | size=7 344 | stride=1 345 | pad=3 346 | activation=relu 347 | 348 | [convolutional] 349 | batch_normalize=0 350 | filters=128 351 | size=7 352 | stride=1 353 | pad=3 354 | activation=relu 355 | 356 | [convolutional] 357 | batch_normalize=0 358 | filters=128 359 | size=7 360 | stride=1 361 | pad=3 362 | activation=relu 363 | 364 | [convolutional] 365 | batch_normalize=0 366 | filters=128 367 | size=1 368 | stride=1 369 | pad=0 370 | activation=relu 371 | 372 | [convolutional] 373 | batch_normalize=0 374 | filters=38 375 | size=1 376 | stride=1 377 | pad=0 378 | activation=linear 379 | 380 | [route] 381 | layers=-8 382 | 383 | [convolutional] 384 | batch_normalize=0 385 | filters=128 386 | size=7 387 | stride=1 388 | pad=3 389 | activation=relu 390 | 391 | [convolutional] 392 | batch_normalize=0 393 | filters=128 394 | size=7 395 | stride=1 396 | pad=3 397 | activation=relu 398 | 399 | [convolutional] 400 | batch_normalize=0 401 | filters=128 402 | size=7 403 | stride=1 404 | pad=3 405 | activation=relu 406 | 407 | [convolutional] 408 | batch_normalize=0 409 | filters=128 410 | size=7 411 | stride=1 412 | pad=3 413 | activation=relu 414 | 415 | [convolutional] 416 | batch_normalize=0 417 | filters=128 418 | size=7 419 | stride=1 420 | pad=3 421 | activation=relu 422 | 423 | [convolutional] 424 | batch_normalize=0 425 | filters=128 426 | size=1 427 | stride=1 428 | pad=0 429 | activation=relu 430 | 431 | [convolutional] 432 | batch_normalize=0 433 | filters=19 434 | size=1 435 | stride=1 436 | pad=0 437 | activation=linear 438 | 439 | [route] 440 | layers=-9,-1,-44 441 | 442 | ###concat_stage4### 443 | 444 | [convolutional] 445 | batch_normalize=0 446 | filters=128 447 | size=7 448 | stride=1 449 | pad=3 450 | activation=relu 451 | 452 | [convolutional] 453 | batch_normalize=0 454 | filters=128 455 | size=7 456 | stride=1 457 | pad=3 458 | activation=relu 459 | 460 | [convolutional] 461 | batch_normalize=0 462 | filters=128 463 | size=7 464 | stride=1 465 | pad=3 466 | activation=relu 467 | 468 | [convolutional] 469 | batch_normalize=0 470 | filters=128 471 | size=7 472 | stride=1 473 | pad=3 474 | activation=relu 475 | 476 | [convolutional] 477 | batch_normalize=0 478 | filters=128 479 | size=7 480 | stride=1 481 | pad=3 482 | activation=relu 483 | 484 | [convolutional] 485 | batch_normalize=0 486 | filters=128 487 | size=1 488 | stride=1 489 | pad=0 490 | activation=relu 491 | 492 | [convolutional] 493 | batch_normalize=0 494 | filters=38 495 | size=1 496 | stride=1 497 | pad=0 498 | activation=linear 499 | 500 | [route] 501 | layers=-8 502 | 503 | [convolutional] 504 | batch_normalize=0 505 | filters=128 506 | size=7 507 | stride=1 508 | pad=3 509 | activation=relu 510 | 511 | [convolutional] 512 | batch_normalize=0 513 | filters=128 514 | size=7 515 | stride=1 516 | pad=3 517 | activation=relu 518 | 519 | [convolutional] 520 | batch_normalize=0 521 | filters=128 522 | size=7 523 | stride=1 524 | pad=3 525 | activation=relu 526 | 527 | [convolutional] 528 | batch_normalize=0 529 | filters=128 530 | size=7 531 | stride=1 532 | pad=3 533 | activation=relu 534 | 535 | [convolutional] 536 | batch_normalize=0 537 | filters=128 538 | size=7 539 | stride=1 540 | pad=3 541 | activation=relu 542 | 543 | [convolutional] 544 | batch_normalize=0 545 | filters=128 546 | size=1 547 | stride=1 548 | pad=0 549 | activation=relu 550 | 551 | [convolutional] 552 | batch_normalize=0 553 | filters=19 554 | size=1 555 | stride=1 556 | pad=0 557 | activation=linear 558 | 559 | [route] 560 | layers=-9,-1,-60 561 | 562 | ###concat_stage5### 563 | 564 | [convolutional] 565 | batch_normalize=0 566 | filters=128 567 | size=7 568 | stride=1 569 | pad=3 570 | activation=relu 571 | 572 | [convolutional] 573 | batch_normalize=0 574 | filters=128 575 | size=7 576 | stride=1 577 | pad=3 578 | activation=relu 579 | 580 | [convolutional] 581 | batch_normalize=0 582 | filters=128 583 | size=7 584 | stride=1 585 | pad=3 586 | activation=relu 587 | 588 | [convolutional] 589 | batch_normalize=0 590 | filters=128 591 | size=7 592 | stride=1 593 | pad=3 594 | activation=relu 595 | 596 | [convolutional] 597 | batch_normalize=0 598 | filters=128 599 | size=7 600 | stride=1 601 | pad=3 602 | activation=relu 603 | 604 | [convolutional] 605 | batch_normalize=0 606 | filters=128 607 | size=1 608 | stride=1 609 | pad=0 610 | activation=relu 611 | 612 | [convolutional] 613 | batch_normalize=0 614 | filters=38 615 | size=1 616 | stride=1 617 | pad=0 618 | activation=linear 619 | 620 | [route] 621 | layers=-8 622 | 623 | [convolutional] 624 | batch_normalize=0 625 | filters=128 626 | size=7 627 | stride=1 628 | pad=3 629 | activation=relu 630 | 631 | [convolutional] 632 | batch_normalize=0 633 | filters=128 634 | size=7 635 | stride=1 636 | pad=3 637 | activation=relu 638 | 639 | [convolutional] 640 | batch_normalize=0 641 | filters=128 642 | size=7 643 | stride=1 644 | pad=3 645 | activation=relu 646 | 647 | [convolutional] 648 | batch_normalize=0 649 | filters=128 650 | size=7 651 | stride=1 652 | pad=3 653 | activation=relu 654 | 655 | [convolutional] 656 | batch_normalize=0 657 | filters=128 658 | size=7 659 | stride=1 660 | pad=3 661 | activation=relu 662 | 663 | [convolutional] 664 | batch_normalize=0 665 | filters=128 666 | size=1 667 | stride=1 668 | pad=0 669 | activation=relu 670 | 671 | [convolutional] 672 | batch_normalize=0 673 | filters=19 674 | size=1 675 | stride=1 676 | pad=0 677 | activation=linear 678 | 679 | [route] 680 | layers=-9,-1,-76 681 | 682 | ###concat_stage6### 683 | 684 | [convolutional] 685 | batch_normalize=0 686 | filters=128 687 | size=7 688 | stride=1 689 | pad=3 690 | activation=relu 691 | 692 | [convolutional] 693 | batch_normalize=0 694 | filters=128 695 | size=7 696 | stride=1 697 | pad=3 698 | activation=relu 699 | 700 | [convolutional] 701 | batch_normalize=0 702 | filters=128 703 | size=7 704 | stride=1 705 | pad=3 706 | activation=relu 707 | 708 | [convolutional] 709 | batch_normalize=0 710 | filters=128 711 | size=7 712 | stride=1 713 | pad=3 714 | activation=relu 715 | 716 | [convolutional] 717 | batch_normalize=0 718 | filters=128 719 | size=7 720 | stride=1 721 | pad=3 722 | activation=relu 723 | 724 | [convolutional] 725 | batch_normalize=0 726 | filters=128 727 | size=1 728 | stride=1 729 | pad=0 730 | activation=relu 731 | 732 | [convolutional] 733 | batch_normalize=0 734 | filters=38 735 | size=1 736 | stride=1 737 | pad=0 738 | activation=linear 739 | 740 | [route] 741 | layers=-8 742 | 743 | [convolutional] 744 | batch_normalize=0 745 | filters=128 746 | size=7 747 | stride=1 748 | pad=3 749 | activation=relu 750 | 751 | [convolutional] 752 | batch_normalize=0 753 | filters=128 754 | size=7 755 | stride=1 756 | pad=3 757 | activation=relu 758 | 759 | [convolutional] 760 | batch_normalize=0 761 | filters=128 762 | size=7 763 | stride=1 764 | pad=3 765 | activation=relu 766 | 767 | [convolutional] 768 | batch_normalize=0 769 | filters=128 770 | size=7 771 | stride=1 772 | pad=3 773 | activation=relu 774 | 775 | [convolutional] 776 | batch_normalize=0 777 | filters=128 778 | size=7 779 | stride=1 780 | pad=3 781 | activation=relu 782 | 783 | [convolutional] 784 | batch_normalize=0 785 | filters=128 786 | size=1 787 | stride=1 788 | pad=0 789 | activation=relu 790 | 791 | [convolutional] 792 | batch_normalize=0 793 | filters=19 794 | size=1 795 | stride=1 796 | pad=0 797 | activation=linear 798 | 799 | [route] 800 | layers=-1,-9 801 | -------------------------------------------------------------------------------- /server/src/DetectorInterface.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __DET_INTERFACE 2 | #define __DET_INTERFACE 3 | #include 4 | #include 5 | 6 | class DetectorInterface { 7 | public: 8 | virtual void detect(cv::Mat, float thresh) = 0; 9 | virtual void draw(cv::Mat) = 0; 10 | virtual std::string det_to_json(int frame_id) = 0; 11 | }; 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /server/src/Hungarian.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Hungarian.cpp: Implementation file for Class HungarianAlgorithm. 3 | // 4 | // This is a C++ wrapper with slight modification of a hungarian algorithm implementation by Markus Buehren. 5 | // The original implementation is a few mex-functions for use in MATLAB, found here: 6 | // http://www.mathworks.com/matlabcentral/fileexchange/6543-functions-for-the-rectangular-assignment-problem 7 | // 8 | // Both this code and the orignal code are published under the BSD license. 9 | // by Cong Ma, 2016 10 | // 11 | 12 | #include "Hungarian.h" 13 | 14 | 15 | HungarianAlgorithm::HungarianAlgorithm(){} 16 | HungarianAlgorithm::~HungarianAlgorithm(){} 17 | 18 | 19 | //********************************************************// 20 | // A single function wrapper for solving assignment problem. 21 | //********************************************************// 22 | double HungarianAlgorithm::Solve(vector >& DistMatrix, vector& Assignment) 23 | { 24 | unsigned int nRows = DistMatrix.size(); 25 | unsigned int nCols = DistMatrix[0].size(); 26 | 27 | double *distMatrixIn = new double[nRows * nCols]; 28 | int *assignment = new int[nRows]; 29 | double cost = 0.0; 30 | 31 | // Fill in the distMatrixIn. Mind the index is "i + nRows * j". 32 | // Here the cost matrix of size MxN is defined as a double precision array of N*M elements. 33 | // In the solving functions matrices are seen to be saved MATLAB-internally in row-order. 34 | // (i.e. the matrix [1 2; 3 4] will be stored as a vector [1 3 2 4], NOT [1 2 3 4]). 35 | for (unsigned int i = 0; i < nRows; i++) 36 | for (unsigned int j = 0; j < nCols; j++) 37 | distMatrixIn[i + nRows * j] = DistMatrix[i][j]; 38 | 39 | // call solving function 40 | assignmentoptimal(assignment, &cost, distMatrixIn, nRows, nCols); 41 | 42 | Assignment.clear(); 43 | for (unsigned int r = 0; r < nRows; r++) 44 | Assignment.push_back(assignment[r]); 45 | 46 | delete[] distMatrixIn; 47 | delete[] assignment; 48 | return cost; 49 | } 50 | 51 | 52 | //********************************************************// 53 | // Solve optimal solution for assignment problem using Munkres algorithm, also known as Hungarian Algorithm. 54 | //********************************************************// 55 | void HungarianAlgorithm::assignmentoptimal(int *assignment, double *cost, double *distMatrixIn, int nOfRows, int nOfColumns) 56 | { 57 | double *distMatrix, *distMatrixTemp, *distMatrixEnd, *columnEnd, value, minValue; 58 | bool *coveredColumns, *coveredRows, *starMatrix, *newStarMatrix, *primeMatrix; 59 | int nOfElements, minDim, row, col; 60 | 61 | /* initialization */ 62 | *cost = 0; 63 | for (row = 0; row nOfColumns) */ 128 | { 129 | minDim = nOfColumns; 130 | 131 | for (col = 0; col= 0) 211 | *cost += distMatrix[row + nOfRows*col]; 212 | } 213 | } 214 | 215 | /********************************************************/ 216 | void HungarianAlgorithm::step2a(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim) 217 | { 218 | bool *starMatrixTemp, *columnEnd; 219 | int col; 220 | 221 | /* cover every column containing a starred zero */ 222 | for (col = 0; col 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | using namespace std; 19 | 20 | 21 | class HungarianAlgorithm 22 | { 23 | public: 24 | HungarianAlgorithm(); 25 | ~HungarianAlgorithm(); 26 | double Solve(vector >& DistMatrix, vector& Assignment); 27 | 28 | private: 29 | void assignmentoptimal(int *assignment, double *cost, double *distMatrix, int nOfRows, int nOfColumns); 30 | void buildassignmentvector(int *assignment, bool *starMatrix, int nOfRows, int nOfColumns); 31 | void computeassignmentcost(int *assignment, double *cost, double *distMatrix, int nOfRows); 32 | void step2a(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim); 33 | void step2b(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim); 34 | void step3(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim); 35 | void step4(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim, int row, int col); 36 | void step5(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim); 37 | }; 38 | -------------------------------------------------------------------------------- /server/src/KalmanTracker.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // KalmanTracker.cpp: KalmanTracker Class Implementation Declaration 3 | 4 | #include "KalmanTracker.h" 5 | 6 | 7 | int KalmanTracker::kf_count = 0; 8 | 9 | 10 | // initialize Kalman filter 11 | void KalmanTracker::init_kf(StateType stateMat) 12 | { 13 | int stateNum = 7; 14 | int measureNum = 4; 15 | kf = KalmanFilter(stateNum, measureNum, 0); 16 | 17 | measurement = Mat::zeros(measureNum, 1, CV_32F); 18 | 19 | kf.transitionMatrix = (Mat_(stateNum, stateNum) << 20 | 1, 0, 0, 0, 1, 0, 0, 21 | 0, 1, 0, 0, 0, 1, 0, 22 | 0, 0, 1, 0, 0, 0, 1, 23 | 0, 0, 0, 1, 0, 0, 0, 24 | 0, 0, 0, 0, 1, 0, 0, 25 | 0, 0, 0, 0, 0, 1, 0, 26 | 0, 0, 0, 0, 0, 0, 1); 27 | 28 | setIdentity(kf.measurementMatrix); 29 | setIdentity(kf.processNoiseCov, Scalar::all(1e-2)); 30 | setIdentity(kf.measurementNoiseCov, Scalar::all(1e-1)); 31 | setIdentity(kf.errorCovPost, Scalar::all(1)); 32 | 33 | // initialize state vector with bounding box in [cx,cy,s,r] style 34 | kf.statePost.at(0, 0) = stateMat.x + stateMat.width / 2; 35 | kf.statePost.at(1, 0) = stateMat.y + stateMat.height / 2; 36 | kf.statePost.at(2, 0) = stateMat.area(); 37 | kf.statePost.at(3, 0) = stateMat.width / stateMat.height; 38 | } 39 | 40 | 41 | // Predict the estimated bounding box. 42 | StateType KalmanTracker::predict() 43 | { 44 | // predict 45 | Mat p = kf.predict(); 46 | m_age += 1; 47 | 48 | if (m_time_since_update > 0) 49 | m_hit_streak = 0; 50 | m_time_since_update += 1; 51 | 52 | StateType predictBox = get_rect_xysr(p.at(0, 0), p.at(1, 0), p.at(2, 0), p.at(3, 0)); 53 | 54 | m_history.push_back(predictBox); 55 | return m_history.back(); 56 | } 57 | 58 | 59 | // Update the state vector with observed bounding box. 60 | void KalmanTracker::update(StateType stateMat) 61 | { 62 | m_time_since_update = 0; 63 | m_history.clear(); 64 | m_hits += 1; 65 | m_hit_streak += 1; 66 | 67 | // measurement 68 | measurement.at(0, 0) = stateMat.x + stateMat.width / 2; 69 | measurement.at(1, 0) = stateMat.y + stateMat.height / 2; 70 | measurement.at(2, 0) = stateMat.area(); 71 | measurement.at(3, 0) = stateMat.width / stateMat.height; 72 | 73 | // update 74 | kf.correct(measurement); 75 | } 76 | 77 | 78 | // Return the current state vector 79 | StateType KalmanTracker::get_state() 80 | { 81 | Mat s = kf.statePost; 82 | return get_rect_xysr(s.at(0, 0), s.at(1, 0), s.at(2, 0), s.at(3, 0)); 83 | } 84 | 85 | 86 | // Convert bounding box from [cx,cy,s,r] to [x,y,w,h] style. 87 | StateType KalmanTracker::get_rect_xysr(float cx, float cy, float s, float r) 88 | { 89 | float w = sqrt(s * r); 90 | float h = s / w; 91 | float x = (cx - w / 2); 92 | float y = (cy - h / 2); 93 | 94 | if (x < 0 && cx > 0) 95 | x = 0; 96 | if (y < 0 && cy > 0) 97 | y = 0; 98 | 99 | return StateType(x, y, w, h); 100 | } 101 | 102 | 103 | 104 | /* 105 | // -------------------------------------------------------------------- 106 | // Kalman Filter Demonstrating, a 2-d ball demo 107 | // -------------------------------------------------------------------- 108 | 109 | const int winHeight = 600; 110 | const int winWidth = 800; 111 | 112 | Point mousePosition = Point(winWidth >> 1, winHeight >> 1); 113 | 114 | // mouse event callback 115 | void mouseEvent(int event, int x, int y, int flags, void *param) 116 | { 117 | if (event == CV_EVENT_MOUSEMOVE) { 118 | mousePosition = Point(x, y); 119 | } 120 | } 121 | 122 | void TestKF(); 123 | 124 | void main() 125 | { 126 | TestKF(); 127 | } 128 | 129 | 130 | void TestKF() 131 | { 132 | int stateNum = 4; 133 | int measureNum = 2; 134 | KalmanFilter kf = KalmanFilter(stateNum, measureNum, 0); 135 | 136 | // initialization 137 | Mat processNoise(stateNum, 1, CV_32F); 138 | Mat measurement = Mat::zeros(measureNum, 1, CV_32F); 139 | 140 | kf.transitionMatrix = *(Mat_(stateNum, stateNum) << 141 | 1, 0, 1, 0, 142 | 0, 1, 0, 1, 143 | 0, 0, 1, 0, 144 | 0, 0, 0, 1); 145 | 146 | setIdentity(kf.measurementMatrix); 147 | setIdentity(kf.processNoiseCov, Scalar::all(1e-2)); 148 | setIdentity(kf.measurementNoiseCov, Scalar::all(1e-1)); 149 | setIdentity(kf.errorCovPost, Scalar::all(1)); 150 | 151 | randn(kf.statePost, Scalar::all(0), Scalar::all(winHeight)); 152 | 153 | namedWindow("Kalman"); 154 | setMouseCallback("Kalman", mouseEvent); 155 | Mat img(winHeight, winWidth, CV_8UC3); 156 | 157 | while (1) 158 | { 159 | // predict 160 | Mat prediction = kf.predict(); 161 | Point predictPt = Point(prediction.at(0, 0), prediction.at(1, 0)); 162 | 163 | // generate measurement 164 | Point statePt = mousePosition; 165 | measurement.at(0, 0) = statePt.x; 166 | measurement.at(1, 0) = statePt.y; 167 | 168 | // update 169 | kf.correct(measurement); 170 | 171 | // visualization 172 | img.setTo(Scalar(255, 255, 255)); 173 | circle(img, predictPt, 8, CV_RGB(0, 255, 0), -1); // predicted point as green 174 | circle(img, statePt, 8, CV_RGB(255, 0, 0), -1); // current position as red 175 | 176 | imshow("Kalman", img); 177 | char code = (char)waitKey(100); 178 | if (code == 27 || code == 'q' || code == 'Q') 179 | break; 180 | } 181 | destroyWindow("Kalman"); 182 | } 183 | */ 184 | -------------------------------------------------------------------------------- /server/src/KalmanTracker.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // KalmanTracker.h: KalmanTracker Class Declaration 3 | 4 | #ifndef KALMAN_H 5 | #define KALMAN_H 2 6 | 7 | #include "opencv2/video/tracking.hpp" 8 | #include "opencv2/highgui/highgui.hpp" 9 | #include "people.hpp" 10 | 11 | using namespace std; 12 | using namespace cv; 13 | 14 | #define StateType Rect_ 15 | 16 | 17 | // This class represents the internel state of individual tracked objects observed as bounding box. 18 | class KalmanTracker 19 | { 20 | public: 21 | KalmanTracker() 22 | { 23 | init_kf(StateType()); 24 | m_time_since_update = 0; 25 | m_hits = 0; 26 | m_hit_streak = 0; 27 | m_age = 0; 28 | m_id = kf_count; 29 | //kf_count++; 30 | } 31 | KalmanTracker(StateType initRect, Person *p = NULL) 32 | { 33 | init_kf(initRect); 34 | m_time_since_update = 0; 35 | m_hits = 0; 36 | m_hit_streak = 0; 37 | m_age = 0; 38 | m_id = kf_count; 39 | m_p = p; 40 | kf_count++; 41 | } 42 | 43 | ~KalmanTracker() 44 | { 45 | m_history.clear(); 46 | } 47 | 48 | StateType predict(); 49 | void update(StateType stateMat); 50 | 51 | StateType get_state(); 52 | StateType get_rect_xysr(float cx, float cy, float s, float r); 53 | 54 | static int kf_count; 55 | 56 | int m_time_since_update; 57 | int m_hits; 58 | int m_hit_streak; 59 | int m_age; 60 | int m_id; 61 | Person *m_p; 62 | 63 | private: 64 | void init_kf(StateType stateMat); 65 | 66 | cv::KalmanFilter kf; 67 | cv::Mat measurement; 68 | 69 | std::vector m_history; 70 | }; 71 | 72 | 73 | 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /server/src/Tracker.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "Hungarian.h" 6 | #include "Tracker.hpp" 7 | 8 | using namespace std; 9 | 10 | 11 | Tracker::Tracker() { 12 | frame_count = 0; 13 | max_age = 1; 14 | min_hits = 3; 15 | iouThreshold = 0.3; 16 | trkNum = detNum = 0; 17 | KalmanTracker::kf_count = 0; // tracking id relies on this, so we have to reset it in each seq. 18 | } 19 | 20 | // Computes IOU between two bounding boxes 21 | double Tracker::GetIOU(Rect_ bb_test, Rect_ bb_gt) 22 | { 23 | float in = (bb_test & bb_gt).area(); 24 | float un = bb_test.area() + bb_gt.area() - in; 25 | 26 | if (un < DBL_EPSILON) 27 | return 0; 28 | 29 | return (double)(in / un); 30 | } 31 | 32 | vector Tracker::init(vector &detections) { 33 | frame_count = 1; 34 | // initialize kalman trackers using first detections. 35 | for (unsigned int i = 0; i < detections.size(); i++) 36 | { 37 | KalmanTracker trk = KalmanTracker(detections[i].box, detections[i].p); 38 | trackers.push_back(trk); 39 | } 40 | 41 | // output the first frame detections 42 | for (unsigned int id = 0; id < detections.size(); id++) 43 | { 44 | TrackingBox tb = detections[id]; 45 | tb.id = id + 1; 46 | tb.p->set_id(tb.id); 47 | frameTrackingResult.push_back(tb); 48 | // 49 | } 50 | return frameTrackingResult; 51 | } 52 | 53 | vector Tracker::update(vector &detections) { 54 | frame_count++; 55 | // 3.1. get predicted locations from existing trackers. 56 | predictedBoxes.clear(); 57 | 58 | for (auto it = trackers.begin(); it != trackers.end();) 59 | { 60 | Rect_ pBox = (*it).predict(); 61 | if (pBox.x >= 0 && pBox.y >= 0) 62 | { 63 | predictedBoxes.push_back(pBox); 64 | it++; 65 | } 66 | else 67 | { 68 | it = trackers.erase(it); 69 | //cerr << "Box invalid at frame: " << frame_count << endl; 70 | } 71 | } 72 | 73 | /////////////////////////////////////// 74 | // 3.2. associate detections to tracked object (both represented as bounding boxes) 75 | // dets : detFrameData[fi] 76 | trkNum = predictedBoxes.size(); 77 | detNum = detections.size(); 78 | 79 | iouMatrix.clear(); 80 | iouMatrix.resize(trkNum, vector(detNum, 0)); 81 | 82 | for (unsigned int i = 0; i < trkNum; i++) // compute iou matrix as a distance matrix 83 | { 84 | for (unsigned int j = 0; j < detNum; j++) 85 | { 86 | // use 1-iou because the hungarian algorithm computes a minimum-cost assignment. 87 | iouMatrix[i][j] = 1 - GetIOU(predictedBoxes[i], detections[j].box); 88 | } 89 | } 90 | 91 | // solve the assignment problem using hungarian algorithm. 92 | // the resulting assignment is [track(prediction) : detection], with len=preNum 93 | HungarianAlgorithm HungAlgo; 94 | assignment.clear(); 95 | HungAlgo.Solve(iouMatrix, assignment); 96 | 97 | // find matches, unmatched_detections and unmatched_predictions 98 | unmatchedTrajectories.clear(); 99 | unmatchedDetections.clear(); 100 | allItems.clear(); 101 | matchedItems.clear(); 102 | 103 | if (detNum > trkNum) // there are unmatched detections 104 | { 105 | for (unsigned int n = 0; n < detNum; n++) 106 | allItems.insert(n); 107 | 108 | for (unsigned int i = 0; i < trkNum; ++i) 109 | matchedItems.insert(assignment[i]); 110 | 111 | set_difference(allItems.begin(), allItems.end(), 112 | matchedItems.begin(), matchedItems.end(), 113 | insert_iterator>(unmatchedDetections, unmatchedDetections.begin())); 114 | } 115 | else if (detNum < trkNum) // there are unmatched trajectory/predictions 116 | { 117 | for (unsigned int i = 0; i < trkNum; ++i) 118 | if (assignment[i] == -1) // unassigned label will be set as -1 in the assignment algorithm 119 | unmatchedTrajectories.insert(i); 120 | } 121 | else 122 | ; 123 | 124 | // filter out matched with low IOU 125 | matchedPairs.clear(); 126 | for (unsigned int i = 0; i < trkNum; ++i) 127 | { 128 | if (assignment[i] == -1) // pass over invalid values 129 | continue; 130 | if (1 - iouMatrix[i][assignment[i]] < iouThreshold) 131 | { 132 | unmatchedTrajectories.insert(i); 133 | unmatchedDetections.insert(assignment[i]); 134 | } 135 | else 136 | matchedPairs.push_back(cv::Point(i, assignment[i])); 137 | } 138 | 139 | /////////////////////////////////////// 140 | // 3.3. updating trackers 141 | 142 | // update matched trackers with assigned detections. 143 | // each prediction is corresponding to a tracker 144 | int detIdx, trkIdx; 145 | for (unsigned int i = 0; i < matchedPairs.size(); i++) 146 | { 147 | trkIdx = matchedPairs[i].x; 148 | detIdx = matchedPairs[i].y; 149 | trackers[trkIdx].update(detections[detIdx].box); 150 | trackers[trkIdx].m_p->update(detections[detIdx].p); 151 | } 152 | 153 | // create and initialise new trackers for unmatched detections 154 | for (auto umd : unmatchedDetections) 155 | { 156 | KalmanTracker tracker = KalmanTracker(detections[umd].box, detections[umd].p); 157 | trackers.push_back(tracker); 158 | } 159 | 160 | // get trackers' output 161 | frameTrackingResult.clear(); 162 | for (auto it = trackers.begin(); it != trackers.end();) 163 | { 164 | if (((*it).m_time_since_update < 1) && 165 | ((*it).m_hit_streak >= min_hits || frame_count <= min_hits)) 166 | { 167 | TrackingBox res; 168 | res.box = (*it).get_state(); 169 | res.id = (*it).m_id + 1; 170 | res.frame = frame_count; 171 | 172 | // person info update 173 | res.p = (*it).m_p; 174 | res.p->set_id(res.id); 175 | res.p->set_rect(res.box); 176 | 177 | frameTrackingResult.push_back(res); 178 | it++; 179 | } 180 | else 181 | it++; 182 | 183 | // remove dead tracklet 184 | if (it != trackers.end() && (*it).m_time_since_update > max_age) { 185 | // delete person object 186 | delete (*it).m_p; 187 | it = trackers.erase(it); 188 | } 189 | } 190 | 191 | return frameTrackingResult; 192 | } // update method end 193 | 194 | Tracker::~Tracker() { 195 | 196 | } 197 | -------------------------------------------------------------------------------- /server/src/Tracker.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "KalmanTracker.h" 3 | #include 4 | #include "people.hpp" 5 | 6 | typedef struct TrackingBox 7 | { 8 | int frame; 9 | int id; 10 | Rect_ box; 11 | Person* p; 12 | }TrackingBox; 13 | 14 | class Tracker 15 | { 16 | 17 | private: 18 | int frame_count; 19 | int max_age; 20 | int min_hits; 21 | double iouThreshold; 22 | 23 | unsigned int trkNum; 24 | unsigned int detNum; 25 | 26 | vector trackers; 27 | 28 | // variables used in the for-loop 29 | vector > predictedBoxes; 30 | vector > iouMatrix; 31 | vector assignment; 32 | set unmatchedDetections; 33 | set unmatchedTrajectories; 34 | set allItems; 35 | set matchedItems; 36 | vector matchedPairs; 37 | vector frameTrackingResult; 38 | 39 | public: 40 | Tracker(); 41 | // Computes IOU between two bounding boxes 42 | double GetIOU(Rect_ bb_test, Rect_ bb_gt); 43 | vector init(vector &detections); 44 | vector update(vector &detections); 45 | ~Tracker(); 46 | }; 47 | -------------------------------------------------------------------------------- /server/src/args.cpp: -------------------------------------------------------------------------------- 1 | // https://github.com/pjreddie/template 2 | 3 | #include 4 | #include 5 | #include 6 | #include "args.hpp" 7 | 8 | void del_arg(int argc, char **argv, int index) 9 | { 10 | int i; 11 | for (i = index; i < argc - 1; ++i) argv[i] = argv[i + 1]; 12 | argv[i] = 0; 13 | } 14 | 15 | int find_arg(int argc, char* argv[], const char *arg) 16 | { 17 | int i; 18 | for (i = 0; i < argc; ++i) { 19 | if (!argv[i]) continue; 20 | if (0 == strcmp(argv[i], arg)) { 21 | del_arg(argc, argv, i); 22 | return 1; 23 | } 24 | } 25 | return 0; 26 | } 27 | 28 | int find_int_arg(int argc, char **argv, const char *arg, int def) 29 | { 30 | int i; 31 | for (i = 0; i < argc - 1; ++i) { 32 | if (!argv[i]) continue; 33 | if (0 == strcmp(argv[i], arg)) { 34 | def = atoi(argv[i + 1]); 35 | del_arg(argc, argv, i); 36 | del_arg(argc, argv, i); 37 | break; 38 | } 39 | } 40 | return def; 41 | } 42 | 43 | float find_float_arg(int argc, char **argv, const char *arg, float def) 44 | { 45 | int i; 46 | for (i = 0; i < argc - 1; ++i) { 47 | if (!argv[i]) continue; 48 | if (0 == strcmp(argv[i], arg)) { 49 | def = atof(argv[i + 1]); 50 | del_arg(argc, argv, i); 51 | del_arg(argc, argv, i); 52 | break; 53 | } 54 | } 55 | return def; 56 | } 57 | 58 | const char *find_char_arg(int argc, char **argv, const char *arg, const char *def) 59 | { 60 | int i; 61 | for (i = 0; i < argc - 1; ++i) { 62 | if (!argv[i]) continue; 63 | if (0 == strcmp(argv[i], arg)) { 64 | def = argv[i + 1]; 65 | del_arg(argc, argv, i); 66 | del_arg(argc, argv, i); 67 | break; 68 | } 69 | } 70 | return def; 71 | } 72 | -------------------------------------------------------------------------------- /server/src/args.hpp: -------------------------------------------------------------------------------- 1 | // https://github.com/pjreddie/template 2 | 3 | #ifndef ARGS_H 4 | #define ARGS_H 5 | 6 | int find_arg(int argc, char* argv[], const char *arg); 7 | int find_int_arg(int argc, char **argv, const char *arg, int def); 8 | float find_float_arg(int argc, char **argv, const char *arg, float def); 9 | const char *find_char_arg(int argc, char **argv, const char *arg, const char *def); 10 | 11 | #endif -------------------------------------------------------------------------------- /server/src/base64.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | base64.cpp and base64.h 3 | 4 | base64 encoding and decoding with C++. 5 | 6 | Version: 1.01.00 7 | 8 | Copyright (C) 2004-2017 René Nyffenegger 9 | 10 | This source code is provided 'as-is', without any express or implied 11 | warranty. In no event will the author be held liable for any damages 12 | arising from the use of this software. 13 | 14 | Permission is granted to anyone to use this software for any purpose, 15 | including commercial applications, and to alter it and redistribute it 16 | freely, subject to the following restrictions: 17 | 18 | 1. The origin of this source code must not be misrepresented; you must not 19 | claim that you wrote the original source code. If you use this source code 20 | in a product, an acknowledgment in the product documentation would be 21 | appreciated but is not required. 22 | 23 | 2. Altered source versions must be plainly marked as such, and must not be 24 | misrepresented as being the original source code. 25 | 26 | 3. This notice may not be removed or altered from any source distribution. 27 | 28 | René Nyffenegger rene.nyffenegger@adp-gmbh.ch 29 | 30 | */ 31 | 32 | #include "base64.h" 33 | #include 34 | 35 | static const std::string base64_chars = 36 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 37 | "abcdefghijklmnopqrstuvwxyz" 38 | "0123456789+/"; 39 | 40 | 41 | static inline bool is_base64(unsigned char c) { 42 | return (isalnum(c) || (c == '+') || (c == '/')); 43 | } 44 | 45 | std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { 46 | std::string ret; 47 | int i = 0; 48 | int j = 0; 49 | unsigned char char_array_3[3]; 50 | unsigned char char_array_4[4]; 51 | 52 | while (in_len--) { 53 | char_array_3[i++] = *(bytes_to_encode++); 54 | if (i == 3) { 55 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 56 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 57 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 58 | char_array_4[3] = char_array_3[2] & 0x3f; 59 | 60 | for(i = 0; (i <4) ; i++) 61 | ret += base64_chars[char_array_4[i]]; 62 | i = 0; 63 | } 64 | } 65 | 66 | if (i) 67 | { 68 | for(j = i; j < 3; j++) 69 | char_array_3[j] = '\0'; 70 | 71 | char_array_4[0] = ( char_array_3[0] & 0xfc) >> 2; 72 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 73 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 74 | 75 | for (j = 0; (j < i + 1); j++) 76 | ret += base64_chars[char_array_4[j]]; 77 | 78 | while((i++ < 3)) 79 | ret += '='; 80 | 81 | } 82 | 83 | return ret; 84 | 85 | } 86 | 87 | std::string base64_decode(std::string const& encoded_string) { 88 | size_t in_len = encoded_string.size(); 89 | int i = 0; 90 | int j = 0; 91 | int in_ = 0; 92 | unsigned char char_array_4[4], char_array_3[3]; 93 | std::string ret; 94 | 95 | while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { 96 | char_array_4[i++] = encoded_string[in_]; in_++; 97 | if (i ==4) { 98 | for (i = 0; i <4; i++) 99 | char_array_4[i] = base64_chars.find(char_array_4[i]) & 0xff; 100 | 101 | char_array_3[0] = ( char_array_4[0] << 2 ) + ((char_array_4[1] & 0x30) >> 4); 102 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 103 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 104 | 105 | for (i = 0; (i < 3); i++) 106 | ret += char_array_3[i]; 107 | i = 0; 108 | } 109 | } 110 | 111 | if (i) { 112 | for (j = 0; j < i; j++) 113 | char_array_4[j] = base64_chars.find(char_array_4[j]) & 0xff; 114 | 115 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 116 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 117 | 118 | for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; 119 | } 120 | 121 | return ret; 122 | } 123 | -------------------------------------------------------------------------------- /server/src/base64.h: -------------------------------------------------------------------------------- 1 | // 2 | // base64 encoding and decoding with C++. 3 | // Version: 1.01.00 4 | // 5 | 6 | #ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A 7 | #define BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A 8 | 9 | #include 10 | 11 | std::string base64_encode(unsigned char const* , unsigned int len); 12 | std::string base64_decode(std::string const& s); 13 | 14 | #endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */ 15 | -------------------------------------------------------------------------------- /server/src/frame.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "frame.hpp" 5 | #include "json.h" 6 | #include "base64.h" 7 | 8 | using namespace std; 9 | 10 | Frame_pool::Frame_pool() 11 | { 12 | mem_pool_msg = new CMemPool(MEM_POOL_UNIT_NUM, SEQ_BUF_LEN); 13 | mem_pool_seq = new CMemPool(MEM_POOL_UNIT_NUM, MSG_BUF_LEN); 14 | mem_pool_det = new CMemPool(MEM_POOL_UNIT_NUM, DET_BUF_LEN); 15 | }; 16 | 17 | Frame_pool::Frame_pool(int unit_num) 18 | { 19 | mem_pool_msg = new CMemPool(unit_num, SEQ_BUF_LEN); 20 | mem_pool_seq = new CMemPool(unit_num, MSG_BUF_LEN); 21 | mem_pool_det = new CMemPool(unit_num, DET_BUF_LEN); 22 | }; 23 | 24 | Frame_pool::~Frame_pool() 25 | { 26 | 27 | }; 28 | 29 | Frame Frame_pool::alloc_frame(void) { 30 | Frame frame; 31 | frame_init(frame); 32 | return frame; 33 | }; 34 | 35 | void Frame_pool::free_frame(Frame& frame) { 36 | mem_pool_seq->Free((void *)frame.seq_buf); 37 | mem_pool_msg->Free((void *)frame.msg_buf); 38 | mem_pool_det->Free((void *)frame.det_buf); 39 | } 40 | 41 | void Frame_pool::frame_init(Frame& frame) { 42 | frame.seq_len = frame.msg_len = frame.det_len = 0; 43 | frame.seq_buf = (unsigned char *)(mem_pool_seq->Alloc(SEQ_BUF_LEN, true)); 44 | frame.msg_buf = (unsigned char *)(mem_pool_msg->Alloc(MSG_BUF_LEN, true)); 45 | frame.det_buf = (unsigned char *)(mem_pool_det->Alloc(DET_BUF_LEN, true)); 46 | }; 47 | 48 | 49 | 50 | int frame_to_json(void* buf, const Frame& frame) { 51 | stringstream ss; 52 | ss << "{\n\"seq\":\"" << base64_encode((unsigned char *)frame.seq_buf, frame.seq_len) << "\",\n" 53 | << "\"msg\": \"" << base64_encode((unsigned char*)(frame.msg_buf), frame.msg_len) << "\",\n" 54 | << "\"det\": \"" << base64_encode((unsigned char*)(frame.det_buf), frame.det_len) 55 | << "\"\n}"; 56 | 57 | 58 | memcpy(buf, ss.str().c_str(), ss.str().size()); 59 | ((unsigned char*)buf)[ss.str().size()] = '\0'; 60 | return ss.str().size(); 61 | }; 62 | 63 | void json_to_frame(void* buf, Frame& frame) { 64 | json_object *raw_obj; 65 | raw_obj = json_tokener_parse((const char*)buf); 66 | 67 | json_object *seq_obj = json_object_object_get(raw_obj, "seq"); 68 | json_object *msg_obj = json_object_object_get(raw_obj, "msg"); 69 | json_object *det_obj = json_object_object_get(raw_obj, "det"); 70 | 71 | string seq(base64_decode(json_object_get_string(seq_obj))); 72 | string msg(base64_decode(json_object_get_string(msg_obj))); 73 | string det(base64_decode(json_object_get_string(det_obj))); 74 | 75 | frame.seq_len = seq.size(); 76 | frame.msg_len = msg.size(); 77 | frame.det_len = det.size(); 78 | 79 | memcpy(frame.seq_buf, seq.c_str(), frame.seq_len); 80 | ((unsigned char*)frame.seq_buf)[frame.seq_len] = '\0'; 81 | 82 | memcpy(frame.msg_buf, msg.c_str(), frame.msg_len); 83 | ((unsigned char*)frame.msg_buf)[frame.msg_len] = '\0'; 84 | 85 | if (frame.det_len > 0) { 86 | memcpy(frame.det_buf, det.c_str(), frame.det_len); 87 | ((unsigned char*)frame.det_buf)[frame.det_len] = '\0'; 88 | } 89 | 90 | // free 91 | json_object_put(seq_obj); 92 | json_object_put(msg_obj); 93 | json_object_put(det_obj); 94 | }; 95 | -------------------------------------------------------------------------------- /server/src/frame.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __FRAME_H 2 | #define __FRAME_H 3 | 4 | #include "mem_pool.hpp" 5 | 6 | struct Frame { 7 | int seq_len; 8 | int msg_len; 9 | int det_len; 10 | unsigned char *seq_buf; 11 | unsigned char *msg_buf; 12 | unsigned char *det_buf; 13 | }; 14 | 15 | const int SEQ_BUF_LEN = 100; 16 | const int MSG_BUF_LEN = 76800; 17 | const int DET_BUF_LEN = 25600; 18 | const int JSON_BUF_LEN = MSG_BUF_LEN * 2; 19 | class Frame_pool 20 | { 21 | private: 22 | CMemPool *mem_pool_msg; 23 | CMemPool *mem_pool_seq; 24 | CMemPool *mem_pool_det; 25 | const int MEM_POOL_UNIT_NUM = 5000; 26 | 27 | public: 28 | Frame_pool(); 29 | Frame_pool(int unit_num); 30 | Frame alloc_frame(void); 31 | void free_frame(Frame& frame); 32 | void frame_init(Frame& frame); 33 | ~Frame_pool(); 34 | }; 35 | 36 | int frame_to_json(void* buf, const Frame& frame); 37 | void json_to_frame(void* buf, Frame& frame); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /server/src/mem_pool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "mem_pool.hpp" 4 | 5 | /*========================================================== 6 | CMemPool: 7 | Constructor of this class. It allocate memory block from system and create 8 | a static double linked list to manage all memory unit. 9 | 10 | Parameters: 11 | [in]ulUnitNum 12 | The number of unit which is a part of memory block. 13 | 14 | [in]ulUnitSize 15 | The size of unit. 16 | //========================================================= 17 | */ 18 | CMemPool::CMemPool(unsigned long ulUnitNum,unsigned long ulUnitSize) : 19 | m_pMemBlock(NULL), m_pAllocatedMemBlock(NULL), m_pFreeMemBlock(NULL), 20 | m_ulBlockSize(ulUnitNum * (ulUnitSize+sizeof(struct _Unit))), 21 | m_ulUnitSize(ulUnitSize) 22 | { 23 | m_pMemBlock = malloc(m_ulBlockSize); //Allocate a memory block. 24 | 25 | if(NULL != m_pMemBlock) 26 | { 27 | for(unsigned long i=0; ipPrev = NULL; 32 | pCurUnit->pNext = m_pFreeMemBlock; //Insert the new unit at head. 33 | 34 | if(NULL != m_pFreeMemBlock) 35 | { 36 | m_pFreeMemBlock->pPrev = pCurUnit; 37 | } 38 | m_pFreeMemBlock = pCurUnit; 39 | } 40 | } 41 | } 42 | 43 | /*=============================================================== 44 | ~CMemPool(): 45 | Destructor of this class. Its task is to free memory block. 46 | //=============================================================== 47 | */ 48 | CMemPool::~CMemPool() 49 | { 50 | free(m_pMemBlock); 51 | } 52 | 53 | /*================================================================ 54 | Alloc: 55 | To allocate a memory unit. If memory pool can`t provide proper memory unit, 56 | It will call system function. 57 | 58 | Parameters: 59 | [in]ulSize 60 | Memory unit size. 61 | 62 | [in]bUseMemPool 63 | Whether use memory pool. 64 | 65 | Return Values: 66 | Return a pointer to a memory unit. 67 | //================================================================= 68 | */ 69 | void* CMemPool::Alloc(unsigned long ulSize, bool bUseMemPool) 70 | { 71 | if( ulSize > m_ulUnitSize || false == bUseMemPool || 72 | NULL == m_pMemBlock || NULL == m_pFreeMemBlock) 73 | { 74 | return malloc(ulSize); 75 | } 76 | 77 | //Now FreeList isn`t empty 78 | struct _Unit *pCurUnit = m_pFreeMemBlock; 79 | m_pFreeMemBlock = pCurUnit->pNext; //Get a unit from free linkedlist. 80 | if(NULL != m_pFreeMemBlock) 81 | { 82 | m_pFreeMemBlock->pPrev = NULL; 83 | } 84 | 85 | pCurUnit->pNext = m_pAllocatedMemBlock; 86 | 87 | if(NULL != m_pAllocatedMemBlock) 88 | { 89 | m_pAllocatedMemBlock->pPrev = pCurUnit; 90 | } 91 | m_pAllocatedMemBlock = pCurUnit; 92 | 93 | return (void *)((char *)pCurUnit + sizeof(struct _Unit) ); 94 | } 95 | 96 | /*================================================================ 97 | Free: 98 | To free a memory unit. If the pointer of parameter point to a memory unit, 99 | then insert it to "Free linked list". Otherwise, call system function "free". 100 | 101 | Parameters: 102 | [in]p 103 | It point to a memory unit and prepare to free it. 104 | 105 | Return Values: 106 | none 107 | //================================================================ 108 | */ 109 | void CMemPool::Free( void* p ) 110 | { 111 | if(m_pMemBlock

pNext; 116 | if(NULL != m_pAllocatedMemBlock) 117 | { 118 | m_pAllocatedMemBlock->pPrev = NULL; 119 | } 120 | 121 | pCurUnit->pNext = m_pFreeMemBlock; 122 | if(NULL != m_pFreeMemBlock) 123 | { 124 | m_pFreeMemBlock->pPrev = pCurUnit; 125 | } 126 | 127 | m_pFreeMemBlock = pCurUnit; 128 | } 129 | else 130 | { 131 | free(p); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /server/src/mem_pool.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __MEMPOOL_H__ 2 | #define __MEMPOOL_H__ 3 | // https://www.codeproject.com/Articles/27487/Why-to-use-memory-pool-and-how-to-implement-it 4 | class CMemPool 5 | { 6 | private: 7 | //The purpose of the structure`s definition is that we can operate linkedlist conveniently 8 | struct _Unit //The type of the node of linkedlist. 9 | { 10 | struct _Unit *pPrev, *pNext; 11 | }; 12 | 13 | void* m_pMemBlock; //The address of memory pool. 14 | 15 | //Manage all unit with two linkedlist. 16 | struct _Unit* m_pAllocatedMemBlock; //Head pointer to Allocated linkedlist. 17 | struct _Unit* m_pFreeMemBlock; //Head pointer to Free linkedlist. 18 | 19 | unsigned long m_ulUnitSize; //Memory unit size. There are much unit in memory pool. 20 | unsigned long m_ulBlockSize;//Memory pool size. Memory pool is make of memory unit. 21 | 22 | public: 23 | CMemPool(unsigned long lUnitNum = 50, unsigned long lUnitSize = 1024); 24 | ~CMemPool(); 25 | 26 | void* Alloc(unsigned long ulSize, bool bUseMemPool = true); //Allocate memory unit 27 | void Free( void* p ); //Free memory unit 28 | }; 29 | 30 | #endif //__MEMPOOL_H__ 31 | -------------------------------------------------------------------------------- /server/src/people.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "people.hpp" 14 | 15 | void Person::set_part(int part, float x, float y) { 16 | history.front().x[part] = x; 17 | history.front().y[part] = y; 18 | 19 | // bounding box update 20 | if (x < min_x) 21 | min_x = x; 22 | else if (x > max_x) 23 | max_x = x; 24 | 25 | if (y < min_y) 26 | min_y = y; 27 | else if (y > max_y) 28 | max_y = y; 29 | } 30 | 31 | void Person::set_action(int type) { 32 | if (type < 0) 33 | type = ACTION_TYPE_NUM; 34 | action = type; 35 | 36 | if (actions.size() == ACTION_HIS_NUM) 37 | actions.pop_front(); 38 | actions.push_back(type); 39 | } 40 | 41 | float Person::get_dist(float x1, float y1, float x2, float y2) { 42 | if (x1 == 0 || x2 == 0) 43 | return 0.0; 44 | else { 45 | return sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); 46 | } 47 | } 48 | 49 | float Person::get_deg(float x1, float y1, float x2, float y2) { 50 | if (x1 == 0 || x2 == 0) 51 | return 0.0; 52 | double dx = x2 - x1; 53 | double dy = y2 - y1; 54 | double rad = atan2(dy, dx); 55 | double degree = (rad * 180) / M_PI; 56 | if (degree < 0) 57 | degree += 360; 58 | return degree; 59 | } 60 | 61 | bool Person::has_output(void) { 62 | if (overlap_count <= 0 && history.size() == HIS_NUM) { 63 | overlap_count = OVERLAP_NUM; 64 | return true; 65 | } 66 | else 67 | return false; 68 | } 69 | 70 | void Person::update(Person* n_p) 71 | { 72 | static const int change_part[] = { 73 | RELBOW, RWRIST, LELBOW, LWRIST, RKNEE, RANKLE, LKNEE, LANKLE 74 | }; 75 | static const int change_pair[] = { 76 | RSHOULDER, RELBOW, 77 | RELBOW, RWRIST, 78 | LSHOULDER, LELBOW, 79 | LELBOW, LWRIST, 80 | RHIP, RKNEE, 81 | RKNEE, RANKLE, 82 | LHIP, LKNEE, 83 | LKNEE, LANKLE 84 | }; 85 | 86 | Change c; 87 | double deg, n_deg; 88 | int part, pair_1, pair_2; 89 | const Joint& j = history.back(); 90 | const Joint& n_j = n_p->history.front(); 91 | 92 | // change calc 93 | for (int i = 0; i < CHANGE_NUM; i++) { 94 | part = change_part[i]; 95 | c.dist[i] = abs(get_dist(j.x[part], n_j.x[part], j.y[part], n_j.y[part])); 96 | 97 | pair_1 = change_pair[i * 2]; 98 | pair_2 = change_pair[i * 2 + 1]; 99 | 100 | deg = get_deg(j.x[pair_1], j.y[pair_1], j.x[pair_2], j.y[pair_2]); 101 | n_deg = get_deg(n_j.x[pair_1], n_j.y[pair_1], n_j.x[pair_2], n_j.y[pair_2]); 102 | c.cur_deg[i] = n_deg; 103 | if (deg == 0 || n_deg == 0) 104 | c.deg[i] = 0.0; 105 | else 106 | c.deg[i] = abs(deg - n_deg); 107 | } 108 | 109 | if (history.size() == HIS_NUM) { 110 | history.pop_front(); 111 | change_history.pop_front(); 112 | } 113 | history.push_back(n_p->history.front()); 114 | change_history.push_back(c); 115 | overlap_count--; 116 | 117 | // delete 118 | delete n_p; 119 | } 120 | 121 | /* 122 | std::string get_history(void) const 123 | { 124 | std::stringstream ss; 125 | for (int i = 0; i < history.size(); i++) { 126 | if (i != 0) 127 | ss << '\n'; 128 | for (int j = 0; j < JOINT_NUM; j++) { 129 | if (j != 0) 130 | ss << ','; 131 | ss << history[i].x[j] << ',' << history[i].y[j]; 132 | } 133 | } 134 | for (int i = history.size(); i < HIS_NUM; i++) { 135 | if (i != 0) 136 | ss << '\n'; 137 | for (int j = 0; j < JOINT_NUM; j++) { 138 | if (j != 0) 139 | ss << ','; 140 | ss << 0.0 << ',' << 0.0; 141 | } 142 | } 143 | return ss.str(); 144 | } 145 | */ 146 | 147 | bool Person::check_crash(const Person& other) const 148 | { 149 | const static int punch_check_joint[] = {RELBOW, RWRIST, LELBOW, LWRIST}; 150 | const static int kick_check_joint[] = {RKNEE, RANKLE, LKNEE, LANKLE}; 151 | 152 | const int* check_joint = nullptr; 153 | int my_action = this->get_action(); 154 | 155 | if (my_action == PUNCH) 156 | check_joint = punch_check_joint; 157 | else if (my_action == KICK) 158 | check_joint = kick_check_joint; 159 | else 160 | return false; 161 | 162 | cv::Rect_ other_rect = other.get_rect(); 163 | 164 | const Joint& j = history.back(); 165 | float x, y; 166 | for (int i = 0; i < 4; i++) { 167 | x = j.x[check_joint[i]]; 168 | y = j.y[check_joint[i]]; 169 | 170 | if (other_rect.x <= x && x <= other_rect.x + other_rect.width && 171 | other_rect.y <= y && y <= other_rect.y + other_rect.height) 172 | return true; 173 | } 174 | return false; 175 | } 176 | 177 | cv::Rect_ Person::get_crash_rect(const Person& p) const 178 | { 179 | cv::Rect_ a_rect = this->get_rect(); 180 | cv::Rect_ b_rect = p.get_rect(); 181 | 182 | float min_x, min_y, max_x, max_y; 183 | 184 | min_x = a_rect.x < b_rect.x ? a_rect.x : b_rect.x; 185 | min_y = a_rect.y < b_rect.y ? a_rect.y : b_rect.y; 186 | max_x = (a_rect.x + a_rect.width) > (b_rect.x + b_rect.width) ? (a_rect.x + a_rect.width) : (b_rect.x + 187 | b_rect.width); 188 | max_y = (a_rect.y + a_rect.height) > (b_rect.y + b_rect.height) ? (a_rect.y + a_rect.height) : (b_rect.y + 189 | b_rect.height); 190 | 191 | return cv::Rect_(cv::Point_(min_x, min_y), 192 | cv::Point_(max_x, max_y)); 193 | } 194 | 195 | std::string Person::get_history(void) const 196 | { 197 | std::stringstream ss; 198 | for (size_t i = 0; i < change_history.size(); i++) { 199 | if (i != 0) 200 | ss << '\n'; 201 | for (int j = 0; j < CHANGE_NUM; j++) { 202 | if (j != 0) 203 | ss << ','; 204 | ss << change_history[i].dist[j] << ',' << change_history[i].deg[j] << ',' << change_history[i].cur_deg[j]; 205 | } 206 | } 207 | for (int i = change_history.size(); i < HIS_NUM - 1; i++) { 208 | if (i != 0) 209 | ss << '\n'; 210 | for (int j = 0; j < CHANGE_NUM; j++) { 211 | if (j != 0) 212 | ss << ','; 213 | ss << 0.0 << ',' << 0.0 << ',' << 0.0; 214 | } 215 | } 216 | return ss.str(); 217 | } 218 | 219 | cv::Rect_ Person::get_rect(void) const 220 | { 221 | return cv::Rect_(cv::Point_(min_x, min_y), 222 | cv::Point_(max_x, max_y)); 223 | } 224 | 225 | void Person::set_rect(cv::Rect_& rect) 226 | { 227 | min_x = rect.x; 228 | min_y = rect.y; 229 | max_x = rect.x + rect.width; 230 | max_y = rect.y + rect.height; 231 | } 232 | 233 | Person& Person::operator=(const Person& p) 234 | { 235 | track_id = p.track_id; 236 | max_x = p.max_x; 237 | max_y = p.max_y; 238 | min_x = p.min_x; 239 | min_y = p.min_y; 240 | history = p.history; 241 | change_history = p.change_history; 242 | overlap_count = p.overlap_count; 243 | return *this; 244 | } 245 | 246 | std::ostream& operator<<(std::ostream &out, const Person &p) 247 | { 248 | for (size_t i = 0; i < p.change_history.size(); i++) { 249 | for (int j = 0; j < p.CHANGE_NUM; j++) { 250 | if (j != 0) 251 | out << ','; 252 | out << p.change_history[i].dist[j] << ',' << p.change_history[i].deg[j] << ',' << p.change_history[i].cur_deg[j]; 253 | } 254 | out << '\n'; 255 | } 256 | return out; 257 | } 258 | 259 | 260 | std::vector People::to_person(void) { 261 | std::vector persons; 262 | int person_num = keyshape[0]; 263 | int part_num = keyshape[1]; 264 | for (int person = 0; person < person_num; person++) { 265 | Person *p = new Person(); 266 | for (int part = 0; part < part_num; part++) { 267 | int index = (person * part_num + part) * keyshape[2]; 268 | if (keypoints[index + 2] > thresh) { 269 | p->set_part(part, keypoints[index] * scale, keypoints[index + 1] * scale); 270 | } 271 | } 272 | persons.push_back(p); 273 | } 274 | return persons; 275 | } 276 | 277 | std::string People::get_output(void) { 278 | std::string out_str = "\"people\": [\n"; 279 | int person_num = keyshape[0]; 280 | int part_num = keyshape[1]; 281 | for (int person = 0; person < person_num; person++) { 282 | if (person != 0) 283 | out_str += ",\n"; 284 | out_str += " {\n"; 285 | for (int part = 0; part < part_num; part++) { 286 | if (part != 0) 287 | out_str += ",\n "; 288 | int index = (person * part_num + part) * keyshape[2]; 289 | char *buf = (char*)calloc(2048, sizeof(char)); 290 | 291 | if (keypoints[index + 2] > thresh) { 292 | sprintf(buf, " \"%d\":[%f, %f]", part, keypoints[index] * scale, keypoints[index + 1] * scale); 293 | } 294 | else { 295 | sprintf(buf, " \"%d\":[%f, %f]", part, 0.0, 0.0); 296 | } 297 | out_str += buf; 298 | free(buf); 299 | } 300 | out_str += "\n }"; 301 | } 302 | out_str += "\n ]"; 303 | return out_str; 304 | } 305 | 306 | void People::render_pose_keypoints(cv::Mat& frame) 307 | { 308 | const int num_keypoints = keyshape[1]; 309 | unsigned int pairs[] = 310 | { 311 | 1, 2, 1, 5, 2, 3, 3, 4, 5, 6, 6, 7, 1, 8, 8, 9, 9, 10, 312 | 1, 11, 11, 12, 12, 13, 1, 0, 0, 14, 14, 16, 0, 15, 15, 17 313 | }; 314 | float colors[] = 315 | { 316 | 255.f, 0.f, 85.f, 255.f, 0.f, 0.f, 255.f, 85.f, 0.f, 255.f, 170.f, 0.f, 317 | 255.f, 255.f, 0.f, 170.f, 255.f, 0.f, 85.f, 255.f, 0.f, 0.f, 255.f, 0.f, 318 | 0.f, 255.f, 85.f, 0.f, 255.f, 170.f, 0.f, 255.f, 255.f, 0.f, 170.f, 255.f, 319 | 0.f, 85.f, 255.f, 0.f, 0.f, 255.f, 255.f, 0.f, 170.f, 170.f, 0.f, 255.f, 320 | 255.f, 0.f, 255.f, 85.f, 0.f, 255.f 321 | }; 322 | const int pairs_size = sizeof(pairs) / sizeof(unsigned int); 323 | const int number_colors = sizeof(colors) / sizeof(float); 324 | 325 | for (int person = 0; person < keyshape[0]; ++person) 326 | { 327 | // Draw lines 328 | for (int pair = 0u; pair < pairs_size; pair += 2) 329 | { 330 | const int index1 = (person * num_keypoints + pairs[pair]) * keyshape[2]; 331 | const int index2 = (person * num_keypoints + pairs[pair + 1]) * keyshape[2]; 332 | if (keypoints[index1 + 2] > thresh && keypoints[index2 + 2] > thresh) 333 | { 334 | const int color_index = pairs[pair + 1] * 3; 335 | cv::Scalar color { colors[(color_index + 2) % number_colors], 336 | colors[(color_index + 1) % number_colors], 337 | colors[(color_index + 0) % number_colors]}; 338 | cv::Point keypoint1{ intRoundUp(keypoints[index1] * scale), intRoundUp(keypoints[index1 + 1] * scale) }; 339 | cv::Point keypoint2{ intRoundUp(keypoints[index2] * scale), intRoundUp(keypoints[index2 + 1] * scale) }; 340 | cv::line(frame, keypoint1, keypoint2, color, 2); 341 | } 342 | } 343 | // Draw circles 344 | for (int part = 0; part < num_keypoints; ++part) 345 | { 346 | const int index = (person * num_keypoints + part) * keyshape[2]; 347 | if (keypoints[index + 2] > thresh) 348 | { 349 | const int color_index = part * 3; 350 | cv::Scalar color { colors[(color_index + 2) % number_colors], 351 | colors[(color_index + 1) % number_colors], 352 | colors[(color_index + 0) % number_colors]}; 353 | cv::Point center{ intRoundUp(keypoints[index] * scale), intRoundUp(keypoints[index + 1] * scale) }; 354 | cv::circle(frame, center, 3, color, -1); 355 | } 356 | } 357 | } 358 | } 359 | -------------------------------------------------------------------------------- /server/src/people.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __PEOPLE 2 | #define __PEOPLE 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | template 17 | inline int intRoundUp(const T a) 18 | { 19 | return int(a+0.5f); 20 | } 21 | 22 | class Person 23 | { 24 | private: 25 | enum { 26 | UNTRACK = -1, 27 | OVERLAP_NUM = 6, 28 | HIS_NUM = 33, 29 | CHANGE_NUM = 8, 30 | 31 | // action 32 | ACTION_TYPE_NUM = 4, ACTION_HIS_NUM = 10, 33 | STAND = 0, WALK = 1, PUNCH = 2, KICK = 3, UNKNOWN = 4, 34 | 35 | // coco joint keypoint 36 | JOINT_NUM = 18, 37 | NOSE = 0, NECK = 1, RSHOULDER = 2, RELBOW = 3, RWRIST = 4, LSHOULDER = 5, LELBOW = 6, 38 | LWRIST = 7, RHIP = 8, RKNEE = 9, RANKLE = 10, LHIP = 11, LKNEE = 12, LANKLE = 13, 39 | REYE = 14, LEYE = 15, REAR = 16, LEAR = 17 40 | }; 41 | struct Joint { 42 | float x[JOINT_NUM]; 43 | float y[JOINT_NUM]; 44 | }; 45 | 46 | struct Change { 47 | float dist[CHANGE_NUM]; 48 | float deg[CHANGE_NUM]; 49 | float cur_deg[CHANGE_NUM]; 50 | }; 51 | 52 | std::deque history; 53 | std::deque change_history; 54 | std::deque actions; 55 | 56 | int overlap_count; 57 | int track_id; 58 | int action; 59 | 60 | // for bounding box 61 | float max_x; 62 | float max_y; 63 | float min_x; 64 | float min_y; 65 | 66 | Person* enemy; 67 | 68 | public: 69 | Person() : enemy(nullptr), overlap_count(0), track_id(UNTRACK), action(ACTION_TYPE_NUM), max_x(FLT_MIN), max_y(FLT_MIN), min_x(FLT_MAX), min_y(FLT_MAX) { history.assign(1, {0}); } 70 | 71 | ~Person() { if (enemy != nullptr) enemy->set_enemy(nullptr); } 72 | // set 73 | void set_part(int part, float x, float y); 74 | void set_id(int id) { track_id = id; } 75 | void set_action(int type); 76 | void set_enemy(Person* p) { enemy = p; } 77 | void set_rect(cv::Rect_& rect); 78 | 79 | // get 80 | inline int get_id(void) const { return track_id; } 81 | inline int get_action(void) const { return action; } 82 | inline const char* get_action_str(void) const { 83 | static const char* action_str[] = {"STAND", "WALK", "PUNCH", "KICK", "UNKNOWN"}; 84 | return action_str[action]; 85 | } 86 | inline const Person* get_enemy(void) const { return enemy; } 87 | cv::Rect_ get_rect(void) const; 88 | cv::Rect_ get_crash_rect(const Person& p) const; 89 | 90 | // crash 91 | inline bool is_danger(void) const { return (action == PUNCH || action == KICK); } 92 | bool check_crash(const Person& p) const; 93 | 94 | void update(Person* n_p); 95 | bool has_output(void); 96 | std::string get_history(void) const; 97 | 98 | // util 99 | float get_dist(float x1, float y1, float x2, float y2); 100 | float get_deg(float x1, float y1, float x2, float y2); 101 | 102 | Person& operator=(const Person& p); 103 | friend std::ostream& operator<<(std::ostream &out, const Person &p); 104 | }; 105 | 106 | class People 107 | { 108 | public: 109 | friend class boost::serialization::access; 110 | const float thresh = 0.05; 111 | std::vector keypoints; 112 | std::vector keyshape; 113 | float scale; 114 | 115 | People() {} 116 | 117 | People(std::vector _keypoints, std::vector _keyshape, float _scale) : 118 | keypoints(_keypoints), keyshape(_keyshape), scale(_scale) {} 119 | 120 | inline int get_person_num(void) const { return keyshape[0]; }; 121 | 122 | std::vector to_person(void); 123 | std::string get_output(void); 124 | void render_pose_keypoints(cv::Mat& frame); 125 | 126 | template 127 | void serialize(Archive & ar, const unsigned int version) 128 | { 129 | ar & keypoints; 130 | ar & keyshape; 131 | ar & scale; 132 | } 133 | }; 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /server/src/pose_detector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | #include 5 | #include 6 | #include 7 | using namespace cv; 8 | #include "pose_detector.hpp" 9 | 10 | #define POSE_MAX_PEOPLE 96 11 | #define NET_OUT_CHANNELS 57 // 38 for pafs, 19 for parts 12 | 13 | template 14 | inline int intRound(const T a) 15 | { 16 | return int(a+0.5f); 17 | } 18 | 19 | template 20 | inline T fastMin(const T a, const T b) 21 | { 22 | return (a < b ? a : b); 23 | } 24 | 25 | void PoseDetector::connect_bodyparts 26 | ( 27 | vector& pose_keypoints, 28 | const float* const map, 29 | const float* const peaks, 30 | int mapw, 31 | int maph, 32 | const int inter_min_above_th, 33 | const float inter_th, 34 | const int min_subset_cnt, 35 | const float min_subset_score, 36 | vector& keypoint_shape 37 | ) 38 | { 39 | keypoint_shape.resize(3); 40 | const int body_part_pairs[] = 41 | { 42 | 1, 2, 1, 5, 2, 3, 3, 4, 5, 6, 6, 7, 1, 8, 8, 9, 9, 10, 1, 11, 11, 43 | 12, 12, 13, 1, 0, 0, 14, 14, 16, 0, 15, 15, 17, 2, 16, 5, 17 44 | }; 45 | const int limb_idx[] = 46 | { 47 | 31, 32, 39, 40, 33, 34, 35, 36, 41, 42, 43, 44, 19, 20, 21, 22, 23, 24, 25, 48 | 26, 27, 28, 29, 30, 47, 48, 49, 50, 53, 54, 51, 52, 55, 56, 37, 38, 45, 46 49 | }; 50 | const int num_body_parts = 18; // COCO part number 51 | const int num_body_part_pairs = num_body_parts + 1; 52 | std::vector, double>> subset; 53 | const int subset_counter_index = num_body_parts; 54 | const int subset_size = num_body_parts + 1; 55 | const int peaks_offset = 3 * (POSE_MAX_PEOPLE + 1); 56 | const int map_offset = mapw * maph; 57 | 58 | for (unsigned int pair_index = 0u; pair_index < num_body_part_pairs; ++pair_index) 59 | { 60 | const int body_partA = body_part_pairs[2 * pair_index]; 61 | const int body_partB = body_part_pairs[2 * pair_index + 1]; 62 | const float* candidateA = peaks + body_partA*peaks_offset; 63 | const float* candidateB = peaks + body_partB*peaks_offset; 64 | const int nA = (int)(candidateA[0]); // number of part A candidates 65 | const int nB = (int)(candidateB[0]); // number of part B candidates 66 | 67 | // add parts into the subset in special case 68 | if (nA == 0 || nB == 0) 69 | { 70 | // Change w.r.t. other 71 | if (nA == 0) // nB == 0 or not 72 | { 73 | for (int i = 1; i <= nB; ++i) 74 | { 75 | bool num = false; 76 | for (unsigned int j = 0u; j < subset.size(); ++j) 77 | { 78 | const int off = body_partB*peaks_offset + i * 3 + 2; 79 | if (subset[j].first[body_partB] == off) 80 | { 81 | num = true; 82 | break; 83 | } 84 | } 85 | if (!num) 86 | { 87 | std::vector row_vector(subset_size, 0); 88 | // store the index 89 | row_vector[body_partB] = body_partB*peaks_offset + i * 3 + 2; 90 | // the parts number of that person 91 | row_vector[subset_counter_index] = 1; 92 | // total score 93 | const float subsetScore = candidateB[i * 3 + 2]; 94 | subset.emplace_back(std::make_pair(row_vector, subsetScore)); 95 | } 96 | } 97 | } 98 | else // if (nA != 0 && nB == 0) 99 | { 100 | for (int i = 1; i <= nA; i++) 101 | { 102 | bool num = false; 103 | for (unsigned int j = 0u; j < subset.size(); ++j) 104 | { 105 | const int off = body_partA*peaks_offset + i * 3 + 2; 106 | if (subset[j].first[body_partA] == off) 107 | { 108 | num = true; 109 | break; 110 | } 111 | } 112 | if (!num) 113 | { 114 | std::vector row_vector(subset_size, 0); 115 | // store the index 116 | row_vector[body_partA] = body_partA*peaks_offset + i * 3 + 2; 117 | // parts number of that person 118 | row_vector[subset_counter_index] = 1; 119 | // total score 120 | const float subsetScore = candidateA[i * 3 + 2]; 121 | subset.emplace_back(std::make_pair(row_vector, subsetScore)); 122 | } 123 | } 124 | } 125 | } 126 | else // if (nA != 0 && nB != 0) 127 | { 128 | std::vector> temp; 129 | const int num_inter = 10; 130 | // limb PAF x-direction heatmap 131 | const float* const mapX = map + limb_idx[2 * pair_index] * map_offset; 132 | // limb PAF y-direction heatmap 133 | const float* const mapY = map + limb_idx[2 * pair_index + 1] * map_offset; 134 | // start greedy algorithm 135 | for (int i = 1; i <= nA; i++) 136 | { 137 | for (int j = 1; j <= nB; j++) 138 | { 139 | const int dX = candidateB[j * 3] - candidateA[i * 3]; 140 | const int dY = candidateB[j * 3 + 1] - candidateA[i * 3 + 1]; 141 | const float norm_vec = float(std::sqrt(dX*dX + dY*dY)); 142 | // If the peaksPtr are coincident. Don't connect them. 143 | if (norm_vec > 1e-6) 144 | { 145 | const float sX = candidateA[i * 3]; 146 | const float sY = candidateA[i * 3 + 1]; 147 | const float vecX = dX / norm_vec; 148 | const float vecY = dY / norm_vec; 149 | float sum = 0.; 150 | int count = 0; 151 | for (int lm = 0; lm < num_inter; lm++) 152 | { 153 | const int mX = fastMin(mapw - 1, intRound(sX + lm*dX / num_inter)); 154 | const int mY = fastMin(maph - 1, intRound(sY + lm*dY / num_inter)); 155 | const int idx = mY * mapw + mX; 156 | const float score = (vecX*mapX[idx] + vecY*mapY[idx]); 157 | if (score > inter_th) 158 | { 159 | sum += score; 160 | ++count; 161 | } 162 | } 163 | 164 | // parts score + connection score 165 | if (count > inter_min_above_th) 166 | { 167 | temp.emplace_back(std::make_tuple(sum / count, i, j)); 168 | } 169 | } 170 | } 171 | } 172 | // select the top minAB connection, assuming that each part occur only once 173 | // sort rows in descending order based on parts + connection score 174 | if (!temp.empty()) 175 | { 176 | std::sort(temp.begin(), temp.end(), std::greater>()); 177 | } 178 | std::vector> connectionK; 179 | 180 | const int minAB = fastMin(nA, nB); 181 | // assuming that each part occur only once, filter out same part1 to different part2 182 | std::vector occurA(nA, 0); 183 | std::vector occurB(nB, 0); 184 | int counter = 0; 185 | for (unsigned int row = 0u; row < temp.size(); row++) 186 | { 187 | const float score = std::get<0>(temp[row]); 188 | const int aidx = std::get<1>(temp[row]); 189 | const int bidx = std::get<2>(temp[row]); 190 | if (!occurA[aidx - 1] && !occurB[bidx - 1]) 191 | { 192 | // save two part score "position" and limb mean PAF score 193 | connectionK.emplace_back(std::make_tuple(body_partA*peaks_offset + aidx * 3 + 2, 194 | body_partB*peaks_offset + bidx * 3 + 2, score)); 195 | ++counter; 196 | if (counter == minAB) 197 | { 198 | break; 199 | } 200 | occurA[aidx - 1] = 1; 201 | occurB[bidx - 1] = 1; 202 | } 203 | } 204 | // Cluster all the body part candidates into subset based on the part connection 205 | // initialize first body part connection 206 | if (pair_index == 0) 207 | { 208 | for (const auto connectionKI : connectionK) 209 | { 210 | std::vector row_vector(num_body_parts + 3, 0); 211 | const int indexA = std::get<0>(connectionKI); 212 | const int indexB = std::get<1>(connectionKI); 213 | const double score = std::get<2>(connectionKI); 214 | row_vector[body_part_pairs[0]] = indexA; 215 | row_vector[body_part_pairs[1]] = indexB; 216 | row_vector[subset_counter_index] = 2; 217 | // add the score of parts and the connection 218 | const double subset_score = peaks[indexA] + peaks[indexB] + score; 219 | subset.emplace_back(std::make_pair(row_vector, subset_score)); 220 | } 221 | } 222 | // Add ears connections (in case person is looking to opposite direction to camera) 223 | else if (pair_index == 17 || pair_index == 18) 224 | { 225 | for (const auto& connectionKI : connectionK) 226 | { 227 | const int indexA = std::get<0>(connectionKI); 228 | const int indexB = std::get<1>(connectionKI); 229 | for (auto& subsetJ : subset) 230 | { 231 | auto& subsetJ_first = subsetJ.first[body_partA]; 232 | auto& subsetJ_first_plus1 = subsetJ.first[body_partB]; 233 | if (subsetJ_first == indexA && subsetJ_first_plus1 == 0) 234 | { 235 | subsetJ_first_plus1 = indexB; 236 | } 237 | else if (subsetJ_first_plus1 == indexB && subsetJ_first == 0) 238 | { 239 | subsetJ_first = indexA; 240 | } 241 | } 242 | } 243 | } 244 | else 245 | { 246 | if (!connectionK.empty()) 247 | { 248 | for (unsigned int i = 0u; i < connectionK.size(); ++i) 249 | { 250 | const int indexA = std::get<0>(connectionK[i]); 251 | const int indexB = std::get<1>(connectionK[i]); 252 | const double score = std::get<2>(connectionK[i]); 253 | int num = 0; 254 | // if A is already in the subset, add B 255 | for (unsigned int j = 0u; j < subset.size(); j++) 256 | { 257 | if (subset[j].first[body_partA] == indexA) 258 | { 259 | subset[j].first[body_partB] = indexB; 260 | ++num; 261 | subset[j].first[subset_counter_index] = subset[j].first[subset_counter_index] + 1; 262 | subset[j].second = subset[j].second + peaks[indexB] + score; 263 | } 264 | } 265 | // if A is not found in the subset, create new one and add both 266 | if (num == 0) 267 | { 268 | std::vector row_vector(subset_size, 0); 269 | row_vector[body_partA] = indexA; 270 | row_vector[body_partB] = indexB; 271 | row_vector[subset_counter_index] = 2; 272 | const float subsetScore = peaks[indexA] + peaks[indexB] + score; 273 | subset.emplace_back(std::make_pair(row_vector, subsetScore)); 274 | } 275 | } 276 | } 277 | } 278 | } 279 | } 280 | 281 | // Delete people below thresholds, and save to output 282 | int number_people = 0; 283 | std::vector valid_subset_indexes; 284 | valid_subset_indexes.reserve(fastMin((size_t)POSE_MAX_PEOPLE, subset.size())); 285 | for (unsigned int index = 0; index < subset.size(); ++index) 286 | { 287 | const int subset_counter = subset[index].first[subset_counter_index]; 288 | const double subset_score = subset[index].second; 289 | if (subset_counter >= min_subset_cnt && (subset_score / subset_counter) > min_subset_score) 290 | { 291 | ++number_people; 292 | valid_subset_indexes.emplace_back(index); 293 | if (number_people == POSE_MAX_PEOPLE) 294 | { 295 | break; 296 | } 297 | } 298 | } 299 | 300 | // Fill and return pose_keypoints 301 | keypoint_shape = { number_people, (int)num_body_parts, 3 }; 302 | if (number_people > 0) 303 | { 304 | pose_keypoints.resize(number_people * (int)num_body_parts * 3); 305 | } 306 | else 307 | { 308 | pose_keypoints.clear(); 309 | } 310 | for (unsigned int person = 0u; person < valid_subset_indexes.size(); ++person) 311 | { 312 | const auto& subsetI = subset[valid_subset_indexes[person]].first; 313 | for (int bodyPart = 0u; bodyPart < num_body_parts; bodyPart++) 314 | { 315 | const int base_offset = (person*num_body_parts + bodyPart) * 3; 316 | const int body_part_index = subsetI[bodyPart]; 317 | if (body_part_index > 0) 318 | { 319 | pose_keypoints[base_offset] = peaks[body_part_index - 2]; 320 | pose_keypoints[base_offset + 1] = peaks[body_part_index - 1]; 321 | pose_keypoints[base_offset + 2] = peaks[body_part_index]; 322 | } 323 | else 324 | { 325 | pose_keypoints[base_offset] = 0.f; 326 | pose_keypoints[base_offset + 1] = 0.f; 327 | pose_keypoints[base_offset + 2] = 0.f; 328 | } 329 | } 330 | } 331 | } 332 | 333 | void PoseDetector::find_heatmap_peaks 334 | ( 335 | const float *src, 336 | float *dst, 337 | const int SRCW, 338 | const int SRCH, 339 | const int SRC_CH, 340 | const float TH 341 | ) 342 | { 343 | // find peaks (8-connected neighbor), weights with 7 by 7 area to get sub-pixel location and response 344 | const int SRC_PLANE_OFFSET = SRCW * SRCH; 345 | // add 1 for saving total people count, 3 for x, y, score 346 | const int DST_PLANE_OFFSET = (POSE_MAX_PEOPLE + 1) * 3; 347 | float *dstptr = dst; 348 | int c = 0; 349 | int x = 0; 350 | int y = 0; 351 | int i = 0; 352 | int j = 0; 353 | // TODO: reduce multiplication by using pointer 354 | for(c = 0; c < SRC_CH - 1; ++c) 355 | { 356 | int num_people = 0; 357 | for(y = 1; y < SRCH - 1 && num_people != POSE_MAX_PEOPLE; ++y) 358 | { 359 | for(x = 1; x < SRCW - 1 && num_people != POSE_MAX_PEOPLE; ++x) 360 | { 361 | int idx = y * SRCW + x; 362 | float value = src[idx]; 363 | if (value > TH) 364 | { 365 | const float TOPLEFT = src[idx - SRCW - 1]; 366 | const float TOP = src[idx - SRCW]; 367 | const float TOPRIGHT = src[idx - SRCW + 1]; 368 | const float LEFT = src[idx - 1]; 369 | const float RIGHT = src[idx + 1]; 370 | const float BUTTOMLEFT = src[idx + SRCW - 1]; 371 | const float BUTTOM = src[idx + SRCW]; 372 | const float BUTTOMRIGHT = src[idx + SRCW + 1]; 373 | if(value > TOPLEFT && value > TOP && value > TOPRIGHT && value > LEFT && 374 | value > RIGHT && value > BUTTOMLEFT && value > BUTTOM && value > BUTTOMRIGHT) 375 | { 376 | float x_acc = 0; 377 | float y_acc = 0; 378 | float score_acc = 0; 379 | for (i = -3; i <= 3; ++i) 380 | { 381 | int ux = x + i; 382 | if (ux >= 0 && ux < SRCW) 383 | { 384 | for (j = -3; j <= 3; ++j) 385 | { 386 | int uy = y + j; 387 | if (uy >= 0 && uy < SRCH) 388 | { 389 | float score = src[uy * SRCW + ux]; 390 | x_acc += ux * score; 391 | y_acc += uy * score; 392 | score_acc += score; 393 | } 394 | } 395 | } 396 | } 397 | x_acc /= score_acc; 398 | y_acc /= score_acc; 399 | score_acc = value; 400 | dstptr[(num_people + 1) * 3 + 0] = x_acc; 401 | dstptr[(num_people + 1) * 3 + 1] = y_acc; 402 | dstptr[(num_people + 1) * 3 + 2] = score_acc; 403 | ++num_people; 404 | } 405 | } 406 | } 407 | } 408 | dstptr[0] = num_people; 409 | src += SRC_PLANE_OFFSET; 410 | dstptr += DST_PLANE_OFFSET; 411 | } 412 | } 413 | 414 | Mat PoseDetector::create_netsize_im 415 | ( 416 | const Mat &im, 417 | const int netw, 418 | const int neth, 419 | float *scale 420 | ) 421 | { 422 | // for tall image 423 | int newh = neth; 424 | float s = newh / (float)im.rows; 425 | int neww = im.cols * s; 426 | if (neww > netw) 427 | { 428 | //for fat image 429 | neww = netw; 430 | s = neww / (float)im.cols; 431 | newh = im.rows * s; 432 | } 433 | 434 | *scale = 1 / s; 435 | Rect dst_area(0, 0, neww, newh); 436 | Mat dst = Mat::zeros(neth, netw, CV_8UC3); 437 | resize(im, dst(dst_area), Size(neww, newh)); 438 | return dst; 439 | } 440 | 441 | PoseDetector::PoseDetector(const char *cfg_path, const char *weight_path, int gpu_id) : Detector(cfg_path, weight_path, 442 | gpu_id) { 443 | det_people = nullptr; 444 | // initialize net 445 | net_inw = get_net_width(); 446 | net_inh = get_net_height(); 447 | net_outw = get_net_out_width(); 448 | net_outh = get_net_out_height(); 449 | } 450 | 451 | void PoseDetector::detect(cv::Mat im, float thresh) { 452 | // 3. resize to net input size, put scaled image on the top left 453 | float scale = 0.0f; 454 | Mat netim = create_netsize_im(im, net_inw, net_inh, &scale); 455 | 456 | // 4. normalized to float type 457 | netim.convertTo(netim, CV_32F, 1 / 256.f, -0.5); 458 | 459 | // 5. split channels 460 | float *netin_data = new float[net_inw * net_inh * 3](); 461 | float *netin_data_ptr = netin_data; 462 | vector input_channels; 463 | for (int i = 0; i < 3; ++i) 464 | { 465 | Mat channel(net_inh, net_inw, CV_32FC1, netin_data_ptr); 466 | input_channels.emplace_back(channel); 467 | netin_data_ptr += (net_inw * net_inh); 468 | } 469 | split(netim, input_channels); 470 | 471 | // 6. feed forward 472 | double time_begin = getTickCount(); 473 | float *netoutdata = Detector::predict(netin_data); 474 | double fee_time = (getTickCount() - time_begin) / getTickFrequency() * 1000; 475 | #ifdef DEBUG 476 | cout << "forward fee: " << fee_time << "ms" << endl; 477 | #endif 478 | // 7. resize net output back to input size to get heatmap 479 | float *heatmap = new float[net_inw * net_inh * NET_OUT_CHANNELS]; 480 | for (int i = 0; i < NET_OUT_CHANNELS; ++i) 481 | { 482 | Mat netout(net_outh, net_outw, CV_32F, (netoutdata + net_outh*net_outw*i)); 483 | Mat nmsin(net_inh, net_inw, CV_32F, heatmap + net_inh*net_inw*i); 484 | resize(netout, nmsin, Size(net_inw, net_inh), 0, 0, CV_INTER_CUBIC); 485 | } 486 | 487 | // 8. get heatmap peaks 488 | float *heatmap_peaks = new float[3 * (POSE_MAX_PEOPLE+1) * (NET_OUT_CHANNELS-1)]; 489 | find_heatmap_peaks(heatmap, heatmap_peaks, net_inw, net_inh, NET_OUT_CHANNELS, 0.05); 490 | 491 | // 9. link parts 492 | vector keypoints; 493 | vector shape; 494 | connect_bodyparts(keypoints, heatmap, heatmap_peaks, net_inw, net_inh, 9, 0.05, 6, 0.4, shape); 495 | 496 | delete [] heatmap_peaks; 497 | delete [] heatmap; 498 | delete [] netin_data; 499 | 500 | // people 501 | if (det_people != nullptr) 502 | delete det_people; 503 | det_people = new People(keypoints, shape, scale); 504 | } 505 | 506 | void PoseDetector::draw(cv::Mat mat) 507 | { 508 | det_people->render_pose_keypoints(mat); 509 | } 510 | 511 | std::string PoseDetector::det_to_json(int frame_id) 512 | { 513 | std::string out_str; 514 | char* tmp_buf = (char *)calloc(1024, sizeof(char)); 515 | sprintf(tmp_buf, "{\n \"frame_id\":%d, \n ", frame_id); 516 | out_str = tmp_buf; 517 | out_str += det_people->get_output(); 518 | out_str += "\n}"; 519 | free(tmp_buf); 520 | return out_str; 521 | } 522 | 523 | PoseDetector::~PoseDetector() { 524 | } 525 | -------------------------------------------------------------------------------- /server/src/pose_detector.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __POSE_DETECTOR 2 | #define __POSE_DETECTOR 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "yolo_v2_class.hpp" 9 | #include "DetectorInterface.hpp" 10 | #include "people.hpp" 11 | 12 | class PoseDetector : public Detector, public DetectorInterface 13 | { 14 | private: 15 | int net_inw; 16 | int net_inh; 17 | int net_outw; 18 | int net_outh; 19 | People* det_people; 20 | public: 21 | PoseDetector(const char *cfg_path, const char *weight_path, int gpu_id); 22 | ~PoseDetector(); 23 | inline People* get_people(void) { return det_people; } 24 | virtual void detect(cv::Mat mat, float thresh); 25 | virtual void draw(cv::Mat mat); 26 | virtual std::string det_to_json(int frame_id); 27 | private: 28 | void connect_bodyparts 29 | ( 30 | std::vector& pose_keypoints, 31 | const float* const map, 32 | const float* const peaks, 33 | int mapw, 34 | int maph, 35 | const int inter_min_above_th, 36 | const float inter_th, 37 | const int min_subset_cnt, 38 | const float min_subset_score, 39 | std::vector& keypoint_shape 40 | ); 41 | 42 | void find_heatmap_peaks 43 | ( 44 | const float *src, 45 | float *dst, 46 | const int SRCW, 47 | const int SRCH, 48 | const int SRC_CH, 49 | const float TH 50 | ); 51 | 52 | cv::Mat create_netsize_im 53 | ( 54 | const cv::Mat &im, 55 | const int netw, 56 | const int neth, 57 | float *scale 58 | ); 59 | }; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /server/src/share_queue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // https://stackoverflow.com/questions/36762248/why-is-stdqueue-not-thread-safe 3 | #include 4 | #include 5 | #include 6 | 7 | template 8 | class SharedQueue 9 | { 10 | public: 11 | SharedQueue(); 12 | ~SharedQueue(); 13 | 14 | T& front(); 15 | void pop_front(); 16 | 17 | void push_back(const T& item); 18 | void push_back(T&& item); 19 | 20 | int size(); 21 | bool empty(); 22 | 23 | private: 24 | std::deque queue_; 25 | std::mutex mutex_; 26 | std::condition_variable cond_; 27 | }; 28 | 29 | template 30 | SharedQueue::SharedQueue() {} 31 | 32 | template 33 | SharedQueue::~SharedQueue() {} 34 | 35 | template 36 | T& SharedQueue::front() 37 | { 38 | std::unique_lock mlock(mutex_); 39 | while (queue_.empty()) 40 | { 41 | cond_.wait(mlock); 42 | } 43 | return queue_.front(); 44 | } 45 | 46 | template 47 | void SharedQueue::pop_front() 48 | { 49 | std::unique_lock mlock(mutex_); 50 | while (queue_.empty()) 51 | { 52 | cond_.wait(mlock); 53 | } 54 | queue_.pop_front(); 55 | } 56 | 57 | template 58 | void SharedQueue::push_back(const T& item) 59 | { 60 | std::unique_lock mlock(mutex_); 61 | queue_.push_back(item); 62 | mlock.unlock(); // unlock before notificiation to minimize mutex con 63 | cond_.notify_one(); // notify one waiting thread 64 | 65 | } 66 | 67 | template 68 | void SharedQueue::push_back(T&& item) 69 | { 70 | std::unique_lock mlock(mutex_); 71 | queue_.push_back(std::move(item)); 72 | mlock.unlock(); // unlock before notificiation to minimize mutex con 73 | cond_.notify_one(); // notify one waiting thread 74 | 75 | } 76 | 77 | template 78 | int SharedQueue::size() 79 | { 80 | std::unique_lock mlock(mutex_); 81 | int size = queue_.size(); 82 | mlock.unlock(); 83 | return size; 84 | } 85 | -------------------------------------------------------------------------------- /server/src/sink.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "share_queue.h" 14 | #include "people.hpp" 15 | #include "Tracker.hpp" 16 | #include "frame.hpp" 17 | 18 | // ZMQ 19 | void *sock_pull; 20 | void *sock_pub; 21 | void *sock_rnn; 22 | 23 | // ShareQueue 24 | tbb::concurrent_hash_map frame_map; 25 | SharedQueue processed_frame_queue; 26 | 27 | // pool 28 | Frame_pool *frame_pool; 29 | 30 | // signal 31 | volatile bool exit_flag = false; 32 | void sig_handler(int s) 33 | { 34 | exit_flag = true; 35 | } 36 | 37 | void *recv_in_thread(void *ptr) 38 | { 39 | int recv_json_len; 40 | unsigned char json_buf[JSON_BUF_LEN]; 41 | Frame frame; 42 | 43 | while(!exit_flag) { 44 | recv_json_len = zmq_recv(sock_pull, json_buf, JSON_BUF_LEN, ZMQ_NOBLOCK); 45 | 46 | if (recv_json_len > 0) { 47 | frame = frame_pool->alloc_frame(); 48 | json_buf[recv_json_len] = '\0'; 49 | json_to_frame(json_buf, frame); 50 | 51 | #ifdef DEBUG 52 | std::cout << "Sink | Recv From Worker | SEQ : " << frame.seq_buf 53 | << " LEN : " << frame.msg_len << std::endl; 54 | #endif 55 | 56 | tbb::concurrent_hash_map::accessor a; 57 | while(1) 58 | { 59 | if(frame_map.insert(a, atoi((char *)frame.seq_buf))) { 60 | a->second = frame; 61 | break; 62 | } 63 | } 64 | } 65 | } 66 | } 67 | 68 | void *send_in_thread(void *ptr) 69 | { 70 | int send_json_len; 71 | unsigned char json_buf[JSON_BUF_LEN]; 72 | Frame frame; 73 | 74 | while(!exit_flag) { 75 | if (processed_frame_queue.size() > 0) { 76 | frame = processed_frame_queue.front(); 77 | processed_frame_queue.pop_front(); 78 | 79 | #ifdef DEBUG 80 | std::cout << "Sink | Pub To Client | SEQ : " << frame.seq_buf 81 | << " LEN : " << frame.msg_len << std::endl; 82 | #endif 83 | 84 | send_json_len = frame_to_json(json_buf, frame); 85 | zmq_send(sock_pub, json_buf, send_json_len, 0); 86 | 87 | frame_pool->free_frame(frame); 88 | } 89 | } 90 | } 91 | 92 | int main() 93 | { 94 | // ZMQ 95 | int ret; 96 | void *context = zmq_ctx_new(); 97 | 98 | sock_pull = zmq_socket(context, ZMQ_PULL); 99 | ret = zmq_bind(sock_pull, "ipc://processed"); 100 | assert(ret != -1); 101 | 102 | sock_pub = zmq_socket(context, ZMQ_PUB); 103 | ret = zmq_bind(sock_pub, "tcp://*:5570"); 104 | assert(ret != -1); 105 | 106 | sock_rnn = zmq_socket(context, ZMQ_REQ); 107 | ret = zmq_connect(sock_rnn, "ipc://action"); 108 | assert(ret != -1); 109 | 110 | // frame_pool 111 | frame_pool = new Frame_pool(5000); 112 | 113 | // Thread 114 | pthread_t recv_thread; 115 | if (pthread_create(&recv_thread, 0, recv_in_thread, 0)) 116 | std::cerr << "Thread creation failed (recv_thread)" << std::endl; 117 | 118 | pthread_t send_thread; 119 | if (pthread_create(&send_thread, 0, send_in_thread, 0)) 120 | std::cerr << "Thread creation failed (recv_thread)" << std::endl; 121 | 122 | pthread_detach(send_thread); 123 | pthread_detach(recv_thread); 124 | 125 | // serialize 126 | std::stringstream ss; 127 | std::stringbuf *pbuf = ss.rdbuf(); 128 | 129 | // frame 130 | Frame frame; 131 | int frame_len; 132 | unsigned char *frame_buf_ptr; 133 | char json_tmp_buf[1024]; 134 | 135 | // Tracker 136 | Tracker tracker; 137 | TrackingBox tb; 138 | std::vector det_data; 139 | std::vector track_data; 140 | volatile int track_frame = 1; 141 | int init_flag = 0; 142 | 143 | // person 144 | std::vector person_data; 145 | std::stringstream p_ss; 146 | std::ofstream output_file("output.txt"); 147 | 148 | // rnn 149 | unsigned char rnn_buf[100]; 150 | 151 | // draw 152 | std::vector param = {cv::IMWRITE_JPEG_QUALITY, 60 }; 153 | const int CNUM = 20; 154 | cv::RNG rng(0xFFFFFFFF); 155 | cv::Scalar_ randColor[CNUM]; 156 | for (int i = 0; i < CNUM; i++) 157 | rng.fill(randColor[i], cv::RNG::UNIFORM, 0, 256); 158 | 159 | while(!exit_flag) { 160 | if (!frame_map.empty()) { 161 | tbb::concurrent_hash_map::accessor c_a; 162 | 163 | if (frame_map.find(c_a, (const int)track_frame)) 164 | { 165 | frame = (Frame)c_a->second; 166 | while(1) { 167 | if (frame_map.erase(c_a)) 168 | break; 169 | } 170 | 171 | // unsigned char array -> vector 172 | frame_len = frame.msg_len; 173 | frame_buf_ptr = frame.msg_buf; 174 | std::vector raw_vec(frame_buf_ptr, frame_buf_ptr + frame_len); 175 | 176 | // vector -> mat 177 | cv::Mat raw_mat = cv::imdecode(cv::Mat(raw_vec), 1); 178 | 179 | // get people & unserialize 180 | ss.str(""); // ss clear 181 | pbuf->sputn((const char*)frame.det_buf, frame.det_len); 182 | People people; 183 | { 184 | boost::archive::text_iarchive ia(ss); 185 | ia >> people; 186 | } 187 | 188 | // detect people result to json 189 | std::string det_json; 190 | sprintf(json_tmp_buf, "{\n \"frame_id\":%d, \n ", track_frame); 191 | det_json = json_tmp_buf; 192 | det_json += people.get_output(); 193 | det_json += "\n}"; 194 | 195 | frame.det_len = det_json.size(); 196 | memcpy(frame.det_buf, det_json.c_str(), frame.det_len); 197 | frame.det_buf[frame.det_len] = '\0'; 198 | 199 | // draw people skeleton 200 | people.render_pose_keypoints(raw_mat); 201 | 202 | // people to person 203 | person_data.clear(); 204 | person_data = people.to_person(); 205 | 206 | // ready to track 207 | det_data.clear(); 208 | for (auto it = person_data.begin(); it != person_data.end(); it++) { 209 | tb.frame = track_frame; 210 | tb.box = (*it)->get_rect(); 211 | tb.p = (*it); 212 | det_data.push_back(tb); 213 | } 214 | 215 | // not detect people 216 | if (det_data.size() < 1) { 217 | processed_frame_queue.push_back(frame); 218 | track_frame++; 219 | continue; 220 | } 221 | 222 | // Track 223 | track_data.clear(); 224 | if (init_flag == 0) { 225 | track_data = tracker.init(det_data); 226 | init_flag = 1; 227 | } 228 | else { 229 | track_data = tracker.update(det_data); 230 | } 231 | 232 | p_ss.str(""); 233 | for (unsigned int i = 0; i < track_data.size(); i++) { 234 | // get person 235 | Person* track_person = track_data[i].p; 236 | 237 | // get history 238 | if (i != 0) 239 | p_ss << '\n'; 240 | p_ss << track_person->get_history(); 241 | 242 | // get_output for train 243 | if (track_person->has_output()) { 244 | output_file << *(track_person); 245 | } 246 | } 247 | 248 | 249 | // RNN action 250 | double time_begin = getTickCount(); 251 | 252 | std::string hists = p_ss.str(); 253 | if (hists.size() > 0) { 254 | zmq_send(sock_rnn, hists.c_str(), hists.size(), 0); 255 | zmq_recv(sock_rnn, rnn_buf, 100, 0); 256 | 257 | // action update 258 | for (unsigned int i = 0; i < track_data.size(); i++) { 259 | track_data[i].p->set_action(rnn_buf[i] - '0'); 260 | } 261 | } 262 | 263 | double fee_time = (getTickCount() - time_begin) / getTickFrequency() * 1000; 264 | #ifdef DEBUG 265 | std::cout << "RNN fee: " << fee_time << "ms | T : " << track_frame << std::endl; 266 | #endif 267 | 268 | // check crash 269 | for (unsigned int i = 0; i < track_data.size(); i++) { 270 | Person* me = track_data[i].p; 271 | if (me->is_danger()) { // punch or kick 272 | for (unsigned int j = 0; j < track_data.size(); j++) { 273 | if (j == i) 274 | continue; 275 | Person* other = track_data[j].p; 276 | 277 | // if me and other crash 278 | if (me->check_crash(*other)) { 279 | //std::cout << "CRASH !!! " << me->get_id() << " : " << other->get_id() << std::endl; 280 | me->set_enemy(other); 281 | other->set_enemy(me); 282 | } 283 | } 284 | } 285 | 286 | // draw fight bounding box 287 | const Person *my_enemy = me->get_enemy(); 288 | if (my_enemy != nullptr) { 289 | cv::Rect_ crash_rect = me->get_crash_rect(*my_enemy); 290 | 291 | cv::putText(raw_mat, "FIGHT", cv::Point(crash_rect.x, crash_rect.y), cv::FONT_HERSHEY_DUPLEX, 0.8, cv::Scalar(0, 255, 0), 2); 292 | cv::rectangle(raw_mat, crash_rect, cv::Scalar(0, 255, 0), 2, 8, 0); 293 | } 294 | } 295 | 296 | 297 | // draw Track data 298 | for (auto td: track_data) { 299 | // draw track_id 300 | cv::putText(raw_mat, to_string(td.id), cv::Point(td.box.x, td.box.y), cv::FONT_HERSHEY_DUPLEX, 0.8, randColor[td.id % CNUM], 2); 301 | // draw person bounding box 302 | cv::putText(raw_mat, td.p->get_action_str(), cv::Point(td.box.x, td.box.y + 30), cv::FONT_HERSHEY_DUPLEX, 0.8, 303 | randColor[td.id % CNUM], 2); 304 | cv::rectangle(raw_mat, td.box, randColor[td.id % CNUM], 2, 8, 0); 305 | } 306 | 307 | // draw_mat -> vector 308 | std::vector res_vec; 309 | cv::imencode(".jpg", raw_mat, res_vec, param); 310 | 311 | // vector -> frame array 312 | frame.msg_len = res_vec.size(); 313 | std::copy(res_vec.begin(), res_vec.end(), frame.msg_buf); 314 | 315 | processed_frame_queue.push_back(frame); 316 | track_frame++; 317 | } 318 | } 319 | } 320 | 321 | delete frame_pool; 322 | 323 | output_file.close(); 324 | 325 | zmq_close(sock_pull); 326 | zmq_close(sock_pub); 327 | zmq_close(sock_rnn); 328 | 329 | zmq_ctx_destroy(context); 330 | return 0; 331 | } 332 | -------------------------------------------------------------------------------- /server/src/ventilator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "share_queue.h" 7 | #include "frame.hpp" 8 | 9 | // ZMQ 10 | void *sock_pull; 11 | void *sock_push; 12 | 13 | // ShareQueue 14 | SharedQueue frame_queue; 15 | 16 | // pool 17 | Frame_pool *frame_pool; 18 | 19 | // signal 20 | volatile bool exit_flag = false; 21 | void sig_handler(int s) 22 | { 23 | exit_flag = true; 24 | } 25 | 26 | void *recv_in_thread(void *ptr) 27 | { 28 | int recv_json_len; 29 | unsigned char json_buf[JSON_BUF_LEN]; 30 | Frame frame; 31 | 32 | while(!exit_flag) { 33 | recv_json_len = zmq_recv(sock_pull, json_buf, JSON_BUF_LEN, ZMQ_NOBLOCK); 34 | 35 | if (recv_json_len > 0) { 36 | frame = frame_pool->alloc_frame(); 37 | json_buf[recv_json_len] = '\0'; 38 | json_to_frame(json_buf, frame); 39 | #ifdef DEBUG 40 | std::cout << "Ventilator | Recv From Client | SEQ : " << frame.seq_buf 41 | << " LEN : " << frame.msg_len << std::endl; 42 | #endif 43 | frame_queue.push_back(frame); 44 | } 45 | } 46 | } 47 | 48 | void *send_in_thread(void *ptr) 49 | { 50 | int send_json_len; 51 | unsigned char json_buf[JSON_BUF_LEN]; 52 | Frame frame; 53 | while(!exit_flag) { 54 | if (frame_queue.size() > 0) { 55 | frame = frame_queue.front(); 56 | frame_queue.pop_front(); 57 | 58 | #ifdef DEBUG 59 | std::cout << "Ventilator | Send To Worker | SEQ : " << frame.seq_buf 60 | << " LEN : " << frame.msg_len << std::endl; 61 | #endif 62 | 63 | send_json_len = frame_to_json(json_buf, frame); 64 | zmq_send(sock_push, json_buf, send_json_len, 0); 65 | 66 | frame_pool->free_frame(frame); 67 | } 68 | } 69 | } 70 | int main() 71 | { 72 | // ZMQ 73 | int ret; 74 | void *context = zmq_ctx_new(); 75 | sock_pull = zmq_socket(context, ZMQ_PULL); 76 | ret = zmq_bind(sock_pull, "tcp://*:5575"); 77 | assert(ret != -1); 78 | 79 | sock_push = zmq_socket(context, ZMQ_PUSH); 80 | ret = zmq_bind(sock_push, "ipc://unprocessed"); 81 | assert(ret != -1); 82 | 83 | // frame_pool 84 | frame_pool = new Frame_pool(); 85 | 86 | // Thread 87 | pthread_t recv_thread; 88 | if (pthread_create(&recv_thread, 0, recv_in_thread, 0)) 89 | std::cerr << "Thread creation failed (recv_thread)" << std::endl; 90 | 91 | pthread_t send_thread; 92 | if (pthread_create(&send_thread, 0, send_in_thread, 0)) 93 | std::cerr << "Thread creation failed (recv_thread)" << std::endl; 94 | 95 | pthread_detach(send_thread); 96 | pthread_detach(recv_thread); 97 | 98 | while(!exit_flag); 99 | 100 | delete frame_pool; 101 | zmq_close(sock_pull); 102 | zmq_close(sock_push); 103 | zmq_ctx_destroy(context); 104 | } 105 | -------------------------------------------------------------------------------- /server/src/worker.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "share_queue.h" 13 | #include "frame.hpp" 14 | #include "args.hpp" 15 | #include "pose_detector.hpp" 16 | // opencv 17 | #include // C++ 18 | 19 | // ZMQ 20 | void *sock_pull; 21 | void *sock_push; 22 | 23 | // ShareQueue 24 | SharedQueue unprocessed_frame_queue; 25 | SharedQueue processed_frame_queue;; 26 | 27 | // pool 28 | Frame_pool *frame_pool; 29 | 30 | // signal 31 | volatile bool exit_flag = false; 32 | void sig_handler(int s) 33 | { 34 | exit_flag = true; 35 | } 36 | 37 | void *recv_in_thread(void *ptr) 38 | { 39 | int recv_json_len; 40 | unsigned char json_buf[JSON_BUF_LEN]; 41 | Frame frame; 42 | 43 | while(!exit_flag) { 44 | recv_json_len = zmq_recv(sock_pull, json_buf, JSON_BUF_LEN, ZMQ_NOBLOCK); 45 | 46 | if (recv_json_len > 0) { 47 | frame = frame_pool->alloc_frame(); 48 | json_buf[recv_json_len] = '\0'; 49 | json_to_frame(json_buf, frame); 50 | 51 | #ifdef DEBUG 52 | std::cout << "Worker | Recv From Ventilator | SEQ : " << frame.seq_buf 53 | << " LEN : " << frame.msg_len << std::endl; 54 | #endif 55 | unprocessed_frame_queue.push_back(frame); 56 | } 57 | } 58 | } 59 | 60 | void *send_in_thread(void *ptr) 61 | { 62 | int send_json_len; 63 | unsigned char json_buf[JSON_BUF_LEN]; 64 | Frame frame; 65 | 66 | while(!exit_flag) { 67 | if (processed_frame_queue.size() > 0) { 68 | frame = processed_frame_queue.front(); 69 | processed_frame_queue.pop_front(); 70 | 71 | #ifdef DEBUG 72 | std::cout << "Worker | Send To Sink | SEQ : " << frame.seq_buf 73 | << " LEN : " << frame.msg_len << std::endl; 74 | #endif 75 | send_json_len = frame_to_json(json_buf, frame); 76 | zmq_send(sock_push, json_buf, send_json_len, 0); 77 | 78 | frame_pool->free_frame(frame); 79 | } 80 | } 81 | } 82 | 83 | int main(int argc, char *argv[]) 84 | { 85 | if (argc < 2) { 86 | fprintf(stderr, "usage: %s [-gpu GPU_ID] [-thresh THRESH]\n", argv[0]); 87 | return 0; 88 | } 89 | 90 | const char *cfg_path = argv[1]; 91 | const char *weights_path = argv[2]; 92 | int gpu_id = find_int_arg(argc, argv, "-gpu", 0); 93 | float thresh = find_float_arg(argc, argv, "-thresh", 0.2); 94 | fprintf(stdout, "cfg : %s, weights : %s, gpu-id : %d, thresh : %f\n", 95 | cfg_path, weights_path, gpu_id, thresh); 96 | 97 | // opencv 98 | std::vector param = {cv::IMWRITE_JPEG_QUALITY, 60 }; 99 | 100 | // ZMQ 101 | int ret; 102 | 103 | void *context = zmq_ctx_new(); 104 | 105 | sock_pull = zmq_socket(context, ZMQ_PULL); 106 | ret = zmq_connect(sock_pull, "ipc://unprocessed"); 107 | assert(ret != -1); 108 | 109 | sock_push = zmq_socket(context, ZMQ_PUSH); 110 | ret = zmq_connect(sock_push, "ipc://processed"); 111 | assert(ret != -1); 112 | 113 | // frame_pool 114 | frame_pool = new Frame_pool(5000); 115 | 116 | // Thread 117 | pthread_t recv_thread; 118 | if (pthread_create(&recv_thread, 0, recv_in_thread, 0)) 119 | std::cerr << "Thread creation failed (recv_thread)" << std::endl; 120 | 121 | pthread_t send_thread; 122 | if (pthread_create(&send_thread, 0, send_in_thread, 0)) 123 | std::cerr << "Thread creation failed (recv_thread)" << std::endl; 124 | 125 | pthread_detach(send_thread); 126 | pthread_detach(recv_thread); 127 | 128 | // darkent openpose detector 129 | PoseDetector detector(cfg_path, weights_path, gpu_id); 130 | People* det_people = nullptr; 131 | std::stringstream ss; 132 | 133 | // frame 134 | Frame frame; 135 | int frame_len; 136 | unsigned char *frame_buf_ptr; 137 | 138 | // time 139 | auto time_begin = std::chrono::steady_clock::now(); 140 | auto time_end = std::chrono::steady_clock::now(); 141 | double det_time; 142 | 143 | while(!exit_flag) { 144 | // recv from ventilator 145 | if (unprocessed_frame_queue.size() > 0) { 146 | frame = unprocessed_frame_queue.front(); 147 | unprocessed_frame_queue.pop_front(); 148 | 149 | frame_len = frame.msg_len; 150 | frame_buf_ptr = frame.msg_buf; 151 | 152 | // unsigned char array -> vector 153 | std::vector raw_vec(frame_buf_ptr, frame_buf_ptr + frame_len); 154 | 155 | // vector -> mat 156 | cv::Mat raw_mat = cv::imdecode(cv::Mat(raw_vec), 1); 157 | 158 | // detect pose 159 | time_begin = std::chrono::steady_clock::now(); 160 | detector.detect(raw_mat, thresh); 161 | det_people = detector.get_people(); 162 | time_end = std::chrono::steady_clock::now(); 163 | det_time = std::chrono::duration (time_end - time_begin).count(); 164 | #ifdef DEBUG 165 | std::cout << "Darknet | Detect | SEQ : " << frame.seq_buf << " Time : " << det_time << "ms" << std::endl; 166 | #endif 167 | // det_people & serialize 168 | ss.str(""); // ss clear 169 | { 170 | boost::archive::text_oarchive oa(ss); 171 | oa << *det_people; 172 | } 173 | std::string ser_people = ss.str(); 174 | 175 | frame.det_len = ser_people.size(); 176 | memcpy(frame.det_buf, ser_people.c_str(), frame.det_len); 177 | frame.det_buf[frame.det_len] = '\0'; 178 | 179 | // mat -> vector 180 | std::vector res_vec; 181 | cv::imencode(".jpg", raw_mat, res_vec, param); 182 | 183 | // vector -> frame array 184 | frame.msg_len = res_vec.size(); 185 | std::copy(res_vec.begin(), res_vec.end(), frame.msg_buf); 186 | 187 | // push to processed frame_queue 188 | processed_frame_queue.push_back(frame); 189 | } 190 | } 191 | 192 | delete frame_pool; 193 | zmq_close(sock_pull); 194 | zmq_close(sock_push); 195 | zmq_ctx_destroy(context); 196 | 197 | return 0; 198 | } 199 | -------------------------------------------------------------------------------- /server/train/train.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imsoo/fight_detection/f6a435ce03bda03d1f722058c1fce44b231da254/server/train/train.txt -------------------------------------------------------------------------------- /server/weights/action.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imsoo/fight_detection/f6a435ce03bda03d1f722058c1fce44b231da254/server/weights/action.h5 -------------------------------------------------------------------------------- /server/weights/action2.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imsoo/fight_detection/f6a435ce03bda03d1f722058c1fce44b231da254/server/weights/action2.h5 -------------------------------------------------------------------------------- /util/171204_pose3_info.txt: -------------------------------------------------------------------------------- 1 | 0, 0, 10 2 | 1, 10, 13 3 | 0, 20, 25 4 | 3, 37, 44 5 | 3, 56, 60 6 | 0, 116, 136 7 | 1, 147, 150 8 | 1, 162, 165 9 | 3, 192, 199 10 | 3, 211, 215 11 | 0, 272, 302 12 | 1, 303, 305 13 | -------------------------------------------------------------------------------- /util/171204_pose5_info.txt: -------------------------------------------------------------------------------- 1 | 1, 6, 9 2 | 0, 17, 23 3 | 3, 33, 41 4 | 3, 53, 56 5 | 0, 114, 144 6 | 1, 145, 148 7 | 1, 160, 164 8 | 0, 173, 177 9 | 3, 188, 195 10 | 3, 208, 211 11 | 0, 268, 297 12 | 1, 298, 301 13 | 1, 311, 315 14 | 0, 323, 328 15 | 3, 339, 347 16 | 3, 359, 362 17 | 0, 419, 449 18 | 1, 450, 453 19 | 1, 464, 466 20 | 1, 468, 469 21 | 0, 477, 481 22 | 3, 492, 419 23 | 3, 511, 515 24 | 0, 572, 601 25 | 1, 602, 606 26 | 1, 617, 621 27 | 0, 629, 634 28 | 3, 645, 653 29 | 3, 665, 669 30 | 0, 726, 754 31 | 1, 757, 759 32 | 1, 776, 781 33 | 0, 789, 792 34 | 3, 805, 811 35 | 3, 822, 827 36 | 0, 885, 905 -------------------------------------------------------------------------------- /util/171204_pose6_info.txt: -------------------------------------------------------------------------------- 1 | 0, 1, 10 2 | 1, 11, 15 3 | 0, 23, 28 4 | 3, 39, 46 5 | 3, 58, 60 6 | 0, 119, 149 7 | 1, 150, 152 8 | 1, 165, 170 9 | 0, 178, 182 10 | 3, 195, 211 11 | 3, 213, 217 12 | 0, 275, 303 13 | 1, 304, 306 14 | 1, 320, 326 15 | 0, 335, 338 16 | 3, 349, 355 17 | 0, 432, 458 18 | 1, 461, 463 19 | 1, 472, 478 20 | 0, 485, 489 21 | 3, 501, 508 22 | 3, 521, 523 23 | 0, 582, 609 24 | 1, 612, 615 25 | 1, 627, 631 26 | 0, 641, 644 27 | 3, 655, 663 28 | 3, 675, 678 29 | 0, 736, 763 30 | 1, 766, 768 31 | -------------------------------------------------------------------------------- /util/concat.py: -------------------------------------------------------------------------------- 1 | ''' 2 | this python script for MHAD dataset 3 | To run this script is required ffmpeg-python(https://github.com/kkroening/ffmpeg-python) 4 | ''' 5 | 6 | import glob 7 | import ffmpeg 8 | 9 | clusters = glob.glob("./Cluster*") 10 | 11 | ffmpeg_path = "C:\\ffmpeg\\bin\\ffmpeg.exe" 12 | output_path = ".\\output\\" 13 | output_count = 1 14 | for cl in clusters: 15 | cameras = glob.glob(cl + "\\Cam*") 16 | for ca in cameras: 17 | subjects = glob.glob(ca + "\\S*") 18 | for su in subjects: 19 | targets = glob.glob(su + "\\A04\\R03\\*.pgm") 20 | targets[0] = targets[0].replace("000.pgm", "%3d.pgm") 21 | in_stream = ffmpeg.input(targets[0]) 22 | in_stream.output(output_path + str(output_count).zfill(5) + ".mp4", r = 25, pix_fmt="yuv420p").run(ffmpeg_path, overwrite_output=True) 23 | output_count += 1 -------------------------------------------------------------------------------- /util/cut.py: -------------------------------------------------------------------------------- 1 | ''' 2 | this python script for CMU panoptic dataset 3 | To run this script is required ffmpeg-python(https://github.com/kkroening/ffmpeg-python) 4 | ''' 5 | import glob 6 | import ffmpeg 7 | 8 | info_file_name = "./info.txt" 9 | 10 | in_files = glob.glob("./*00_*.mp4") 11 | in_file_streams = [] 12 | for file in in_files: 13 | in_file_streams.append(ffmpeg.input(file)) 14 | 15 | out_file_names = ["1.mp4", "2.mp4", "3.mp4", "4.mp4"] 16 | out_file_streams = [None] * len(out_file_names) 17 | init_flags = [False] * len(out_file_names) 18 | filestream = open(info_file_name, "r") 19 | for line in filestream: 20 | values = line[:-1].split(",") 21 | action_type = int(values[0]) 22 | start = int(values[1]) 23 | end = int(values[2]) 24 | for in_stream in in_file_streams: 25 | if init_flags[action_type] == False: 26 | init_flags[action_type] = True 27 | out_file_streams[action_type] = ffmpeg.concat(in_stream.trim(start=start, end=end).setpts('PTS-STARTPTS')) 28 | else: 29 | out_file_streams[action_type] = ffmpeg.concat(out_file_streams[action_type], in_stream.trim(start=start, end=end).setpts('PTS-STARTPTS')) 30 | 31 | 32 | for i in range(len(out_file_names)): 33 | if i == 0: 34 | continue 35 | if init_flags[i] == False: 36 | continue 37 | out = ffmpeg.output(out_file_streams[i], out_file_names[i], s='720x480') 38 | ffmpeg.run(out, 'C:\\ffmpeg\\bin\\ffmpeg.exe') --------------------------------------------------------------------------------