1
0
Fork 0
mirror of https://gitlab.com/Shinobi-Systems/ShinobiCE.git synced 2025-03-09 15:40:15 +00:00

Coy Cobra

This commit is contained in:
Moe 2018-09-01 13:16:57 -07:00
parent 157bf6feb0
commit c8b67c57b4
45 changed files with 3076 additions and 144 deletions

5
plugins/python-yolo/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
conf.json
cascades
cfg
weights
data

View file

@ -0,0 +1,109 @@
#!/bin/bash
echo "-----------------------------------------------"
echo "-- Installing Python Yolo Plugin for Shinobi --"
echo "-----------------------------------------------"
echo "-----------------------------------"
if [ ! -d "weights" ]; then
echo "Downloading yolov3 weights..."
mkdir weights
wget -O weights/yolov3.weights https://pjreddie.com/media/files/yolov3.weights
else
echo "yolov3 weights found..."
fi
echo "-----------------------------------"
if [ ! -d "cfg" ]; then
echo "Downloading yolov3 cfg"
mkdir cfg
wget -O cfg/coco.data https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/coco.data
wget -O cfg/yolov3.cfg https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/yolov3.cfg
else
echo "yolov3 cfg found..."
fi
echo "-----------------------------------"
if [ ! -d "data" ]; then
echo "Downloading yolov3 data"
mkdir data
wget -O data/coco.names https://raw.githubusercontent.com/pjreddie/darknet/master/data/coco.names
else
echo "yolov3 data found..."
fi
echo "-----------------------------------"
if [ ! -e "./conf.json" ]; then
echo "Creating conf.json"
sudo cp conf.sample.json conf.json
else
echo "conf.json already exists..."
fi
echo "-----------------------------------"
sudo apt update -y
sudo apt-get install libxml2-dev libxslt-dev libxslt1-dev zlib1g-dev -y
echo "Installing python3"
sudo apt install python3 python3-dev python3-pip -y
sudo apt install python3-lxml libxml2-dev -y
echo "-----------------------------------"
if ! [ -x "$(command -v gcc-6)" ]; then
echo "Installing gcc-6 and g++-6"
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
sudo apt-get install gcc-6 g++-6 -y && sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 60 --slave /usr/bin/g++ g++ /usr/bin/g++-6
else
echo "gcc-6 and g++-6 found..."
fi
echo "-----------------------------------"
if ! [ -x "$(command -v nvidia-smi)" ]; then
echo "You need to install NVIDIA Drivers to use this."
echo "inside the Shinobi directory run the following :"
echo "sh INSTALL/cuda9-part1.sh"
exit 1
else
echo "NVIDIA Drivers found..."
echo "$(nvidia-smi |grep 'Driver Version')"
fi
echo "-----------------------------------"
if [ ! -d "/usr/local/cuda" ]; then
echo "You need to install CUDA Toolkit to use this."
echo "inside the Shinobi directory run the following :"
echo "sh INSTALL/cuda9-part2-after-reboot.sh"
exit 1
else
echo "CUDA Toolkit found..."
fi
echo "-----------------------------------"
if ! [ -x "$(command -v opencv_version)" ]; then
echo "You need to install OpenCV with CUDA first."
echo "inside the Shinobi directory run the following :"
echo "sh INSTALL/opencv-cuda.sh"
exit 1
else
echo "OpenCV found... : $(opencv_version)"
fi
echo "-----------------------------------"
echo "Getting new pip..."
pip3 install --upgrade pip
pip install --user --upgrade pip
echo "Smoking pips..."
pip3 install flask_socketio
pip3 install flask
pip3 install numpy
pip3 install gevent gevent-websocket
export PATH=/usr/local/cuda/bin:$PATH
pip3 install lxml
pip3 install numpy
pip3 install cython
echo "Installing Darknet..."
cd /opt
git clone https://github.com/pjreddie/darknet.git darknet
cd darknet
make
cd ..
echo "Installing YOLO3-4-Py"
echo "Learn more about this wrapper here : https://github.com/madhawav/YOLO3-4-Py"
git clone https://github.com/madhawav/YOLO3-4-Py.git YOLO3-4-Py
cd YOLO3-4-Py
export GPU=1
export OPENCV=1
pip3 install .
apt remove libpython-all-dev python-all python-all-dev python-asn1crypto python-cffi-backend python-crypto python-cryptography python-dbus python-enum34 python-gi python-idna python-ipaddress python-keyring python-keyrings.alt python-pkg-resources python-secretstorage python-setuptools python-six python-wheel python-xdg -y
echo "Done Installing Darknet..."
export PATH=/opt/darknet:$PATH
echo "Start the plugin with pm2 like so :"
echo "pm2 start shinobi-python-yolo.js"

