├── .gitignore ├── .gitmodules ├── .npmignore ├── CHANGELOG.md ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── binding.gyp ├── index.js ├── package.json ├── src ├── module.cpp ├── module.h ├── napi_yolo_errors.c └── napi_yolo_errors.h ├── util └── has_lib.js └── yolo └── src ├── libyolo.cpp ├── libyolo.h ├── private_structs.h ├── yolo_error.c └── yolo_error.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Cmake 35 | cmake-build-debug/ 36 | 37 | # Clion 38 | .idea/ 39 | .gitconfig 40 | 41 | #libyolo build folder 42 | obj/ 43 | *.weights 44 | weights/ 45 | data/ 46 | 47 | #NodeJS 48 | build/ 49 | node_modules/ 50 | package-lock.json 51 | vapi-node-yolo*.tgz 52 | 53 | #macOS 54 | .DS_Store -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "darknet"] 2 | path = darknet 3 | url = https://github.com/rcaceiro/darknet.git 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | CMakeLists.txt 3 | weights/ 4 | *.weights 5 | *.a 6 | *.dylib 7 | *.o 8 | *.so 9 | darknet/cfg 10 | darknet/data 11 | darknet/examples 12 | darknet/python 13 | cmake-build-debug/ 14 | node_modules/ 15 | data/ 16 | build/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | **Note**: In this project the version semantic will be x.y.z, when: 3 | - **z** represent, backwards compatible bug fixes are introduced 4 | - **y** represent, new backwards compatible functionality are introduced 5 | - **x** represent, any backwards incompatible changes are introduced, and/or a big bundle of bug fixes and new functionalities are introduced. 6 | 7 | ## [v2.1.2](https://github.com/rcaceiro/node-yolo/tree/v2.1.2) (24-12-2018) 8 | [Full Changelog](https://github.com/rcaceiro/node-yolo/compare/v2.1.1...v2.1.2) 9 | 10 | **Fixed bugs:** 11 | - Fix a bug then make with sudo, the which not run on sudo. 12 | 13 | ## [v2.1.1](https://github.com/rcaceiro/node-yolo/tree/v2.1.1) (24-12-2018) 14 | [Full Changelog](https://github.com/rcaceiro/node-yolo/compare/v2.1.0...v2.1.1) 15 | 16 | **Fixed bugs:** 17 | - Removed some extra code that allow gather timings. 18 | 19 | ## [v2.1.0](https://github.com/rcaceiro/node-yolo/tree/v2.1.0) (23-12-2018) 20 | [Full Changelog](https://github.com/rcaceiro/node-yolo/compare/v2.0.0...v2.1.0) 21 | 22 | **New Features**: 23 | - node-yolo now can process only x number of frames. 1/3 means process 1 frames for each 3, the default behaviour is process every frame. 24 | - node-yolo now allow the developer can specify the threshold, the default behavior is 0.5. 25 | 26 | **Implemented enhancements:** 27 | - now on reject the node-yolo no longer kill nodeJS. 28 | 29 | ## [v2.0.1](https://github.com/rcaceiro/node-yolo/tree/v2.0.1) (23-12-2018) 30 | [Full Changelog](https://github.com/rcaceiro/node-yolo/compare/v2.0.0...v2.0.1) 31 | 32 | **Fixed bugs:** 33 | - Small fix then yolo_status_decode is called, it wasn't linked on libyolo 34 | 35 | **Closed issues:** 36 | Detect only first object from same class [\#4](https://github.com/rcaceiro/node-yolo/issues/4) 37 | 38 | ## [v2.0.0](https://github.com/rcaceiro/node-yolo/tree/v2.0.0) (15-11-2018) 39 | [Full Changelog](https://github.com/rcaceiro/node-yolo/compare/previous_to_v2.0.0...v2.0.0) 40 | 41 | **New Features**: 42 | - node-yolo classify videos, but for now only video files, not a camera stream. 43 | - added to results of classification the time that took to be classified. 44 | 45 | **Implemented enhancements:** 46 | - Creation of a changelog 47 | - Added a capability on Makefile to detect OpenMP, only on Linux, if macOs users has it installed has to change manually the parameter OPENMP in Makefile. 48 | - Added constraints on CPU and OS to install this module in package.json 49 | 50 | **Fixed bugs:** 51 | - Fix on Makefile to include more new object files, needed 52 | 53 | **Backwards Incompatibility:** 54 | - The versions prior 2.0.0 of the module has the [ImageMagick](https://www.imagemagick.org) as a dependency, but with OpenCV we can archive the desired goal. And by this we remove one dependency of the project. 55 | 56 | **Closed issues:** 57 | - Getting Empty Array In Detections Instead Of Object [\#1](https://github.com/rcaceiro/node-yolo/issues/1) 58 | - Built with GPU stuff enabled but using CPU [\#3](https://github.com/rcaceiro/node-yolo/issues/3) -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.10) 2 | PROJECT(node-yolo) 3 | 4 | SET(CMAKE_CXX_STANDARD 17) 5 | 6 | INCLUDE_DIRECTORIES(.) 7 | INCLUDE_DIRECTORIES(darknet/src) 8 | INCLUDE_DIRECTORIES(darknet/include) 9 | INCLUDE_DIRECTORIES(yolo/src) 10 | INCLUDE_DIRECTORIES(/usr/local/include/node) 11 | INCLUDE_DIRECTORIES(/usr/include/node) 12 | INCLUDE_DIRECTORIES(/usr/local/Cellar/opencv/3.4.3_1/include/opencv) 13 | INCLUDE_DIRECTORIES(/usr/local/Cellar/opencv/3.4.3_1/include) 14 | ADD_COMPILE_DEFINITIONS(OPENCV) 15 | 16 | ADD_EXECUTABLE(node-yolo 17 | yolo/src/libyolo.h 18 | yolo/src/libyolo.cpp src/module.cpp src/module.h yolo/src/yolo_error.c yolo/src/yolo_error.h yolo/src/private_structs.h src/napi_yolo_errors.c src/napi_yolo_errors.h) 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Rúben Caceiro 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GPU=0 2 | CUDNN=0 3 | OPENCV=1 4 | OPENMP=0 5 | DEBUG=0 6 | 7 | NVCC = $(shell find /usr -iname "nvcc" 2> /dev/null) 8 | NVCC_TEST := $(notdir $(NVCC)) 9 | 10 | ifeq ($(NVCC_TEST),nvcc) 11 | GPU=1 12 | endif 13 | 14 | ARCH= -gencode arch=compute_35,code=sm_35 \ 15 | -gencode arch=compute_50,code=[sm_50,compute_50] \ 16 | -gencode arch=compute_52,code=[sm_52,compute_52] \ 17 | -gencode arch=compute_61,code=sm_61 \ 18 | -gencode arch=compute_62,code=sm_62 \ 19 | -gencode arch=compute_70,code=sm_70 20 | 21 | # This is what I use, uncomment if you know your arch and want to specify 22 | # ARCH= -gencode arch=compute_52,code=compute_52 23 | 24 | SHARE_LIB_OPT= 25 | 26 | MAC_OS_LOCAL_FOLDER= 27 | 28 | LIB_RAW_NAME=libyolo 29 | SLIB= 30 | SLIB_DIR= 31 | 32 | ALIB=libyolo.a 33 | ALIB_DIR=$(addprefix yolo/, $(ALIB)) 34 | 35 | UNAME_S := $(shell uname -s) 36 | ifeq ($(UNAME_S),Linux) 37 | SHARE_LIB_OPT += -shared 38 | SLIB=$(addprefix $(LIB_RAW_NAME),.so) 39 | OPENMP=$(shell ldconfig -p | grep gomp > /dev/null 2> /dev/null && echo 1 || echo 0) 40 | endif 41 | ifeq ($(UNAME_S),Darwin) 42 | SHARE_LIB_OPT += -dynamiclib 43 | MAC_OS_LOCAL_FOLDER=local/ 44 | SLIB=$(addprefix $(LIB_RAW_NAME),.dylib) 45 | endif 46 | 47 | SLIB_DIR=$(addprefix yolo/, $(SLIB)) 48 | 49 | ROOT= 50 | #ifeq ($(shell id -u), 0) 51 | # ROOT=sudo 52 | #endif 53 | 54 | OBJDIR=./obj/ 55 | VPATH=./darknet/src/:./yolo/src/:./stack/ 56 | 57 | CC=gcc 58 | CPP=g++ 59 | AR=ar 60 | ARFLAGS=rcs 61 | OPTS= 62 | LDFLAGS= -lm -pthread 63 | COMMON= -I./darknet/include/ -I./darknet/src/ 64 | CFLAGS=-Wall -Wno-unused-result -Wno-unknown-pragmas -Wfatal-errors -fPIC 65 | 66 | ifeq ($(OPENMP), 1) 67 | CFLAGS+= -fopenmp 68 | endif 69 | 70 | ifeq ($(DEBUG), 1) 71 | OPTS+=-O0 -g 72 | else 73 | OPTS+=-Ofast 74 | endif 75 | 76 | CFLAGS+=$(OPTS) 77 | 78 | ifeq ($(OPENCV), 1) 79 | COMMON+= -DOPENCV 80 | CFLAGS+= -DOPENCV 81 | LDFLAGS+= `pkg-config --libs opencv` -lstdc++ 82 | COMMON+= `pkg-config --cflags opencv` 83 | endif 84 | 85 | ifeq ($(GPU), 1) 86 | COMMON+= -DGPU -I/usr/local/cuda/include/ 87 | CFLAGS+= -DGPU 88 | LDFLAGS+= -L/usr/local/cuda/lib64 -L/usr/lib/x86_64-linux-gnu/ -lcuda -lcudart -lcublas -lcurand 89 | endif 90 | 91 | ifeq ($(CUDNN), 1) 92 | COMMON+= -DCUDNN 93 | CFLAGS+= -DCUDNN 94 | LDFLAGS+= -lcudnn 95 | endif 96 | 97 | OBJ=libyolo.o yolo_error.o gemm.o utils.o cuda.o deconvolutional_layer.o convolutional_layer.o list.o image.o activations.o im2col.o col2im.o blas.o crop_layer.o dropout_layer.o maxpool_layer.o softmax_layer.o data.o matrix.o network.o connected_layer.o cost_layer.o parser.o option_list.o detection_layer.o route_layer.o upsample_layer.o box.o normalization_layer.o avgpool_layer.o layer.o local_layer.o shortcut_layer.o logistic_layer.o activation_layer.o rnn_layer.o gru_layer.o crnn_layer.o demo.o batchnorm_layer.o region_layer.o reorg_layer.o tree.o lstm_layer.o l2norm_layer.o yolo_layer.o iseg_layer.o image_opencv.o 98 | EXECOBJA=captcha.o lsd.o super.o art.o tag.o cifar.o go.o rnn.o segmenter.o regressor.o classifier.o coco.o yolo.o detector.o nightmare.o instance-segmenter.o darknet.o 99 | ifeq ($(GPU), 1) 100 | LDFLAGS+= -lstdc++ 101 | OBJ+=convolutional_kernels.o deconvolutional_kernels.o activation_kernels.o im2col_kernels.o col2im_kernels.o blas_kernels.o crop_layer_kernels.o dropout_layer_kernels.o maxpool_layer_kernels.o avgpool_layer_kernels.o 102 | endif 103 | 104 | EXECOBJ = $(addprefix $(OBJDIR), $(EXECOBJA)) 105 | OBJS = $(addprefix $(OBJDIR), $(OBJ)) 106 | DEPS = $(wildcard src/*.h) Makefile ./darknet/include/darknet.h 107 | 108 | all: obj $(SLIB_DIR) $(ALIB_DIR) 109 | #all: obj results $(SLIB) $(ALIB) $(EXEC) 110 | 111 | $(EXEC): $(EXECOBJ) $(ALIB) 112 | $(ROOT) $(CC) $(COMMON) $(CFLAGS) $^ -o $@ $(LDFLAGS) $(ALIB) 113 | 114 | $(ALIB_DIR): $(OBJS) 115 | $(ROOT) $(AR) $(ARFLAGS) $@ $^ 116 | 117 | $(SLIB_DIR): $(OBJS) 118 | $(ROOT) $(CC) $(CFLAGS) -shared $^ -o $@ $(LDFLAGS) 119 | 120 | $(OBJDIR)%.o: %.cpp $(DEPS) 121 | $(ROOT) $(CPP) $(COMMON) $(CFLAGS) -std=c++17 -c $< -o $@ 122 | 123 | $(OBJDIR)%.o: %.c $(DEPS) 124 | $(ROOT) $(CC) $(COMMON) $(CFLAGS) -c $< -o $@ 125 | 126 | $(OBJDIR)%.o: %.cu $(DEPS) 127 | $(ROOT) $(NVCC) $(ARCH) $(COMMON) --compiler-options "$(CFLAGS)" -c $< -o $@ 128 | 129 | obj: 130 | $(ROOT) mkdir -p obj 131 | backup: 132 | mkdir -p backup 133 | results: 134 | mkdir -p results 135 | 136 | .PHONY: clean 137 | 138 | clean_all: clean 139 | $(ROOT) rm -rf $(SLIB_DIR) $(ALIB_DIR) 140 | 141 | clean: 142 | $(ROOT) rm -rf obj 143 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-yolo 2 | This Node.js C++ addon allow you to use a state-of-the-art, real-time object detection system called [Yolo](https://pjreddie.com/darknet/yolo/). 3 |
This addon came out from a computer engineering final project, [VAPi](https://github.com/freakstatic/vapi-server), guided by [Patrício Domingues](https://scholar.google.com/citations?user=LPwSQ2EAAAAJ&hl=en) at [Institute Polytechnic of Leiria](https://www.ipleiria.pt/). 4 |
The version 1.x.x was developed by [Rúben Caceiro](https://github.com/rcaceiro) and [Ricardo Maltez](https://github.com/freakstatic) during the final project. 5 |
The version 2.x.x was sponsored by [Instituto de Telecomunicações](https://www.it.pt) developed by [Rúben Caceiro](https://github.com/rcaceiro) and guided by [Patrício Domingues](https://scholar.google.com/citations?user=LPwSQ2EAAAAJ&hl=en). 6 | 7 | ### Pre-requirements 8 | * C/C++ Compiler (tested with gcc & g++) 9 | * nVidia graphic card (Only if you want to use GPU acceleration): 10 | * [CUDA](https://developer.nvidia.com/cuda-zone) 11 | * [CuDNN](https://developer.nvidia.com/cudnn) 12 | * [Node.js](https://nodejs.org/en/) (tested on node.js>= 8) 13 | * [node-gyp](https://www.npmjs.com/package/node-gyp) 14 | * [OpenCV](https://opencv.org) 15 | 16 | **Note 1**: Before any update please see the [changelog](https://github.com/rcaceiro/node-yolo/blob/master/CHANGELOG.md).
17 | **Note 2**: The versions prior 2.0.0 of the module has the [ImageMagick](https://www.imagemagick.org) as a dependency, but with OpenCV we can archive the desired goal. And by this we remove one dependency of the project. 18 | ### Recommended* hardware requirements 19 | * Quad-core processor** 20 | * 10 GB to run node-yolo 21 | * At least 4GB of GPU memory***, if you want use GPU acceleration 22 | ### Minimum* hardware requirements 23 | * Dual-core processor** 24 | * 8 GB to run node-yolo 25 | * At least 4GB of GPU memory***, if you want use GPU acceleration 26 | ## Installation 27 | ```sh 28 | npm i @vapi/node-yolo --save 29 | ``` 30 | 31 | ## How To Use 32 | 33 | ```javascript 34 | const Yolo = require('@vapi/node-yolo'); 35 | const detector = new Yolo("darknet_configs", "cfg/coco.data", "cfg/yolov3.cfg", "yolov3.weights"); 36 | detector.detectImage(path,threshold,frames_to_process) 37 | .then(detections => 38 | { 39 | // here you receive the detections 40 | }) 41 | .catch(error => 42 | { 43 | // here you can handle the errors. Ex: Out of memory 44 | }); 45 | 46 | detector.detectVideo(path,threshold,frames_to_process) 47 | .then(detections => 48 | { 49 | // here you receive the detections 50 | }) 51 | .catch(error => 52 | { 53 | // here you can handle the errors. Ex: Out of memory 54 | }); 55 | ``` 56 | **path**, required, represents the path to the file 57 | 58 | **threshold**, not required, default behaviour is 0.5, represents the threshold to yolo filter the detections 59 | 60 | **frames_to_process**, not required, default behaviour is 1/1, represents the number of frames that developer wants to process, this means 1/3 process 1 frame for each 3. 61 | 62 | **darknet_configs** is a folder where you should put the Yolo [weights](https://pjreddie.com/darknet/yolo/), [cfg](https://github.com/pjreddie/darknet/tree/master/cfg) and [data files](https://github.com/pjreddie/darknet/tree/master/data). 63 | You need to create two folder, cfg and data and put the files for each one. Like this:
64 | 65 | . 66 | ├── darknet_configs # The folder for the Yolo weight, cfg and data files 67 | │ ├── cfg # cfg folder 68 | | |── coco.data 69 | | |── yolov3.cfg 70 | │ ├── data # data folder 71 | | | |── coco.names 72 | │ └── yolov3.weights # YoloV3 weights file 73 | └── ... 74 | 75 | **Note 1**: Our suggestion for better accuracy use [coco.data](https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/coco.data), [coco.names](https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/coco.names), [yolov3-spp.cfg](https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/yolov3-spp.cfg) and [yolov3-spp.weights](https://pjreddie.com/media/files/yolov3-spp.weights). 76 |
**Note 2**: Our suggestion for faster detection use [coco.data](https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/coco.data), [coco.names](https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/coco.names), [yolov3-tiny.cfg](https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/yolov3-tiny.cfg) and [yolov3-tiny.weights](https://pjreddie.com/media/files/yolov3-tiny.weights). 77 |
**Note 3**: Our suggestion for best of two worlds use [coco.data](https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/coco.data), [coco.names](https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/coco.names), [yolov3.cfg](https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/yolov3.cfg) and [yolov3.weights](https://pjreddie.com/media/files/yolov3.weights). 78 | 79 | #### video detection object 80 | | **Field** | **Type** | **Description** 81 | |:----------|:---------|:----------------------------------------------------- 82 | | `frame` | `long/int64` | number of the frame 83 | | `millisecond` | `double` | the millisecond that frame appear on video 84 | | `timeSpentForClassification` | `double` | time used to classifies one frame 85 | | `detections` | `array` | array of `detection` object, containing all detections 86 | 87 | #### image detection object 88 | | **Field** | **Type** | **Description** 89 | |:----------|:---------|:----------------------------------------------------- 90 | | `timeSpentForClassification` | `double` | time used to classifies one image 91 | | `detections` | `array` | array of `detection` object, containing all detections 92 | 93 | #### detection object 94 | | **Field** | **Type** | **Description** 95 | |:----------|:---------|:----------------------------------------------------- 96 | | `className` | `string` | name of the class of the object detected 97 | | `probability` | `double` | the probability that this className is correct 98 | | `box` | `box` | object that contains box info of the object 99 | 100 | #### box object 101 | | **Field** | **Type** | **Description** 102 | |:----------|:---------|:----------------------------------------------------- 103 | | `x` | `double` | x coordinate in pixels of the picture 104 | | `y` | `double` | y coordinate in pixels of the picture 105 | | `w` | `double` | width from x point in pixels 106 | | `h` | `double` | height from y point in pixels 107 | 108 | \* To get that metrics we calculate the usage for video with 3 hours at 60fps. 109 |
\**If you do not use gpu, may should consider a processor with higher number of cores. 110 |
\***The weaker graphics card used was a nVidia GTX960M 111 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "variables":{ 3 | "with_openmp%":"=9" 33 | }, 34 | "os":[ 35 | "darwin", 36 | "linux" 37 | ], 38 | "cpu":[ 39 | "x64" 40 | ], 41 | "author":"Rúben Caceiro & Ricardo Maltez ", 42 | "license":"MIT" 43 | } 44 | -------------------------------------------------------------------------------- /src/module.cpp: -------------------------------------------------------------------------------- 1 | #include "module.h" 2 | #include "napi_yolo_errors.h" 3 | #include 4 | 5 | napi_status get_string_value(napi_env env, napi_value args[], size_t index, char **value, size_t value_size) 6 | { 7 | napi_status status; 8 | napi_valuetype value_type; 9 | size_t length=0; 10 | status=napi_typeof(env, args[index], &value_type); 11 | if(status != napi_ok) 12 | { 13 | return napi_invalid_arg; 14 | } 15 | 16 | if(value_type != napi_string) 17 | { 18 | return napi_string_expected; 19 | } 20 | 21 | status=napi_get_value_string_utf8(env, args[index], *value, value_size, &length); 22 | assert(status == napi_ok); 23 | ++length; 24 | if(length>value_size) 25 | { 26 | (*value)=static_cast(realloc(*value, length)); 27 | assert((*value) != nullptr); 28 | } 29 | status=napi_get_value_string_utf8(env, args[index], *value, length, &length); 30 | assert(status == napi_ok); 31 | 32 | return napi_ok; 33 | } 34 | 35 | napi_status get_double_value(napi_env env, napi_value args[], size_t index, double *value) 36 | { 37 | napi_status status; 38 | napi_valuetype value_type; 39 | status=napi_typeof(env, args[index], &value_type); 40 | if(status != napi_ok) 41 | { 42 | return napi_invalid_arg; 43 | } 44 | 45 | if(value_type != napi_number) 46 | { 47 | return napi_number_expected; 48 | } 49 | 50 | status=napi_get_value_double(env, args[index], value); 51 | assert(status == napi_ok); 52 | return napi_ok; 53 | } 54 | 55 | napi_status get_int_value(napi_env env, napi_value args[], size_t index, int *value) 56 | { 57 | napi_status status; 58 | napi_valuetype value_type; 59 | status=napi_typeof(env, args[index], &value_type); 60 | if(status != napi_ok) 61 | { 62 | return napi_invalid_arg; 63 | } 64 | 65 | if(value_type != napi_number) 66 | { 67 | return napi_number_expected; 68 | } 69 | 70 | status=napi_get_value_int32(env, args[index], value); 71 | assert(status == napi_ok); 72 | return napi_ok; 73 | } 74 | 75 | yolo_napi_status load_box_object(napi_env env, box img_box, napi_value jsbox) 76 | { 77 | napi_status status; 78 | napi_value x, y, w, h; 79 | 80 | status=napi_create_double(env, img_box.x, &x); 81 | if(status != napi_ok) 82 | { 83 | return yolo_napi_create_box_x_double_failed; 84 | } 85 | status=napi_set_named_property(env, jsbox, "x", x); 86 | if(status != napi_ok) 87 | { 88 | return yolo_napi_create_box_x_named_property_failed; 89 | } 90 | 91 | status=napi_create_double(env, img_box.y, &y); 92 | if(status != napi_ok) 93 | { 94 | return yolo_napi_create_box_y_double_failed; 95 | } 96 | status=napi_set_named_property(env, jsbox, "y", y); 97 | if(status != napi_ok) 98 | { 99 | return yolo_napi_create_box_y_named_property_failed; 100 | } 101 | 102 | status=napi_create_double(env, img_box.w, &w); 103 | if(status != napi_ok) 104 | { 105 | return yolo_napi_create_box_w_double_failed; 106 | } 107 | status=napi_set_named_property(env, jsbox, "w", w); 108 | if(status != napi_ok) 109 | { 110 | return yolo_napi_create_box_w_named_property_failed; 111 | } 112 | 113 | status=napi_create_double(env, img_box.h, &h); 114 | if(status != napi_ok) 115 | { 116 | return yolo_napi_create_box_h_double_failed; 117 | } 118 | status=napi_set_named_property(env, jsbox, "h", h); 119 | if(status != napi_ok) 120 | { 121 | return yolo_napi_create_box_h_named_property_failed; 122 | } 123 | return yolo_napi_ok; 124 | } 125 | 126 | yolo_napi_status load_detections(napi_env env, yolo_detection_image *img_detections, napi_value jsarray) 127 | { 128 | napi_status status; 129 | napi_value jsobj, box_object, classes, prob; 130 | detect det; 131 | for(size_t i=0; inum_boxes; ++i) 132 | { 133 | det=img_detections->detection[i]; 134 | status=napi_create_object(env, &jsobj); 135 | if(status != napi_ok) 136 | { 137 | return yolo_napi_create_object_failed; 138 | } 139 | 140 | status=napi_create_string_utf8(env, det.class_name, strlen(det.class_name), &classes); 141 | if(status != napi_ok) 142 | { 143 | return yolo_napi_create_class_name_string_failed; 144 | } 145 | status=napi_set_named_property(env, jsobj, "className", classes); 146 | if(status != napi_ok) 147 | { 148 | return yolo_napi_set_class_name_property_failed; 149 | } 150 | 151 | status=napi_create_double(env, det.probability, &prob); 152 | if(status != napi_ok) 153 | { 154 | return yolo_napi_create_probability_double_failed; 155 | } 156 | status=napi_set_named_property(env, jsobj, "probability", prob); 157 | if(status != napi_ok) 158 | { 159 | return yolo_napi_set_probability_property_failed; 160 | } 161 | 162 | status=napi_create_object(env, &box_object); 163 | if(status != napi_ok) 164 | { 165 | return yolo_napi_create_box_object_failed; 166 | } 167 | status=napi_set_named_property(env, jsobj, "box", box_object); 168 | if(status != napi_ok) 169 | { 170 | return yolo_napi_set_box_property_failed; 171 | } 172 | 173 | if(load_box_object(env, det.bbox, box_object) == yolo_napi_ok) 174 | { 175 | status=napi_set_element(env, jsarray, (uint32_t)i, jsobj); 176 | if(status != napi_ok) 177 | { 178 | return yolo_napi_set_object_to_array_failed; 179 | } 180 | } 181 | } 182 | return yolo_napi_ok; 183 | } 184 | 185 | yolo_napi_status load_detection_object(napi_env env, yolo_detection_image *img_detections, napi_value object) 186 | { 187 | napi_value js_detections_array; 188 | napi_value js_time_spent_for_classification; 189 | yolo_napi_status yolo_stats; 190 | 191 | if(napi_create_double(env, img_detections->time_spent_for_classification, &js_time_spent_for_classification) != napi_ok) 192 | { 193 | return yolo_napi_create_object_time_spent_for_classification_double_failed; 194 | } 195 | 196 | if(napi_set_named_property(env, object, "timeSpentForClassification", js_time_spent_for_classification) != napi_ok) 197 | { 198 | return yolo_napi_create_object_time_spent_for_classification_named_property_failed; 199 | } 200 | 201 | if(napi_create_array_with_length(env, static_cast(img_detections->num_boxes), &js_detections_array) != napi_ok) 202 | { 203 | return yolo_napi_create_array_failed; 204 | } 205 | 206 | yolo_stats=load_detections(env, img_detections, js_detections_array); 207 | 208 | if(yolo_stats != yolo_napi_ok) 209 | { 210 | return yolo_stats; 211 | } 212 | 213 | if(napi_set_named_property(env, object, "detections", js_detections_array) != napi_ok) 214 | { 215 | return yolo_napi_create_object_time_spent_for_classification_named_property_failed; 216 | } 217 | 218 | return yolo_napi_ok; 219 | } 220 | 221 | yolo_napi_status load_video_detection_object(napi_env env, yolo_detection_video *video_detections, napi_value frames_array) 222 | { 223 | yolo_napi_status yolo_stats; 224 | 225 | for(size_t i=0; icount; ++i) 226 | { 227 | napi_value object; 228 | napi_value frame_id; 229 | napi_value milisecond; 230 | 231 | if(napi_create_object(env, &object) != napi_ok) 232 | { 233 | return yolo_napi_create_main_object_failed; 234 | } 235 | 236 | if(napi_create_int64(env, video_detections->frame_detections[i].frame, &frame_id) != napi_ok) 237 | { 238 | return yolo_napi_create_frame_failed; 239 | } 240 | 241 | if(napi_create_double(env, video_detections->frame_detections[i].milisecond, &milisecond) != napi_ok) 242 | { 243 | return yolo_napi_create_second_failed; 244 | } 245 | 246 | if(napi_set_named_property(env, object, "frame_number", frame_id) != napi_ok) 247 | { 248 | return yolo_napi_set_frame_to_object_failed; 249 | } 250 | 251 | if(napi_set_named_property(env, object, "milisecond", milisecond) != napi_ok) 252 | { 253 | return yolo_napi_set_second_to_object_failed; 254 | } 255 | 256 | yolo_stats=load_detection_object(env, &video_detections->frame_detections[i].detection_frame, object); 257 | if(yolo_stats != yolo_napi_ok) 258 | { 259 | return yolo_napi_create_main_object_failed; 260 | } 261 | 262 | if(napi_set_element(env, frames_array, static_cast(i), object) != napi_ok) 263 | { 264 | return yolo_napi_set_array_property_failed; 265 | } 266 | } 267 | 268 | return yolo_napi_ok; 269 | } 270 | 271 | void reject(napi_env env, yolo_napi_status yolo_stats, data_holder *holder) 272 | { 273 | napi_value error; 274 | yolo_napi_status_detailed status_detailed=yolo_napi_status_decode(yolo_stats, &holder->yolo_stats); 275 | if(napi_create_object(env, &error) == napi_ok) 276 | { 277 | napi_value string_error; 278 | if(napi_create_string_utf8(env, status_detailed.error_message, strlen(status_detailed.error_message), &string_error) == napi_ok) 279 | { 280 | napi_set_named_property(env, error, "errorMessage", string_error); 281 | } 282 | napi_value error_code; 283 | if(napi_create_int32(env, status_detailed.error_code, &error_code) == napi_ok) 284 | { 285 | napi_set_named_property(env, error, "errorCode", error_code); 286 | } 287 | napi_value error_prefix; 288 | if(napi_create_string_utf8(env, status_detailed.error_prefix, strlen(status_detailed.error_message), &error_prefix) == napi_ok) 289 | { 290 | napi_set_named_property(env, error, "errorPrefix", error_prefix); 291 | } 292 | } 293 | napi_reject_deferred(env, holder->deferred, error); 294 | } 295 | 296 | void release_async_work(napi_env env, data_holder *holder) 297 | { 298 | napi_async_work work=holder->work; 299 | 300 | free(holder->image_path); 301 | free(holder); 302 | 303 | napi_delete_async_work(env, work); 304 | } 305 | 306 | void complete_async_detect(napi_env env, napi_status status, void *data) 307 | { 308 | auto *holder=static_cast(data); 309 | napi_value return_value; 310 | yolo_napi_status yolo_stats; 311 | 312 | if(holder->yolo_stats != yolo_ok) 313 | { 314 | reject(env, yolo_napi_error_from_libyolo, holder); 315 | release_async_work(env, holder); 316 | return; 317 | } 318 | 319 | if(status != napi_ok) 320 | { 321 | reject(env, yolo_napi_unknow_error, holder); 322 | release_async_work(env, holder); 323 | return; 324 | } 325 | 326 | if(holder->img_detection == nullptr && holder->video_detection == nullptr) 327 | { 328 | reject(env, yolo_napi_unknow_error, holder); 329 | release_async_work(env, holder); 330 | return; 331 | } 332 | 333 | if(holder->img_detection != nullptr) 334 | { 335 | if(napi_create_object(env, &return_value) != napi_ok) 336 | { 337 | reject(env, yolo_napi_create_main_object_failed, holder); 338 | } 339 | else 340 | { 341 | yolo_stats=load_detection_object(env, holder->img_detection, return_value); 342 | 343 | if(yolo_stats == yolo_napi_ok) 344 | { 345 | napi_resolve_deferred(env, holder->deferred, return_value); 346 | } 347 | else 348 | { 349 | reject(env, yolo_stats, holder); 350 | } 351 | } 352 | yolo_detection_image_free(&holder->img_detection); 353 | } 354 | else 355 | { 356 | if(napi_create_array_with_length(env, holder->video_detection->count, &return_value) != napi_ok) 357 | { 358 | reject(env, yolo_napi_create_main_object_failed, holder); 359 | } 360 | else 361 | { 362 | yolo_stats=load_video_detection_object(env, holder->video_detection, return_value); 363 | 364 | if(yolo_stats == yolo_napi_ok) 365 | { 366 | napi_resolve_deferred(env, holder->deferred, return_value); 367 | } 368 | else 369 | { 370 | reject(env, yolo_stats, holder); 371 | } 372 | } 373 | yolo_detection_video_free(&holder->video_detection); 374 | } 375 | 376 | release_async_work(env, holder); 377 | } 378 | 379 | void async_detect_image(napi_env env, void *data) 380 | { 381 | (void)env; 382 | auto *holder=static_cast(data); 383 | if(holder->yolo->created) 384 | { 385 | holder->yolo->mutex_lock(); 386 | holder->yolo_stats=yolo_detect_image(holder->yolo->yolo, &holder->img_detection, holder->image_path, holder->thresh_value); 387 | holder->yolo->mutex_unlock(); 388 | } 389 | else 390 | { 391 | holder->yolo_stats=yolo_instanciation; 392 | } 393 | } 394 | 395 | void async_detect_video(napi_env env, void *data) 396 | { 397 | (void)env; 398 | auto *holder=static_cast(data); 399 | if(holder->yolo->created) 400 | { 401 | holder->yolo->mutex_lock(); 402 | holder->yolo_stats=yolo_detect_video(holder->yolo->yolo, &holder->video_detection, holder->image_path, holder->thresh_value, holder->fraction_frames_to_process); 403 | holder->yolo->mutex_unlock(); 404 | } 405 | else 406 | { 407 | holder->yolo_stats=yolo_instanciation; 408 | } 409 | } 410 | 411 | napi_ref Yolo::constructor; 412 | 413 | Yolo::Yolo(char *working_directory, char *datacfg, char *cfgfile, char *weightfile) : env_(nullptr), wrapper_(nullptr) 414 | { 415 | this->yolo=nullptr; 416 | yolo_status yolo_stats=yolo_init(&this->yolo, working_directory, datacfg, cfgfile, weightfile); 417 | if(yolo_stats != yolo_ok) 418 | { 419 | this->created=false; 420 | return; 421 | } 422 | pthread_mutex_init(&this->mutex, nullptr); 423 | this->created=true; 424 | } 425 | 426 | Yolo::~Yolo() 427 | { 428 | pthread_mutex_destroy(&this->mutex); 429 | yolo_cleanup(this->yolo); 430 | napi_delete_reference(env_, wrapper_); 431 | } 432 | 433 | void Yolo::Destructor(napi_env env, void *nativeObject, void * /*finalize_hint*/) 434 | { 435 | (void)env; 436 | reinterpret_cast(nativeObject)->~Yolo(); 437 | } 438 | 439 | napi_value Yolo::Init(napi_env env, napi_value exports) 440 | { 441 | napi_status status; 442 | napi_property_descriptor properties[]={{"detectImage\0", nullptr, Yolo::DetectImage, nullptr, nullptr, nullptr, napi_default, nullptr}, 443 | {"detectVideo\0", nullptr, Yolo::DetectVideo, nullptr, nullptr, nullptr, napi_default, nullptr}}; 444 | 445 | napi_value cons; 446 | status=napi_define_class(env, "Yolo\0", NAPI_AUTO_LENGTH, Yolo::New, nullptr, 2, properties, &cons); 447 | assert(status == napi_ok); 448 | 449 | status=napi_create_reference(env, cons, 1, &Yolo::constructor); 450 | assert(status == napi_ok); 451 | 452 | status=napi_set_named_property(env, exports, "Yolo", cons); 453 | assert(status == napi_ok); 454 | return exports; 455 | } 456 | 457 | napi_value Yolo::New(napi_env env, napi_callback_info info) 458 | { 459 | napi_status status; 460 | napi_value target; 461 | status=napi_get_new_target(env, info, &target); 462 | assert(status == napi_ok); 463 | bool is_constructor=target != nullptr; 464 | 465 | if(is_constructor) 466 | { 467 | // Invoked as constructor: `new MyObject(...)` 468 | size_t argc=4; 469 | napi_value args[4]; 470 | napi_value jsthis; 471 | status=napi_get_cb_info(env, info, &argc, args, &jsthis, nullptr); 472 | assert(status == napi_ok); 473 | assert(argc>=4); 474 | char *darknet_path=nullptr; 475 | char *datacfg=nullptr; 476 | char *cfgfile=nullptr; 477 | char *weightfile=nullptr; 478 | 479 | get_string_value(env, args, 0, &darknet_path, 0); 480 | get_string_value(env, args, 1, &datacfg, 0); 481 | get_string_value(env, args, 2, &cfgfile, 0); 482 | get_string_value(env, args, 3, &weightfile, 0); 483 | 484 | auto obj=new Yolo(darknet_path, datacfg, cfgfile, weightfile); 485 | obj->env_=env; 486 | status=napi_wrap(env, jsthis, reinterpret_cast(obj), Yolo::Destructor, nullptr, &obj->wrapper_); 487 | assert(status == napi_ok); 488 | 489 | return jsthis; 490 | } 491 | else 492 | { 493 | // Invoked as plain function `MyObject(...)`, turn into construct call. 494 | size_t argc_=4; 495 | napi_value args[4]; 496 | status=napi_get_cb_info(env, info, &argc_, args, nullptr, nullptr); 497 | assert(status == napi_ok); 498 | 499 | const size_t argc=4; 500 | napi_value argv[4]={args[0], args[1], args[2], args[3]}; 501 | 502 | napi_value cons; 503 | status=napi_get_reference_value(env, Yolo::constructor, &cons); 504 | assert(status == napi_ok); 505 | 506 | napi_value instance; 507 | status=napi_new_instance(env, cons, argc, argv, &instance); 508 | assert(status == napi_ok); 509 | 510 | return instance; 511 | } 512 | } 513 | 514 | napi_value Yolo::DetectImage(napi_env env, napi_callback_info info) 515 | { 516 | napi_status status; 517 | napi_deferred deferred; 518 | napi_value promise; 519 | napi_value jsthis; 520 | size_t argc=2; 521 | napi_value args[2]; 522 | 523 | status=napi_get_cb_info(env, info, &argc, args, &jsthis, nullptr); 524 | if(status != napi_ok) 525 | { 526 | napi_throw_error(env, "00", "Cannot get arguments"); 527 | return nullptr; 528 | } 529 | 530 | if(argc<2) 531 | { 532 | napi_throw_error(env, "01", "You have to pass the path to image and thresh value as parameters"); 533 | return nullptr; 534 | } 535 | 536 | char *image_path=nullptr; 537 | status=get_string_value(env, args, 0, &image_path, 0); 538 | assert(status == napi_ok); 539 | if(image_path == nullptr) 540 | { 541 | napi_throw_error(env, "02", "Cannot get image path"); 542 | return nullptr; 543 | } 544 | 545 | double thresh=0.5; 546 | status=get_double_value(env, args, 1, &thresh); 547 | if(status != napi_ok) 548 | { 549 | napi_throw_error(env, "04", "Cannot get thresh value"); 550 | return nullptr; 551 | } 552 | 553 | void *obj=nullptr; 554 | status=napi_unwrap(env, jsthis, &obj); 555 | assert(status == napi_ok); 556 | auto *yolo_obj=static_cast(obj); 557 | 558 | napi_value resource_name; 559 | napi_value resource; 560 | status=napi_create_string_utf8(env, "nodeyolo.detect_image", 18, &resource_name); 561 | assert(status == napi_ok); 562 | status=napi_create_object(env, &resource); 563 | assert(status == napi_ok); 564 | 565 | auto *holder=static_cast(calloc(1, sizeof(data_holder))); 566 | if(holder == nullptr) 567 | { 568 | napi_throw_error(env, "03", "Cannot allocate a struct in memory"); 569 | return nullptr; 570 | } 571 | 572 | status=napi_create_promise(env, &deferred, &promise); 573 | assert(status == napi_ok); 574 | 575 | holder->deferred=deferred; 576 | holder->image_path=image_path; 577 | holder->thresh_value=(float)thresh; 578 | holder->yolo=yolo_obj; 579 | holder->resource=resource; 580 | 581 | status=napi_create_async_work(env, resource, resource_name, async_detect_image, complete_async_detect, holder, &holder->work); 582 | assert(status == napi_ok); 583 | status=napi_queue_async_work(env, holder->work); 584 | assert(status == napi_ok); 585 | 586 | return promise; 587 | } 588 | 589 | napi_value Yolo::DetectVideo(napi_env env, napi_callback_info info) 590 | { 591 | napi_status status; 592 | napi_deferred deferred; 593 | napi_value promise; 594 | napi_value jsthis; 595 | size_t argc=3; 596 | napi_value args[3]; 597 | 598 | status=napi_get_cb_info(env, info, &argc, args, &jsthis, nullptr); 599 | if(status != napi_ok) 600 | { 601 | napi_throw_error(env, "00", "Cannot get arguments"); 602 | return nullptr; 603 | } 604 | 605 | if(argc<1) 606 | { 607 | napi_throw_error(env, "01", "You have to pass the path to image as parameters"); 608 | return nullptr; 609 | } 610 | 611 | char *video_path=nullptr; 612 | status=get_string_value(env, args, 0, &video_path, 0); 613 | if(status != napi_ok || video_path == nullptr) 614 | { 615 | napi_throw_error(env, "02", "Cannot get video path"); 616 | return nullptr; 617 | } 618 | 619 | double thresh=0.5; 620 | if(argc>1) 621 | { 622 | status=get_double_value(env, args, 1, &thresh); 623 | if(status != napi_ok) 624 | { 625 | napi_throw_error(env, "04", "Cannot get thresh value"); 626 | return nullptr; 627 | } 628 | } 629 | 630 | double fraction_frames_to_process=1; 631 | if(argc>2) 632 | { 633 | status=get_double_value(env, args, 2, &fraction_frames_to_process); 634 | if(status != napi_ok) 635 | { 636 | napi_throw_error(env, "05", "Cannot get fraction to process frames value"); 637 | return nullptr; 638 | } 639 | } 640 | 641 | void *obj=nullptr; 642 | status=napi_unwrap(env, jsthis, &obj); 643 | assert(status == napi_ok); 644 | auto *yolo_obj=static_cast(obj); 645 | 646 | napi_value resource_name; 647 | napi_value resource; 648 | status=napi_create_string_utf8(env, "nodeyolo.detect_video", 18, &resource_name); 649 | assert(status == napi_ok); 650 | status=napi_create_object(env, &resource); 651 | assert(status == napi_ok); 652 | 653 | auto *holder=static_cast(calloc(1, sizeof(data_holder))); 654 | if(holder == nullptr) 655 | { 656 | napi_throw_error(env, "03", "Cannot allocate a struct in memory"); 657 | return nullptr; 658 | } 659 | 660 | status=napi_create_promise(env, &deferred, &promise); 661 | if(status != napi_ok) 662 | { 663 | napi_throw_error(env, "06", "Cannot create promise"); 664 | return nullptr; 665 | } 666 | 667 | holder->deferred=deferred; 668 | holder->image_path=video_path; 669 | holder->thresh_value=(float)thresh; 670 | holder->yolo=yolo_obj; 671 | holder->resource=resource; 672 | holder->fraction_frames_to_process=fraction_frames_to_process; 673 | 674 | status=napi_create_async_work(env, resource, resource_name, async_detect_video, complete_async_detect, holder, &holder->work); 675 | assert(status == napi_ok); 676 | status=napi_queue_async_work(env, holder->work); 677 | assert(status == napi_ok); 678 | 679 | return promise; 680 | } 681 | 682 | void Yolo::mutex_lock() 683 | { 684 | pthread_mutex_lock(&this->mutex); 685 | } 686 | 687 | void Yolo::mutex_unlock() 688 | { 689 | pthread_mutex_unlock(&this->mutex); 690 | } 691 | 692 | NAPI_MODULE(NodeYolo, Yolo::Init); -------------------------------------------------------------------------------- /src/module.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_YOLO_MODULE_H 2 | #define NODE_YOLO_MODULE_H 3 | 4 | #include 5 | #include 6 | 7 | class Yolo 8 | { 9 | public: 10 | static napi_value Init(napi_env env, napi_value exports); 11 | 12 | static void Destructor(napi_env env, void *nativeObject, void *finalize_hint); 13 | 14 | void mutex_lock(); 15 | void mutex_unlock(); 16 | yolo_object *yolo; 17 | bool created; 18 | private: 19 | explicit Yolo(char *working_directory, char *datacfg, char *cfgfile, char *weightfile); 20 | ~Yolo(); 21 | 22 | static napi_value New(napi_env env, napi_callback_info info); 23 | 24 | static napi_value DetectImage(napi_env env, napi_callback_info info); 25 | static napi_value DetectVideo(napi_env env, napi_callback_info info); 26 | 27 | static napi_ref constructor; 28 | 29 | pthread_mutex_t mutex; 30 | napi_env env_; 31 | napi_ref wrapper_; 32 | }; 33 | 34 | typedef struct 35 | { 36 | Yolo *yolo; 37 | char *image_path; 38 | float thresh_value; 39 | double fraction_frames_to_process; 40 | napi_deferred deferred; 41 | napi_async_work work; 42 | napi_value resource; 43 | yolo_detection_image *img_detection; 44 | yolo_detection_video *video_detection; 45 | yolo_status yolo_stats; 46 | }data_holder; 47 | 48 | #endif -------------------------------------------------------------------------------- /src/napi_yolo_errors.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "napi_yolo_errors.h" 3 | 4 | void yolo_napi_fill_error_message(yolo_napi_status status, yolo_napi_status_detailed *status_detailed) 5 | { 6 | switch(status) 7 | { 8 | case yolo_napi_create_object_time_spent_for_classification_double_failed: 9 | status_detailed->error_message="image file is corrupted"; 10 | break; 11 | case yolo_napi_create_object_time_spent_for_classification_named_property_failed: 12 | status_detailed->error_message="image file is corrupted"; 13 | break; 14 | case yolo_napi_set_array_property_failed: 15 | status_detailed->error_message="image file is corrupted"; 16 | break; 17 | case yolo_napi_create_main_object_failed: 18 | status_detailed->error_message="image file is corrupted"; 19 | break; 20 | default: 21 | status_detailed->error_message="Unknow error"; 22 | } 23 | } 24 | 25 | yolo_napi_status_detailed yolo_napi_status_decode(yolo_napi_status status, yolo_status *status2) 26 | { 27 | yolo_napi_status_detailed status_detailed; 28 | if(status == yolo_napi_error_from_libyolo) 29 | { 30 | if(status2 != NULL) 31 | { 32 | return yolo_napi_error_wrap(*status2); 33 | } 34 | else 35 | { 36 | status=yolo_napi_unknow_error; 37 | } 38 | } 39 | status_detailed.error_code=status; 40 | status_detailed.error_prefix="yolo_napi"; 41 | yolo_napi_fill_error_message(status, &status_detailed); 42 | return status_detailed; 43 | } 44 | 45 | yolo_napi_status_detailed yolo_napi_error_wrap(yolo_status status) 46 | { 47 | yolo_napi_status_detailed napi_status_detailed; 48 | yolo_status_detailed status_detailed=yolo_status_decode(status); 49 | napi_status_detailed.error_code=status_detailed.error_code; 50 | napi_status_detailed.error_message=status_detailed.error_message; 51 | napi_status_detailed.error_prefix="yolo"; 52 | return napi_status_detailed; 53 | } -------------------------------------------------------------------------------- /src/napi_yolo_errors.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_YOLO_NAPI_YOLO_ERRORS_H 2 | #define NODE_YOLO_NAPI_YOLO_ERRORS_H 3 | 4 | #include "../yolo/src/yolo_error.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | typedef struct 11 | { 12 | int error_code; 13 | char *error_prefix; 14 | char *error_message; 15 | }yolo_napi_status_detailed; 16 | 17 | typedef enum 18 | { 19 | yolo_napi_ok, 20 | yolo_napi_create_main_object_failed, 21 | yolo_napi_create_array_failed, 22 | yolo_napi_set_array_property_failed, 23 | yolo_napi_create_object_failed, 24 | yolo_napi_set_object_to_array_failed, 25 | yolo_napi_create_class_name_string_failed, 26 | yolo_napi_set_class_name_property_failed, 27 | yolo_napi_create_probability_double_failed, 28 | yolo_napi_set_probability_property_failed, 29 | yolo_napi_create_box_object_failed, 30 | yolo_napi_set_box_property_failed, 31 | yolo_napi_create_frame_failed, 32 | yolo_napi_set_frame_to_object_failed, 33 | yolo_napi_create_second_failed, 34 | yolo_napi_set_second_to_object_failed, 35 | 36 | yolo_napi_create_box_x_double_failed, 37 | yolo_napi_create_box_x_named_property_failed, 38 | yolo_napi_create_box_y_double_failed, 39 | yolo_napi_create_box_y_named_property_failed, 40 | yolo_napi_create_box_w_double_failed, 41 | yolo_napi_create_box_w_named_property_failed, 42 | yolo_napi_create_box_h_double_failed, 43 | yolo_napi_create_box_h_named_property_failed, 44 | yolo_napi_create_object_time_spent_for_classification_double_failed, 45 | yolo_napi_create_object_time_spent_for_classification_named_property_failed, 46 | 47 | yolo_napi_error_from_libyolo, 48 | yolo_napi_unknow_error 49 | }yolo_napi_status; 50 | 51 | yolo_napi_status_detailed yolo_napi_status_decode(yolo_napi_status status, yolo_status *status2); 52 | 53 | yolo_napi_status_detailed yolo_napi_error_wrap(yolo_status status); 54 | 55 | #ifdef __cplusplus 56 | } 57 | #endif 58 | 59 | #endif //NODE_YOLO_NAPI_YOLO_ERRORS_H -------------------------------------------------------------------------------- /util/has_lib.js: -------------------------------------------------------------------------------- 1 | /** 2 | taken from https://github.com/Automattic/node-canvas/blob/master/util/has_lib.js 3 | License 4 | 5 | (The MIT License) 6 | 7 | Copyright (c) 2010 LearnBoost, and contributors 8 | 9 | Copyright (c) 2014 Automattic, Inc and contributors 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | */ 17 | var query = process.argv[2] 18 | var fs = require('fs') 19 | var childProcess = require('child_process') 20 | 21 | var SYSTEM_PATHS = [ 22 | '/lib', 23 | '/usr/lib', 24 | '/usr/local/lib', 25 | '/opt/local/lib', 26 | '/usr/lib/x86_64-linux-gnu', 27 | '/usr/lib/i386-linux-gnu' 28 | ] 29 | 30 | /** 31 | * Checks for lib using ldconfig if present, or searching SYSTEM_PATHS 32 | * otherwise. 33 | * @param String library name, e.g. 'jpeg' in 'libjpeg64.so' (see first line) 34 | * @return Boolean exists 35 | */ 36 | function hasSystemLib(lib) { 37 | var libName = 'lib' + lib + '.+(so|dylib)' 38 | var libNameRegex = new RegExp(libName) 39 | 40 | // Try using ldconfig on linux systems 41 | if (hasLdconfig()) { 42 | try { 43 | if (childProcess.execSync('ldconfig -p 2>/dev/null | grep -E "' + libName + '"').length) { 44 | return true 45 | } 46 | } catch (err) { 47 | // noop -- proceed to other search methods 48 | } 49 | } 50 | 51 | // Try checking common library locations 52 | return SYSTEM_PATHS.some(function (systemPath) { 53 | try { 54 | var dirListing = fs.readdirSync(systemPath) 55 | return dirListing.some(function (file) { 56 | return libNameRegex.test(file) 57 | }) 58 | } catch (err) { 59 | return false 60 | } 61 | }) 62 | } 63 | 64 | /** 65 | * Checks for ldconfig on the path and /sbin 66 | * @return Boolean exists 67 | */ 68 | function hasLdconfig() { 69 | try { 70 | if (os.platform()=='win32') { 71 | return false 72 | } 73 | // Add /sbin to path as ldconfig is located there on some systems -- e.g. 74 | // Debian (and it can still be used by unprivileged users): 75 | childProcess.execSync('export PATH="$PATH:/sbin"') 76 | process.env.PATH = '...' 77 | // execSync throws on nonzero exit 78 | childProcess.execSync('hash ldconfig 2>/dev/null') 79 | return true 80 | } catch (err) { 81 | return false 82 | } 83 | } 84 | 85 | /** 86 | * Check if cuda is present 87 | * @return Boolean exists 88 | */ 89 | function hasCuda() { 90 | try { 91 | return childProcess.execSync('[ -d /usr/local/cuda ] && echo "Yes"').length > 0; 92 | } catch (err) { 93 | return false 94 | } 95 | } 96 | 97 | function main(query) { 98 | switch (query) { 99 | case 'openmp': 100 | return hasSystemLib("omp") 101 | case 'opencv': 102 | return hasSystemLib(query) 103 | case 'cuda': 104 | return hasCuda(); 105 | default: 106 | throw new Error('Unknown library: ' + query) 107 | } 108 | } 109 | 110 | process.stdout.write(main(query).toString()) 111 | -------------------------------------------------------------------------------- /yolo/src/libyolo.cpp: -------------------------------------------------------------------------------- 1 | #include "libyolo.h" 2 | #include "private_structs.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | void fill_detect(yolo_object *yolo, detection *network_detection, int network_detection_index, detect *yolo_detect) 12 | { 13 | size_t strlength=strlen(yolo->names[network_detection_index]); 14 | yolo->names[network_detection_index][strlength]='\0'; 15 | yolo_detect->class_name=(char *)calloc(strlength+1, sizeof(char)); 16 | strcpy(yolo_detect->class_name, yolo->names[network_detection_index]); 17 | yolo_detect->probability=network_detection->prob[network_detection_index]*100; 18 | yolo_detect->bbox=network_detection->bbox; 19 | box *bbox=&yolo_detect->bbox; 20 | bbox->x=bbox->x-(bbox->w/2); 21 | bbox->y=bbox->y-(bbox->h/2); 22 | if(bbox->x<0) 23 | { 24 | bbox->x=0; 25 | } 26 | if(bbox->y<0) 27 | { 28 | bbox->y=0; 29 | } 30 | } 31 | 32 | yolo_status fill_detection(yolo_object *yolo, detection *dets, yolo_detection_image *yolo_detect, float time_spent_for_classification, int nboxes, float thresh) 33 | { 34 | yolo_detect->time_spent_for_classification=time_spent_for_classification; 35 | yolo_detect->num_boxes=0; 36 | int class_index; 37 | detection *det; 38 | 39 | for(int i=0; i=thresh) 46 | { 47 | if(det == nullptr || (dets[i].prob[j]>det->prob[class_index])) 48 | { 49 | class_index=j; 50 | det=dets+i; 51 | } 52 | } 53 | } 54 | if(class_index>-1 && det != nullptr) 55 | { 56 | void *temp_pointer=realloc(yolo_detect->detection, sizeof(detect)*(yolo_detect->num_boxes+1)); 57 | if(temp_pointer == nullptr) 58 | { 59 | return yolo_cannot_realloc_detect; 60 | } 61 | yolo_detect->detection=(detect *)temp_pointer; 62 | fill_detect(yolo, det, class_index, yolo_detect->detection+yolo_detect->num_boxes); 63 | yolo_detect->num_boxes++; 64 | } 65 | } 66 | 67 | return yolo_ok; 68 | } 69 | 70 | yolo_status parse_detections_image(yolo_object *yolo, detection *dets, yolo_detection_image **yolo_detect, float time_spent_for_classification, int nboxes, float thresh) 71 | { 72 | if((*yolo_detect) == nullptr) 73 | { 74 | (*yolo_detect)=(yolo_detection_image *)calloc(1, sizeof(yolo_detection_image)); 75 | if((*yolo_detect) == nullptr) 76 | { 77 | return yolo_cannot_alloc_yolo_detection; 78 | } 79 | } 80 | 81 | return fill_detection(yolo, dets, (*yolo_detect), time_spent_for_classification, nboxes, thresh); 82 | } 83 | 84 | yolo_status parse_detections_video(yolo_object *yolo, detection *dets, yolo_detection_video **yolo_detect, float time_spent_for_classification, long frame_id, double milisecond, int nboxes, float thresh) 85 | { 86 | if((*yolo_detect) == nullptr) 87 | { 88 | (*yolo_detect)=(yolo_detection_video *)calloc(1, sizeof(yolo_detection_video *)); 89 | if((*yolo_detect) == nullptr) 90 | { 91 | return yolo_cannot_alloc_yolo_detection; 92 | } 93 | } 94 | yolo_detection_video *video_detection=*yolo_detect; 95 | auto *temp=(yolo_detection_frame *)realloc(video_detection->frame_detections, sizeof(yolo_detection_frame)*(video_detection->count+1)); 96 | if(temp == nullptr) 97 | { 98 | return yolo_cannot_alloc_yolo_detection; 99 | } 100 | memset(temp+video_detection->count, 0, sizeof(yolo_detection_frame)); 101 | video_detection->frame_detections=temp; 102 | video_detection->frame_detections[video_detection->count].frame=frame_id; 103 | video_detection->frame_detections[video_detection->count].milisecond=milisecond; 104 | yolo_status yolo_stats=fill_detection(yolo, dets, &video_detection->frame_detections[video_detection->count].detection_frame, time_spent_for_classification, nboxes, thresh); 105 | ++video_detection->count; 106 | return yolo_stats; 107 | } 108 | 109 | image libyolo_ipl_to_image(IplImage *src) 110 | { 111 | int h=src->height; 112 | int w=src->width; 113 | int c=src->nChannels; 114 | image im=make_image(w, h, c); 115 | auto *data=(unsigned char *)src->imageData; 116 | int step=src->widthStep; 117 | int i, j, k; 118 | 119 | for(i=0; imutex)) 164 | { 165 | continue; 166 | } 167 | 168 | if(!thread_data->video->isOpened()) 169 | { 170 | thread_data->image_queue->common->end=true; 171 | pthread_mutex_unlock(&thread_data->mutex); 172 | break; 173 | } 174 | 175 | if(!thread_data->video->grab()) 176 | { 177 | thread_data->image_queue->common->end=true; 178 | pthread_mutex_unlock(&thread_data->mutex); 179 | break; 180 | } 181 | 182 | if(!thread_data->number_frames_to_drop) 183 | { 184 | if(!thread_data->video->retrieve(mat)) 185 | { 186 | thread_data->image_queue->common->end=true; 187 | pthread_mutex_unlock(&thread_data->mutex); 188 | break; 189 | } 190 | thread_data->number_frames_to_drop=thread_data->number_frames_to_process_simultaneously; 191 | queue_image.milisecond=thread_data->video->get(CV_CAP_PROP_POS_MSEC); 192 | queue_image.frame_number=(long)thread_data->video->get(CV_CAP_PROP_POS_FRAMES); 193 | } 194 | else 195 | { 196 | skip=true; 197 | --thread_data->number_frames_to_drop; 198 | } 199 | pthread_mutex_unlock(&thread_data->mutex); 200 | 201 | if(skip) 202 | { 203 | continue; 204 | } 205 | 206 | if(mat.empty()) 207 | { 208 | thread_data->image_queue->common->end=true; 209 | break; 210 | } 211 | image yolo_image=libyolo_mat_to_image(mat); 212 | mat.release(); 213 | 214 | queue_image.frame=yolo_image; 215 | 216 | sem_wait(thread_data->image_queue->empty); 217 | 218 | if(pthread_mutex_lock(&thread_data->image_queue->mutex)) 219 | { 220 | sem_post(thread_data->image_queue->empty); 221 | continue; 222 | } 223 | thread_data->image_queue->queue.push_back(queue_image); 224 | pthread_mutex_unlock(&thread_data->image_queue->mutex); 225 | sem_post(thread_data->image_queue->full); 226 | } 227 | return nullptr; 228 | } 229 | 230 | void *thread_detect(void *data) 231 | { 232 | if(data == nullptr) 233 | { 234 | return nullptr; 235 | } 236 | auto *th_data=(thread_processing_image_t *)data; 237 | while(true) 238 | { 239 | if(sem_trywait(th_data->image_queue->full)) 240 | { 241 | if(th_data->image_queue->common->end) 242 | { 243 | break; 244 | } 245 | continue; 246 | } 247 | 248 | queue_image_t queue_image; 249 | bool im_got_sucessfull; 250 | if(pthread_mutex_lock(&th_data->image_queue->mutex)) 251 | { 252 | continue; 253 | } 254 | im_got_sucessfull=!th_data->image_queue->queue.empty(); 255 | if(im_got_sucessfull) 256 | { 257 | queue_image=th_data->image_queue->queue.front(); 258 | th_data->image_queue->queue.pop_front(); 259 | } 260 | pthread_mutex_unlock(&th_data->image_queue->mutex); 261 | sem_post(th_data->image_queue->empty); 262 | 263 | if(!im_got_sucessfull) 264 | { 265 | continue; 266 | } 267 | 268 | layer l=th_data->yolo->net->layers[th_data->yolo->net->n-1]; 269 | unsigned long long time; 270 | float nms=0.45; 271 | 272 | image sized=resize_image(queue_image.frame, th_data->yolo->net->w, th_data->yolo->net->h); 273 | float *X=sized.data; 274 | time=unixTimeMilis(); 275 | network_predict(th_data->yolo->net, X); 276 | 277 | int nboxes=0; 278 | detection *dets=get_network_boxes(th_data->yolo->net, queue_image.frame.w, queue_image.frame.h, th_data->thresh, 0, nullptr, 0, &nboxes); 279 | if(nms>0) 280 | { 281 | do_nms_sort(dets, l.side*l.side*l.n, l.classes, nms); 282 | } 283 | 284 | parse_detections_video(th_data->yolo, dets, th_data->yolo_detect, (unixTimeMilis()-time), queue_image.frame_number, queue_image.milisecond, nboxes, th_data->thresh); 285 | free_detections(dets, nboxes); 286 | 287 | free_image(queue_image.frame); 288 | free_image(sized); 289 | } 290 | return nullptr; 291 | } 292 | 293 | yolo_status yolo_check_before_process_filename(yolo_object *yolo, char *filename) 294 | { 295 | if(yolo == nullptr) 296 | { 297 | return yolo_object_is_not_initialized; 298 | } 299 | 300 | if(access(filename, F_OK) == -1) 301 | { 302 | fprintf(stderr, "error yolo_detect: %s\n", strerror(errno)); 303 | return yolo_image_file_is_not_exists; 304 | } 305 | 306 | if(access(filename, R_OK) == -1) 307 | { 308 | fprintf(stderr, "error yolo_detect: %s\n", strerror(errno)); 309 | return yolo_image_file_is_not_readable; 310 | } 311 | return yolo_ok; 312 | } 313 | 314 | void yolo_cleanup(yolo_object *yolo) 315 | { 316 | if(yolo == nullptr) 317 | { 318 | return; 319 | } 320 | 321 | if(yolo->net != nullptr) 322 | { 323 | free_network(yolo->net); 324 | } 325 | 326 | if(yolo->names != nullptr) 327 | { 328 | for(int i=0; iclass_number; i++) 329 | { 330 | if(yolo->names[i] != nullptr) 331 | { 332 | free(yolo->names[i]); 333 | } 334 | } 335 | free(yolo->names); 336 | } 337 | free(yolo); 338 | yolo_object **ptr_yolo=&yolo; 339 | (*ptr_yolo)=nullptr; 340 | } 341 | 342 | yolo_status yolo_init(yolo_object **yolo_obj, char *workingDir, char *datacfg, char *cfgfile, char *weightfile) 343 | { 344 | clock_t time=clock(); 345 | 346 | yolo_cleanup((*yolo_obj)); 347 | 348 | (*yolo_obj)=(yolo_object *)malloc(sizeof(yolo_object)); 349 | 350 | yolo_object *yolo=(*yolo_obj); 351 | if(!yolo) 352 | { 353 | return yolo_cannot_alloc_node_yolo_object; 354 | } 355 | memset(yolo, 0, sizeof(yolo_object)); 356 | 357 | if(access(workingDir, F_OK) == -1) 358 | { 359 | fprintf(stderr, "error yolo_init: %s\n", strerror(errno)); 360 | return yolo_working_dir_is_not_exists; 361 | } 362 | 363 | if(access(workingDir, R_OK) == -1) 364 | { 365 | fprintf(stderr, "error yolo_init: %s\n", strerror(errno)); 366 | return yolo_working_dir_is_not_readable; 367 | } 368 | char cur_dir[1024]; 369 | getcwd(cur_dir, sizeof(cur_dir)); 370 | if(chdir(workingDir) == -1) 371 | { 372 | fprintf(stderr, "%s\n", strerror(errno)); 373 | return yolo_cannot_change_to_working_dir; 374 | } 375 | 376 | if(access(cfgfile, F_OK) == -1) 377 | { 378 | fprintf(stderr, "error yolo_init: %s\n", strerror(errno)); 379 | return yolo_cfgfile_is_not_exists; 380 | } 381 | if(access(cfgfile, R_OK) == -1) 382 | { 383 | fprintf(stderr, "error yolo_init: %s\n", strerror(errno)); 384 | return yolo_cfgfile_is_not_readable; 385 | } 386 | if(access(weightfile, F_OK) == -1) 387 | { 388 | fprintf(stderr, "error yolo_init: %s\n", strerror(errno)); 389 | return yolo_weight_file_is_not_exists; 390 | } 391 | if(access(weightfile, R_OK) == -1) 392 | { 393 | fprintf(stderr, "error yolo_init: %s\n", strerror(errno)); 394 | return yolo_weight_file_is_not_readable; 395 | } 396 | yolo->net=load_network(cfgfile, weightfile, 0); 397 | 398 | if(access(datacfg, F_OK) == -1) 399 | { 400 | fprintf(stderr, "error yolo_init: %s\n", strerror(errno)); 401 | return yolo_datacfg_is_not_exists; 402 | } 403 | 404 | if(access(datacfg, R_OK) == -1) 405 | { 406 | fprintf(stderr, "error yolo_init: %s\n", strerror(errno)); 407 | return yolo_datacfg_is_not_readable; 408 | } 409 | 410 | list *options=read_data_cfg(datacfg); 411 | char *name_list=option_find_str(options, "names", "data/names.list"); 412 | 413 | if(access(name_list, F_OK) == -1) 414 | { 415 | fprintf(stderr, "error yolo_init: %s\n", strerror(errno)); 416 | return yolo_names_file_is_not_exists; 417 | } 418 | if(access(name_list, R_OK) == -1) 419 | { 420 | fprintf(stderr, "error yolo_init: %s\n", strerror(errno)); 421 | return yolo_names_file_is_not_readable; 422 | } 423 | yolo->names=get_labels(name_list); 424 | char *classes=option_find_str(options, "classes", "data/names.list"); 425 | char *bad_ptr=nullptr; 426 | long value=strtol(classes, &bad_ptr, 10); 427 | if(valueclass_number=(int)value; 430 | } 431 | 432 | set_batch_network(yolo->net, 1); 433 | srand(2222222); 434 | 435 | printf("Network configured and loaded in %f seconds\n", sec(clock()-time)); 436 | chdir(cur_dir); 437 | return yolo_ok; 438 | } 439 | 440 | yolo_status yolo_detect_image(yolo_object *yolo, yolo_detection_image **detect, char *filename, float thresh) 441 | { 442 | yolo_status status=yolo_check_before_process_filename(yolo, filename); 443 | if(status != yolo_ok) 444 | { 445 | return status; 446 | } 447 | 448 | cv::Mat mat=cv::imread(filename, CV_LOAD_IMAGE_COLOR); 449 | if(mat.empty()) 450 | { 451 | fprintf(stderr, "error yolo_detect: %s\n", strerror(errno)); 452 | return yolo_image_file_is_corrupted; 453 | } 454 | 455 | layer l=yolo->net->layers[yolo->net->n-1]; 456 | unsigned long long time; 457 | float nms=0.45; 458 | 459 | image im=libyolo_mat_to_image(mat); 460 | mat.release(); 461 | 462 | image sized=resize_image(im, yolo->net->w, yolo->net->h); 463 | float *X=sized.data; 464 | time=unixTimeMilis(); 465 | network_predict(yolo->net, X); 466 | 467 | int nboxes=0; 468 | detection *dets=get_network_boxes(yolo->net, im.w, im.h, thresh, 0.5, nullptr, 0, &nboxes); 469 | if(nms>0) 470 | { 471 | do_nms_sort(dets, l.side*l.side*l.n, l.classes, nms); 472 | } 473 | 474 | status=parse_detections_image(yolo, dets, detect, unixTimeMilis()-time, nboxes, thresh); 475 | if(status != yolo_ok) 476 | { 477 | return status; 478 | } 479 | 480 | free_detections(dets, nboxes); 481 | free_image(im); 482 | free_image(sized); 483 | 484 | return yolo_ok; 485 | } 486 | 487 | yolo_status yolo_detect_video(yolo_object *yolo, yolo_detection_video **detect, char *filename, float thresh, double fraction_frames_to_process) 488 | { 489 | yolo_status status=yolo_check_before_process_filename(yolo, filename); 490 | if(status != yolo_ok) 491 | { 492 | return status; 493 | } 494 | const size_t num_capture_image_threads=2; 495 | pthread_t *capture_image_thread; 496 | pthread_t process_image_thread; 497 | cv::VideoCapture *capture; 498 | 499 | thread_image_queue_t image_queue; 500 | thread_common_t data_image_common; 501 | thread_get_frame_t data_get_image; 502 | thread_processing_image_t data_process_image; 503 | 504 | data_image_common.end=false; 505 | image_queue.queue=std::deque(); 506 | image_queue.common=&data_image_common; 507 | 508 | data_get_image.image_queue=data_process_image.image_queue=&image_queue; 509 | 510 | data_process_image.yolo=yolo; 511 | data_process_image.thresh=thresh; 512 | data_process_image.yolo_detect=detect; 513 | 514 | data_get_image.number_frames_to_process_simultaneously=data_get_image.number_frames_to_drop=(unsigned int)floor((1/fraction_frames_to_process)-1); 515 | 516 | if(pthread_mutex_init(&data_get_image.mutex, nullptr)) 517 | { 518 | return yolo_video_cannot_alloc_base_structure; 519 | } 520 | 521 | if(pthread_mutex_init(&image_queue.mutex, nullptr)) 522 | { 523 | return yolo_video_cannot_alloc_base_structure; 524 | } 525 | 526 | image_queue.empty=sem_open("/image_empty", O_CREAT, 0644, 20); 527 | if(image_queue.empty == SEM_FAILED) 528 | { 529 | return yolo_video_cannot_alloc_base_structure; 530 | } 531 | image_queue.full=sem_open("/image_full", O_CREAT, 0644, 0); 532 | if(image_queue.full == SEM_FAILED) 533 | { 534 | return yolo_video_cannot_alloc_base_structure; 535 | } 536 | 537 | capture=new cv::VideoCapture(filename); 538 | if(!capture->isOpened()) 539 | { 540 | return yolo_cannot_open_video_stream; 541 | } 542 | data_get_image.video=capture; 543 | 544 | capture_image_thread=(pthread_t *)calloc(num_capture_image_threads, sizeof(pthread_t)); 545 | if(capture_image_thread == nullptr) 546 | { 547 | return yolo_video_cannot_alloc_base_structure; 548 | } 549 | 550 | for(size_t i=0; irelease(); 561 | delete capture; 562 | free(capture_image_thread); 563 | 564 | pthread_join(process_image_thread, nullptr); 565 | 566 | sem_close(image_queue.full); 567 | sem_close(image_queue.empty); 568 | pthread_mutex_destroy(&image_queue.mutex); 569 | image_queue.queue.clear(); 570 | 571 | pthread_mutex_destroy(&data_get_image.mutex); 572 | return yolo_ok; 573 | } 574 | 575 | void yolo_detect_free(yolo_detection_image *yolo_det) 576 | { 577 | for(size_t i=0; inum_boxes; i++) 578 | { 579 | free(yolo_det->detection[i].class_name); 580 | } 581 | free(yolo_det->detection); 582 | } 583 | 584 | void yolo_detection_image_free(yolo_detection_image **yolo) 585 | { 586 | if((*yolo) == nullptr) 587 | { 588 | return; 589 | } 590 | yolo_detect_free(*yolo); 591 | free(*yolo); 592 | (*yolo)=nullptr; 593 | } 594 | 595 | void yolo_detection_video_free(yolo_detection_video **yolo) 596 | { 597 | yolo_detection_video *yolo_det=*yolo; 598 | if(yolo_det == nullptr) 599 | { 600 | return; 601 | } 602 | for(size_t i=0; icount; ++i) 603 | { 604 | yolo_detect_free(&yolo_det->frame_detections[i].detection_frame); 605 | } 606 | free(yolo_det->frame_detections); 607 | free(yolo_det); 608 | (*yolo)=nullptr; 609 | } 610 | -------------------------------------------------------------------------------- /yolo/src/libyolo.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBYOLO_H 2 | #define LIBYOLO_H 3 | 4 | #include "darknet.h" 5 | #include "yolo_error.h" 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | typedef struct 12 | { 13 | char *class_name; 14 | float probability; 15 | box bbox; 16 | }detect; 17 | 18 | typedef struct 19 | { 20 | detect *detection; 21 | size_t num_boxes; 22 | float time_spent_for_classification; 23 | }yolo_detection_image; 24 | 25 | typedef struct 26 | { 27 | yolo_detection_image detection_frame; 28 | double milisecond; 29 | long frame; 30 | }yolo_detection_frame; 31 | 32 | typedef struct 33 | { 34 | yolo_detection_frame *frame_detections; 35 | size_t count; 36 | }yolo_detection_video; 37 | 38 | typedef struct 39 | { 40 | int class_number; 41 | char **names; 42 | float nms; 43 | network *net; 44 | }yolo_object; 45 | 46 | yolo_status yolo_init(yolo_object **yolo_obj, char *workingDir, char *datacfg, char *cfgfile, char *weightfile); 47 | yolo_status yolo_detect_image(yolo_object *yolo, yolo_detection_image **detect, char *filename, float thresh); 48 | yolo_status yolo_detect_video(yolo_object *yolo, yolo_detection_video **detect, char *filename, float thresh, double fraction_frames_to_process); 49 | 50 | void yolo_detection_image_free(yolo_detection_image **yolo); 51 | void yolo_detection_video_free(yolo_detection_video **yolo); 52 | void yolo_cleanup(yolo_object *yolo); 53 | 54 | #ifdef __cplusplus 55 | }; 56 | #endif 57 | 58 | #endif // LIBYOLO_H -------------------------------------------------------------------------------- /yolo/src/private_structs.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_YOLO_PRIVATE_STRUCTS_H 2 | #define NODE_YOLO_PRIVATE_STRUCTS_H 3 | 4 | #include "libyolo.h" 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | typedef struct 12 | { 13 | long frame_number; 14 | double milisecond; 15 | image frame; 16 | }queue_image_t; 17 | 18 | typedef struct 19 | { 20 | bool end; 21 | }thread_common_t; 22 | 23 | typedef struct 24 | { 25 | sem_t *full; 26 | sem_t *empty; 27 | pthread_mutex_t mutex; 28 | std::deque queue; 29 | 30 | thread_common_t *common; 31 | }thread_image_queue_t; 32 | 33 | typedef struct 34 | { 35 | thread_image_queue_t *image_queue; 36 | cv::VideoCapture *video; 37 | 38 | unsigned int number_frames_to_process_simultaneously; 39 | unsigned int number_frames_to_drop; 40 | pthread_mutex_t mutex; 41 | }thread_get_frame_t; 42 | 43 | typedef struct 44 | { 45 | thread_image_queue_t *image_queue; 46 | 47 | yolo_object *yolo; 48 | float thresh; 49 | yolo_detection_video **yolo_detect; 50 | }thread_processing_image_t; 51 | 52 | #endif //NODE_YOLO_PRIVATE_STRUCTS_H -------------------------------------------------------------------------------- /yolo/src/yolo_error.c: -------------------------------------------------------------------------------- 1 | #include "yolo_error.h" 2 | 3 | yolo_status_detailed yolo_status_decode(yolo_status status) 4 | { 5 | yolo_status_detailed status_detailed; 6 | status_detailed.error_code=status; 7 | switch(status) 8 | { 9 | case yolo_instanciation: 10 | status_detailed.error_message="Cannot instantiate due an error."; 11 | break; 12 | case yolo_cannot_realloc_detect: 13 | status_detailed.error_message="Cannot allocate detect in memory"; 14 | break; 15 | case yolo_cannot_alloc_yolo_detection: 16 | status_detailed.error_message="Cannot allocate yolo_detection in memory"; 17 | break; 18 | case yolo_cannot_alloc_node_yolo_object: 19 | status_detailed.error_message="Cannot allocate node_yolo_object in memory"; 20 | break; 21 | case yolo_cannot_alloc_map: 22 | status_detailed.error_message="Cannot allocate map in memory"; 23 | break; 24 | case yolo_cannot_change_to_working_dir: 25 | status_detailed.error_message="Cannot change to working directory"; 26 | break; 27 | case yolo_object_is_not_initialized: 28 | status_detailed.error_message="yolo_object isn't allocated in memory"; 29 | break; 30 | case yolo_working_dir_is_not_exists: 31 | status_detailed.error_message="working directory don't exists"; 32 | break; 33 | case yolo_datacfg_is_not_exists: 34 | status_detailed.error_message="datacfg don't exists"; 35 | break; 36 | case yolo_cfgfile_is_not_exists: 37 | status_detailed.error_message="cfgfile don't exists"; 38 | break; 39 | case yolo_weight_file_is_not_exists: 40 | status_detailed.error_message="weight file don't exists"; 41 | break; 42 | case yolo_working_dir_is_not_readable: 43 | status_detailed.error_message="working directory isn't readable"; 44 | break; 45 | case yolo_datacfg_is_not_readable: 46 | status_detailed.error_message="datacfg isn't readable"; 47 | break; 48 | case yolo_cfgfile_is_not_readable: 49 | status_detailed.error_message="cfgfile isn't readable"; 50 | break; 51 | case yolo_weight_file_is_not_readable: 52 | status_detailed.error_message="weight file isn't readable"; 53 | break; 54 | case yolo_names_file_is_not_exists: 55 | status_detailed.error_message="names file don't exists"; 56 | break; 57 | case yolo_names_file_is_not_readable: 58 | status_detailed.error_message="names file isn't readable"; 59 | break; 60 | case yolo_image_file_is_not_exists: 61 | status_detailed.error_message="image file isn't exists"; 62 | break; 63 | case yolo_image_file_is_not_readable: 64 | status_detailed.error_message="image file isn't readable"; 65 | break; 66 | case yolo_image_file_is_corrupted: 67 | status_detailed.error_message="image file is corrupted"; 68 | break; 69 | // case yolo_napi_create_object_time_spent_for_classification_double_failed: 70 | // status_detailed.error_message="image file is corrupted"; 71 | // break; 72 | // case yolo_napi_create_object_time_spent_for_classification_named_property_failed: 73 | // status_detailed.error_message="image file is corrupted"; 74 | // break; 75 | // case yolo_napi_set_array_property_failed: 76 | // status_detailed.error_message="image file is corrupted"; 77 | // break; 78 | // case yolo_napi_create_main_object_failed: 79 | // status_detailed.error_message="image file is corrupted"; 80 | // break; 81 | default: 82 | status_detailed.error_message="Unknow error"; 83 | } 84 | return status_detailed; 85 | } -------------------------------------------------------------------------------- /yolo/src/yolo_error.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_YOLO_YOLO_ERROR_H 2 | #define NODE_YOLO_YOLO_ERROR_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | typedef struct 9 | { 10 | int error_code; 11 | char *error_message; 12 | }yolo_status_detailed; 13 | 14 | typedef enum 15 | { 16 | yolo_ok, 17 | yolo_instanciation, 18 | yolo_cannot_alloc_node_yolo_object, 19 | yolo_cannot_alloc_map, 20 | yolo_cannot_alloc_yolo_detection, 21 | yolo_cannot_realloc_detect, 22 | yolo_cannot_change_to_working_dir, 23 | yolo_object_is_not_initialized, 24 | yolo_working_dir_is_not_exists, 25 | yolo_datacfg_is_not_exists, 26 | yolo_cfgfile_is_not_exists, 27 | yolo_weight_file_is_not_exists, 28 | yolo_working_dir_is_not_readable, 29 | yolo_datacfg_is_not_readable, 30 | yolo_cfgfile_is_not_readable, 31 | yolo_weight_file_is_not_readable, 32 | yolo_names_file_is_not_exists, 33 | yolo_names_file_is_not_readable, 34 | yolo_image_file_is_not_exists, 35 | yolo_image_file_is_not_readable, 36 | yolo_image_file_is_corrupted, 37 | 38 | yolo_video_cannot_alloc_base_structure, 39 | yolo_cannot_open_video_stream, yolo_error_getting_fps, 40 | 41 | yolo_unknow_error 42 | }yolo_status; 43 | 44 | yolo_status_detailed yolo_status_decode(yolo_status status); 45 | 46 | #ifdef __cplusplus 47 | } 48 | #endif 49 | 50 | #endif //NODE_YOLO_YOLO_ERROR_H 51 | --------------------------------------------------------------------------------