View file

@ -0,0 +1,72 @@
# Python Yolo
> This plugin requires the use of port `7990` by default. You can specify a different port by adding `pythonPort` to your plugin's conf.json.
**Ubuntu and Debian only**
Go to the Shinobi directory. **/home/Shinobi** is the default directory.
```
cd /home/Shinobi/plugins/python-yolo
```
Copy the config file.
```
sh INSTALL.sh
```
Start the plugin.
```
pm2 start shinobi-python-yolo.js
```
Doing this will reveal options in the monitor configuration. Shinobi does not need to be restarted when a plugin is initiated or stopped.
## Run the plugin as a Host
> The main app (Shinobi) will be the client and the plugin will be the host. The purpose of allowing this method is so that you can use one plugin for multiple Shinobi instances. Allowing you to easily manage connections without starting multiple processes.
Edit your plugins configuration file. Set the `hostPort` **to be different** than the `listening port for camera.js`.
```
nano conf.json
```
Here is a sample of a Host configuration for the plugin.
- `plug` is the name of the plugin corresponding in the main configuration file.
- `https` choose if you want to use SSL or not. Default is `false`.
- `hostPort` can be any available port number. **Don't make this the same port number as Shinobi.** Default is `8082`.
- `type` tells the main application (Shinobi) what kind of plugin it is. In this case it is a detector.
```
{
"plug":"PythonYolo",
"hostPort":8082,
"key":"SomeOpenALPRkeySoPeopleDontMessWithYourShinobi",
"mode":"host",
"type":"detector"
}
```
Now modify the **main configuration file** located in the main directory of Shinobi. *Where you currently should be.*
```
nano conf.json
```
Add the `plugins` array if you don't already have it. Add the following *object inside the array*.
```
"plugins":[
{
"id" : "PythonYolo",
"https" : false,
"host" : "localhost",
"port" : 8082,
"key" : "SomeOpenALPRkeySoPeopleDontMessWithYourShinobi",
"mode" : "host",
"type" : "detector"
}
],
```

View file

@ -0,0 +1 @@
python3 -u $@

View file

@ -0,0 +1,10 @@
{
"plug":"PythonYolo",
"host":"localhost",
"port":8080,
"pythonPort":7990,
"hostPort":8082,
"key":"SomeOpenALPRkeySoPeopleDontMessWithYourShinobi",
"mode":"client",
"type":"detector"
}

View file

@ -0,0 +1,14 @@
#!/usr/bin/env bash
echo "Downloading config files..."
mkdir cfg
wget -O cfg/coco.data https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/coco.data
wget -O cfg/yolov3.cfg https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/yolov3.cfg
mkdir data
wget -O data/coco.names https://raw.githubusercontent.com/pjreddie/darknet/master/data/coco.names
echo "Downloading yolov3 weights"
mkdir weights
wget -O weights/yolov3.weights https://pjreddie.com/media/files/yolov3.weights

View file

@ -0,0 +1,18 @@
{
"name": "shinobi-python-yolo",
"version": "1.0.0",
"description": "YOLOv3 plugin for Shinobi that uses Python functions for detection.",
"main": "shinobi-python-yolo.js",
"dependencies": {
"socket.io-client": "^1.7.4",
"express": "^4.16.2",
"moment": "^2.19.2",
"socket.io": "^2.0.4"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Moe Alam",
"license": "ISC"
}

View file

@ -0,0 +1,103 @@
from flask import Flask, request, jsonify, render_template
from flask_socketio import SocketIO, emit
from pydarknet import Detector, Image
import cv2
import os
import json
import numpy as np
import sys
dirname = sys.argv[1]
try:
with open("{}/conf.json".format(dirname)) as json_file:
config = json.load(json_file)
httpPort = config['pythonPort']
try:
httpPort
except NameError:
httpPort = 7990
except Exception as e:
print("conf.json not found.")
httpPort = 7990
# Load Flask
app = Flask("YOLOv3 for Shinobi (Pumpkin Pie)")
socketio = SocketIO(app)
# Silence Flask
import logging
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)
# Load Darknet
net = Detector(bytes("{}/cfg/yolov3.cfg".format(dirname), encoding="utf-8"), bytes("{}/weights/yolov3.weights".format(dirname), encoding="utf-8"), 0, bytes("{}/cfg/coco.data".format(dirname),encoding="utf-8"))
def spark(filepath):
try:
filepath
except NameError:
return "File path not found."
img = cv2.imread(filepath)
img2 = Image(img)
# r = net.classify(img2)
results = net.detect(img2)
returnData = '[]'
try:
new_list = []
for item in results:
sub_list = {}
i = 0
for sub_item in item:
if i == 0:
key = 'tag'
sub_list[key] = sub_item.decode('utf-8')
if i == 1:
key = 'confidence'
sub_list[key] = sub_item
if i == 2:
key = 'points'
points_list = []
for points_item in sub_item:
points_list.append(points_item)
sub_list[key] = points_list
i += 1
new_list.append(sub_list)
returnData = new_list
# returnData = json.dumps(results)
except Exception as e:
returnData = ',\n'.join(map(str, results))
return returnData
# bake the image data by a file path
# POST body contains the "img" variable. The value should be to a local image path.
# Example : /dev/shm/streams/[GROUP_KEY]/[MONITOR_ID]/s.jpg
@app.route('/', methods=['GET'])
def index():
return "Pumpkin.py is running. This web interface should NEVER be accessible remotely. The Node.js plugin that runs this script should only be allowed accessible remotely."
# bake the image data by a file path
# POST body contains the "img" variable. The value should be to a local image path.
# Example : /dev/shm/streams/[GROUP_KEY]/[MONITOR_ID]/s.jpg
@app.route('/post', methods=['POST'])
def post():
filepath = request.form['img']
return jsonify(spark(filepath))
# bake the image data by a file path
# GET string contains the "img" variable. The value should be to a local image path.
# Example : /dev/shm/streams/[GROUP_KEY]/[MONITOR_ID]/s.jpg
@app.route('/get', methods=['GET'])
def get():
filepath = request.args.get('img')
return jsonify(spark(filepath))
@socketio.on('f')
def receiveMessage(message):
emit('f',{'id':message.get("id"),'data':spark(message.get("path"))})
# quick-and-dirty start
if __name__ == '__main__':
socketio.run(app, port=httpPort)

View file

@ -0,0 +1,296 @@
//
// Shinobi - Python YOLOv3 Plugin
// Copyright (C) 2016-2025 Moe Alam, moeiscool
//
// # Donate
//
// If you like what I am doing here and want me to continue please consider donating :)
// PayPal : paypal@m03.ca
//
process.on('uncaughtException', function (err) {
console.error('uncaughtException',err);
});
//main vars
var fs=require('fs');
var exec = require('child_process').exec;
var spawn = require('child_process').spawn;
var moment = require('moment');
var http = require('http');
var express = require('express');
var socketIoClient = require('socket.io-client');
var config = require('./conf.json');
var http = require('http'),
app = express(),
server = http.createServer(app);
s={
group:{},
dir:{},
isWin:(process.platform==='win32'),
s:function(json){return JSON.stringify(json,null,3)}
}
s.checkCorrectPathEnding=function(x){
var length=x.length
if(x.charAt(length-1)!=='/'){
x=x+'/'
}
return x.replace('__DIR__',__dirname)
}
s.debugLog = function(){
if(config.debugLog === true){
console.log(new Date(),arguments)
if(config.debugLogVerbose === true){
console.log(new Error())
}
}
}
if(!config.port){config.port=8080}
if(!config.pythonScript){config.pythonScript=__dirname+'/pumpkin.py'}
if(!config.pythonPort){config.pythonPort=7990}
if(!config.hostPort){config.hostPort=8082}
if(config.systemLog===undefined){config.systemLog=true}
if(config.alprConfig===undefined){config.alprConfig=__dirname+'/openalpr.conf'}
//default stream folder check
if(!config.streamDir){
if(s.isWin===false){
config.streamDir='/dev/shm'
}else{
config.streamDir=config.windowsTempDir
}
if(!fs.existsSync(config.streamDir)){
config.streamDir=__dirname+'/streams/'
}else{
config.streamDir+='/streams/'
}
}
s.dir.streams=config.streamDir;
//streams dir
if(!fs.existsSync(s.dir.streams)){
fs.mkdirSync(s.dir.streams);
}
s.gid=function(x){
if(!x){x=10};var t = "";var p = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for( var i=0; i < x; i++ )
t += p.charAt(Math.floor(Math.random() * p.length));
return t;
};
s.getRequest = function(url,callback){
return http.get(url, function(res){
var body = '';
res.on('data', function(chunk){
body += chunk;
});
res.on('end',function(){
try{body = JSON.parse(body)}catch(err){}
callback(body)
});
}).on('error', function(e){
// s.systemLog("Get Snapshot Error", e);
});
}
s.multiplerHeight = 0.75
s.multiplerWidth = 0.96
s.detectObject=function(buffer,d,tx){
d.tmpFile=s.gid(5)+'.jpg'
if(!fs.existsSync(s.dir.streams)){
fs.mkdirSync(s.dir.streams);
}
d.dir=s.dir.streams+d.ke+'/'
if(!fs.existsSync(d.dir)){
fs.mkdirSync(d.dir);
}
d.dir=s.dir.streams+d.ke+'/'+d.id+'/'
if(!fs.existsSync(d.dir)){
fs.mkdirSync(d.dir);
}
fs.writeFile(d.dir+d.tmpFile,buffer,function(err){
if(err) return s.systemLog(err);
if(s.isPythonRunning === false){
return console.log('Python Script is not Running.')
}
var callbackId = s.gid(10)
s.group[d.ke][d.id].sendToPython({path:d.dir+d.tmpFile,id:callbackId},function(data){
if(data.length > 0){
var mats=[]
data.forEach(function(v){
mats.push({
x:v.points[0] * s.multiplerWidth,
y:v.points[1] * s.multiplerHeight,
width:v.points[2],
height:v.points[3],
confidence:v.confidence,
tag:v.tag
})
})
tx({
f:'trigger',
id:d.id,
ke:d.ke,
details:{
plug:config.plug,
name:'yolo',
reason:'object',
matrices:mats,
imgHeight:d.mon.detector_scale_y,
imgWidth:d.mon.detector_scale_x
}
})
}
delete(s.callbacks[callbackId])
exec('rm -rf '+d.dir+d.tmpFile,{encoding:'utf8'})
})
})
}
s.systemLog=function(q,w,e){
if(w===undefined){return}
if(!w){w=''}
if(!e){e=''}
if(config.systemLog===true){
return console.log(moment().format(),q,w,e)
}
}
s.MainEventController=function(d,cn,tx){
switch(d.f){
case'init_plugin_as_host':
if(!cn){
console.log('No CN',d)
return
}
if(d.key!==config.key){
console.log(new Date(),'Plugin Key Mismatch',cn.request.connection.remoteAddress,d)
cn.emit('init',{ok:false})
cn.disconnect()
}else{
console.log(new Date(),'Plugin Connected to Client',cn.request.connection.remoteAddress)
cn.emit('init',{ok:true,plug:config.plug,notice:config.notice,type:config.type})
}
break;
case'init_monitor':
if(s.group[d.ke]&&s.group[d.ke][d.id]){
delete(s.group[d.ke][d.id].buffer)
}
break;
case'frame':
try{
if(!s.group[d.ke]){
s.group[d.ke]={}
}
if(!s.group[d.ke][d.id]){
s.group[d.ke][d.id]={
sendToPython : s.createCameraBridgeToPython(d.ke+d.id)
}
}
if(!s.group[d.ke][d.id].buffer){
s.group[d.ke][d.id].buffer=[d.frame];
}else{
s.group[d.ke][d.id].buffer.push(d.frame)
}
if(d.frame[d.frame.length-2] === 0xFF && d.frame[d.frame.length-1] === 0xD9){
s.detectObject(Buffer.concat(s.group[d.ke][d.id].buffer),d,tx)
s.group[d.ke][d.id].buffer=null;
}
}catch(err){
if(err){
s.systemLog(err)
delete(s.group[d.ke][d.id].buffer)
}
}
break;
}
}
server.listen(config.hostPort);
//web pages and plugin api
app.get('/', function (req, res) {
res.end('<b>'+config.plug+'</b> for Shinobi is running')
});
//Conector to Shinobi
if(config.mode==='host'){
//start plugin as host
var io = require('socket.io')(server);
io.attach(server);
s.connectedClients={};
io.on('connection', function (cn) {
s.connectedClients[cn.id]={id:cn.id}
s.connectedClients[cn.id].tx = function(data){
data.pluginKey=config.key;data.plug=config.plug;
return io.to(cn.id).emit('ocv',data);
}
cn.on('f',function(d){
s.MainEventController(d,cn,s.connectedClients[cn.id].tx)
});
cn.on('disconnect',function(d){
delete(s.connectedClients[cn.id])
})
});
}else{
//start plugin as client
if(!config.host){config.host='localhost'}
var io = socketIoClient('ws://'+config.host+':'+config.port);//connect to master
s.cx=function(x){x.pluginKey=config.key;x.plug=config.plug;return io.emit('ocv',x)}
io.on('connect',function(d){
s.cx({f:'init',plug:config.plug,notice:config.notice,type:config.type});
})
io.on('disconnect',function(d){
io.connect();
})
io.on('f',function(d){
s.MainEventController(d,null,s.cx)
})
}
//Start Python Controller
s.callbacks = {}
s.createCameraBridgeToPython = function(uniqueId){
var pythonIo = socketIoClient('ws://localhost:'+config.pythonPort,{transports : ['websocket']});
var sendToPython = function(data,callback){
s.callbacks[data.id] = callback
pythonIo.emit('f',data)
}
pythonIo.on('connect',function(d){
s.debugLog(uniqueId+' is Connected from Python')
})
pythonIo.on('disconnect',function(d){
s.debugLog(uniqueId+' is Disconnected from Python')
setTimeout(function(){
pythonIo.connect();
s.debugLog(uniqueId+' is Attempting to Reconect to Python')
},3000)
})
pythonIo.on('f',function(d){
if(s.callbacks[d.id]){
s.callbacks[d.id](d.data)
delete(s.callbacks[d.id])
}
})
return sendToPython
}
//Start Python Daemon
process.env.PYTHONUNBUFFERED = 1;
s.createPythonProcess = function(){
s.isPythonRunning = false
s.pythonScript = spawn('sh',[__dirname+'/bootPy.sh',config.pythonScript,__dirname]);
var onStdErr = function (data) {
s.debugLog('Python ERR')
data = data.toString()
s.debugLog(data)
if(data.indexOf('Done!') > -1){
console.log('PYTHON READY')
s.isPythonRunning = true
onStdErr = function(data){
s.debugLog(data.toString())
}
}
}
s.pythonScript.stderr.on('data',onStdErr);
s.pythonScript.stdout.on('data', function (data) {
s.debugLog('Python OUT')
s.debugLog(data.toString())
});
s.pythonScript.on('close', function () {
s.debugLog('Python CLOSED')
});
}
s.createPythonProcess()