Initial commit

This commit is contained in:
Matt McWilliams 2024-10-16 19:57:08 -04:00
commit 23c54057cd
19 changed files with 2673 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules
.env
*.DS_Store

4
default.env Normal file
View File

@ -0,0 +1,4 @@
PORT=7474
BIN=openpose
CWD=.

16
dist/env/index.js vendored Normal file
View File

@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.envString = envString;
exports.envFloat = envFloat;
exports.envInt = envInt;
function envString(variable, defaultString) {
return typeof process.env[variable] !== 'undefined' ? process.env[variable] : defaultString;
}
function envFloat(variable, defaultFloat) {
return typeof process.env[variable] !== 'undefined' ? parseFloat(process.env[variable]) : defaultFloat;
}
function envInt(variable, defaultInt) {
return typeof process.env[variable] !== 'undefined' ? parseInt(process.env[variable]) : defaultInt;
}
module.exports = { envString, envFloat, envInt };
//# sourceMappingURL=index.js.map

1
dist/env/index.js.map vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/env/index.ts"],"names":[],"mappings":";;AAAA,8BAEC;AAED,4BAEC;AAED,wBAEC;AAVD,SAAgB,SAAS,CAAE,QAAiB,EAAE,aAAsB;IACnE,OAAO,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;AAC7F,CAAC;AAED,SAAgB,QAAQ,CAAE,QAAiB,EAAE,YAAqB;IACjE,OAAO,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;AACxG,CAAC;AAED,SAAgB,MAAM,CAAE,QAAiB,EAAE,UAAmB;IAC7D,OAAO,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;AACpG,CAAC;AAED,MAAM,CAAC,OAAO,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC"}

155
dist/index.js vendored Normal file
View File

@ -0,0 +1,155 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
require("dotenv/config");
const express_1 = __importDefault(require("express"));
const promises_1 = require("fs/promises");
const os_1 = require("os");
const path_1 = require("path");
const body_parser_1 = __importDefault(require("body-parser"));
const multer_1 = __importDefault(require("multer"));
const uuid_1 = require("uuid");
const log_1 = require("./log");
const env_1 = require("./env");
const shell_1 = require("./shell");
const PORT = (0, env_1.envInt)('PORT', 7474);
const BIN = (0, env_1.envString)('BIN', 'openpose');
const CWD = (0, env_1.envString)('CWD', '.');
const log = (0, log_1.createLog)('openpose_api');
const app = (0, express_1.default)();
const tmp = (0, os_1.tmpdir)();
const storage = multer_1.default.diskStorage({
destination: function (req, file, cb) {
cb(null, tmp);
},
filename: function (req, file, cb) {
cb(null, `${+new Date()}_${file.originalname}`);
}
});
async function exists(path) {
try {
await (0, promises_1.access)(path);
return true;
}
catch {
return false;
}
}
app.use(body_parser_1.default.json());
app.use(body_parser_1.default.urlencoded({ extended: true }));
const uploadImage = (0, multer_1.default)({ storage });
app.get('/', (req, res, next) => {
return res.send('/');
});
//./build/examples/openpose/openpose.bin -v 1 -image_dir ./examples/media/ -write_images ./output -write_json ./json --face --hand
app.post('/openpose', uploadImage.single('image'), async (req, res, next) => {
const id = (0, uuid_1.v4)();
const dir = (0, path_1.join)(tmp, id);
const image_dir = (0, path_1.join)(dir, 'image');
const json_dir = (0, path_1.join)(dir, 'json');
const json_file = (0, path_1.join)(json_dir, 'openpose_keypoints.json');
let json_exists = false;
let openpose_str = '{}';
let output;
let image_file;
let image_ext;
let sh;
//
const args = [
BIN,
'--render_pose', '0',
'-display', '0',
'-image_dir', image_dir,
'-write_json', json_dir
];
if (typeof req.file === 'undefined') {
return next(new Error('No image uploaded'));
}
image_ext = (0, path_1.extname)((0, path_1.basename)(req.file.path));
image_file = (0, path_1.join)(image_dir, `openpose${image_ext}`);
if (typeof req.body.face !== 'undefined' && req.body.face === 'true') {
args.push('--face');
}
if (typeof req.body.hand !== 'undefined' && req.body.hand === 'true') {
args.push('--hand');
}
try {
await (0, promises_1.mkdir)(dir);
}
catch (err) {
log.error(`Error making directory ${dir}`, err);
return next(new Error('Error making dir'));
}
try {
await (0, promises_1.mkdir)(image_dir);
}
catch (err) {
log.error(`Error making directory ${image_dir}`, err);
return next(new Error('Error making image_dir'));
}
try {
await (0, promises_1.mkdir)(json_dir);
}
catch (err) {
log.error(`Error making directory ${image_dir}`, err);
return next(new Error('Error making image_dir'));
}
try {
await (0, promises_1.copyFile)(req.file.path, image_file);
}
catch (err) {
log.error(`Error copying file ${req.file.path}`, err);
return next(new Error('Error copying temp image'));
}
sh = new shell_1.Shell(args, CWD, null, null, null, true);
try {
await sh.execute();
}
catch (err) {
log.error(`Error executing ${args.join(' ')}`, err);
return next(new Error('Error running openpose'));
}
try {
json_exists = await exists(json_file);
}
catch (err) {
log.error(`Error checking json ${json_file} exists`, err);
return next(new Error('Error checking json exists'));
}
if (json_exists) {
try {
openpose_str = await (0, promises_1.readFile)(json_file, 'utf8');
}
catch (err) {
log.error(`Error reading json file ${json_file}`, err);
return next(new Error('Error reading json file'));
}
}
else {
log.error(`The json file ${json_file} does not exist`);
return next(new Error('Error running openpose'));
}
try {
output = JSON.parse(openpose_str);
}
catch (err) {
log.error(`Error parsing openpose json`, err);
return next(new Error('Error parsing openpose json'));
}
output.image = req.file.originalname;
try {
await (0, promises_1.rm)(dir, { recursive: true });
}
catch (err) {
log.error(`Error unlinking ${dir}`, err);
return next(new Error('Error unlinking temp dir'));
}
return res.json(output);
});
app.listen(PORT, () => {
log.info(`Openpose API server running on port ${PORT}`);
log.info(`Using openpose binary ${BIN}`);
});
//# sourceMappingURL=index.js.map

1
dist/index.js.map vendored Normal file

File diff suppressed because one or more lines are too long

48
dist/log/index.js vendored Normal file
View File

@ -0,0 +1,48 @@
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.createLog = createLog;
/** @module log */
/** Wrapper for winston that tags streams and optionally writes files with a simple interface. */
/** Module now also supports optional papertrail integration, other services to follow */
const winston_1 = require("winston");
const { SPLAT } = require('triple-beam');
const { isObject } = require('lodash');
const APP_NAME = process.env.APP_NAME || 'default';
function formatObject(param) {
if (isObject(param)) {
return JSON.stringify(param);
}
return param;
}
const all = (0, winston_1.format)((info) => {
const splat = info[SPLAT] || [];
const message = formatObject(info.message);
const rest = splat.map(formatObject).join(' ');
info.message = `${message} ${rest}`;
return info;
});
const myFormat = winston_1.format.printf(({ level, message, label, timestamp }) => {
return `${timestamp} [${label}] ${level}: ${message}`;
});
/**
* Returns a winston logger configured to service
*
* @param {string} label Label appearing on logger
* @param {string} filename Optional file to write log to
*
* @returns {object} Winston logger
*/
function createLog(label, filename = null) {
const tports = [new (winston_1.transports.Console)()];
const fmat = winston_1.format.combine(all(), winston_1.format.label({ label }), winston_1.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }), winston_1.format.colorize(), myFormat);
let papertrailOpts;
if (filename !== null) {
tports.push(new (winston_1.transports.File)({ filename }));
}
return (0, winston_1.createLogger)({
format: fmat,
transports: tports
});
}
module.exports = { createLog };
//# sourceMappingURL=index.js.map

1
dist/log/index.js.map vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/log/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAuCZ,8BAmBC;AAxDD,kBAAkB;AAClB,iGAAiG;AACjG,yFAAyF;AAEzF,qCAA2D;AAC3D,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;AACzC,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEvC,MAAM,QAAQ,GAAY,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,SAAS,CAAC;AAE5D,SAAS,YAAY,CAAE,KAAW;IAChC,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,GAAG,GAAG,IAAA,gBAAM,EAAC,CAAC,IAAU,EAAE,EAAE;IAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAChC,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,CAAC,OAAO,GAAG,GAAG,OAAO,IAAI,IAAI,EAAE,CAAC;IACpC,OAAO,IAAI,CAAC;AAChB,CAAC,CAAC,CAAC;AAEH,MAAM,QAAQ,GAAG,gBAAM,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAQ,EAAE,EAAE;IAC5E,OAAO,GAAG,SAAS,KAAK,KAAK,KAAK,KAAK,KAAK,OAAO,EAAE,CAAC;AACxD,CAAC,CAAC,CAAC;AAEH;;;;;;;EAOE;AACF,SAAgB,SAAS,CAAE,KAAc,EAAE,WAAoB,IAAI;IAC/D,MAAM,MAAM,GAAW,CAAE,IAAI,CAAC,oBAAU,CAAC,OAAO,CAAC,EAAE,CAAE,CAAC;IACtD,MAAM,IAAI,GAAS,gBAAM,CAAC,OAAO,CAC7B,GAAG,EAAE,EACL,gBAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,EACvB,gBAAM,CAAC,SAAS,CAAC,EAAC,MAAM,EAAE,yBAAyB,EAAC,CAAC,EACrD,gBAAM,CAAC,QAAQ,EAAE,EACjB,QAAQ,CACX,CAAC;IACF,IAAI,cAAoB,CAAC;IAEzB,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,CAAC,IAAI,CAAE,IAAI,CAAC,oBAAU,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAE,CAAC;IACvD,CAAC;IAED,OAAO,IAAA,sBAAY,EAAC;QAChB,MAAM,EAAG,IAAI;QACb,UAAU,EAAG,MAAM;KACtB,CAAC,CAAC;AACP,CAAC;AAED,MAAM,CAAC,OAAO,GAAG,EAAE,SAAS,EAAE,CAAC"}

71
dist/shell/index.js vendored Normal file
View File

@ -0,0 +1,71 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Shell = void 0;
const child_process_1 = require("child_process");
const log_1 = require("../log");
const os_1 = require("os");
class Shell {
constructor(args, cwd = null, stdio = null, stderr = null, after = null, silent = false) {
this.lines = [];
this.stdio = null;
this.stderr = null;
this.after = null;
this.silent = false;
const bin = args.shift();
this.bin = bin;
this.args = args;
this.stdio = stdio;
this.stderr = stderr;
this.silent = silent;
this.after = after;
this.cwd = cwd;
this.log = (0, log_1.createLog)(bin);
}
async execute() {
return new Promise((resolve, reject) => {
const options = {};
if (this.cwd !== null) {
options.cwd = this.cwd;
}
this.child = (0, child_process_1.spawn)(this.bin, this.args, options);
this.log.info(`Shell: ${this.bin} ${this.args.join(' ')}`);
this.child.stdout.on('data', (data) => {
if (!this.silent)
this.log.info(data);
if (this.after !== null)
this.lines.push(data);
if (this.stdio !== null) {
this.stdio(data);
}
});
this.child.stderr.on('data', (data) => {
if (!this.silent)
this.log.warn(data);
if (this.stderr !== null) {
this.stderr(data);
}
});
this.child.on('close', (code) => {
if (this.after !== null) {
this.after(this.lines.join(os_1.EOL));
}
if (code === 0) {
this.log.info(`Complete: ${this.bin} ${this.args.join(' ')}`);
return resolve(code);
}
else {
this.log.error(`Error executing: ${this.bin} ${this.args.join(' ')}`);
return reject(code);
}
});
});
}
kill() {
this.log.warn(`Killing: ${this.bin} ${this.args.join(' ')}`);
//this.child.stdin.pause();
this.child.kill();
}
}
exports.Shell = Shell;
module.exports = { Shell };
//# sourceMappingURL=index.js.map

1
dist/shell/index.js.map vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/shell/index.ts"],"names":[],"mappings":"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"}

432
notes/openpose_help.txt Normal file
View File

@ -0,0 +1,432 @@
Flags from ./src/logging.cc:
-alsologtoemail (log messages go to these email addresses in addition to
logfiles) type: string default: ""
-alsologtostderr (log messages go to stderr in addition to logfiles)
type: bool default: false
-colorlogtostderr (color messages logged to stderr (if supported by
terminal)) type: bool default: false
-drop_log_memory (Drop in-memory buffers of log contents. Logs can grow
very quickly and they are rarely read before they need to be evicted from
memory. Instead, drop them from memory as soon as they are flushed to
disk.) type: bool default: true
-log_backtrace_at (Emit a backtrace when logging at file:linenum.)
type: string default: ""
-log_dir (If specified, logfiles are written into this directory instead of
the default logging directory.) type: string default: ""
-log_link (Put additional links to the log files in this directory)
type: string default: ""
-log_prefix (Prepend the log prefix to the start of each log line)
type: bool default: true
-logbuflevel (Buffer log messages logged at this level or lower (-1 means
don't buffer; 0 means buffer INFO only; ...)) type: int32 default: 0
-logbufsecs (Buffer log messages for at most this many seconds) type: int32
default: 30
-logemaillevel (Email log messages logged at this level or higher (0 means
email all; 3 means email FATAL only; ...)) type: int32 default: 999
-logfile_mode (Log file mode/permissions.) type: int32 default: 436
-logmailer (Mailer used to send logging email) type: string
default: "/bin/mail"
-logtostderr (log messages go to stderr instead of logfiles) type: bool
default: false
-max_log_size (approx. maximum log file size (in MB). A value of 0 will be
silently overridden to 1.) type: int32 default: 1800
-minloglevel (Messages logged at a lower level than this don't actually get
logged anywhere) type: int32 default: 0
-stderrthreshold (log messages at or above this level are copied to stderr
in addition to logfiles. This flag obsoletes --alsologtostderr.)
type: int32 default: 2
-stop_logging_if_full_disk (Stop attempting to log to disk if the disk is
full.) type: bool default: false
Flags from ./src/utilities.cc:
-symbolize_stacktrace (Symbolize the stack trace in the tombstone)
type: bool default: true
Flags from ./src/vlog_is_on.cc:
-v (Show all VLOG(m) messages for m <= this. Overridable by --vmodule.)
type: int32 default: 0
-vmodule (per-module verbose level. Argument is a comma-separated list of
<module name>=<log level>. <module name> is a glob pattern, matched
against the filename base (that is, name ignoring .cc/.h./-inl.h). <log
level> overrides any value given by --v.) type: string default: ""
Flags from /build/gflags-WDCpEz/gflags-2.2.2/src/gflags.cc:
-flagfile (load flags from file) type: string default: ""
-fromenv (set flags from the environment [use 'export FLAGS_flag1=value'])
type: string default: ""
-tryfromenv (set flags from the environment if present) type: string
default: ""
-undefok (comma-separated list of flag names that it is okay to specify on
the command line even if the program does not define a flag with that
name. IMPORTANT: flags in this list that have arguments MUST use the
flag=value format) type: string default: ""
Flags from /build/gflags-WDCpEz/gflags-2.2.2/src/gflags_completions.cc:
-tab_completion_columns (Number of columns to use in output for tab
completion) type: int32 default: 80
-tab_completion_word (If non-empty, HandleCommandLineCompletions() will
hijack the process and attempt to do bash-style command line flag
completion on this value.) type: string default: ""
Flags from /build/gflags-WDCpEz/gflags-2.2.2/src/gflags_reporting.cc:
-help (show help on all flags [tip: all flags can have two dashes])
type: bool default: false currently: true
-helpfull (show help on all flags -- same as -help) type: bool
default: false
-helpmatch (show help on modules whose name contains the specified substr)
type: string default: ""
-helpon (show help on the modules named by this flag value) type: string
default: ""
-helppackage (show help on all modules in the main package) type: bool
default: false
-helpshort (show help on only the main module for this program) type: bool
default: false
-helpxml (produce an xml version of help) type: bool default: false
-version (show version and build info and exit) type: bool default: false
Flags from /home/mmcwilliams/src/openpose/include/openpose/flags.hpp:
-3d (Running OpenPose 3-D reconstruction demo: 1) Reading from a stereo
camera system. 2) Performing 3-D reconstruction from the multiple views.
3) Displaying 3-D reconstruction results. Note that it will only display
1 person. If multiple people is present, it will fail.) type: bool
default: false
-3d_min_views (Minimum number of views required to reconstruct each
keypoint. By default (-1), it will require max(2, min(4, #cameras-1))
cameras to see the keypoint in order to reconstruct it.) type: int32
default: -1
-3d_views (Complementary option for `--image_dir` or `--video`. OpenPose
will read as many images per iteration, allowing tasks such as stereo
camera processing (`--3d`). Note that `--camera_parameter_path` must be
set. OpenPose must find as many `xml` files in the parameter folder as
this number indicates.) type: int32 default: -1
-alpha_heatmap (Blending factor (range 0-1) between heatmap and original
frame. 1 will only show the heatmap, 0 will only show the frame. Only
valid for GPU rendering.) type: double default: 0.69999999999999996
-alpha_pose (Blending factor (range 0-1) for the body part rendering. 1
will show it completely, 0 will hide it. Only valid for GPU rendering.)
type: double default: 0.59999999999999998
-body (Select 0 to disable body keypoint detection (e.g., for faster but
less accurate face keypoint detection, custom hand detector, etc.), 1
(default) for body keypoint estimation, and 2 to disable its internal
body pose estimation network but still still run the greedy association
parsing algorithm) type: int32 default: 1
-caffemodel_path (The combination `--model_folder` + `--caffemodel_path`
represents the whole path to the caffemodel file. If empty, it will use
the default OpenPose CaffeModel file.) type: string default: ""
-camera (The camera index for cv::VideoCapture. Integer in the range [0,
9]. Select a negative number (by default), to auto-detect and open the
first available camera.) type: int32 default: -1
-camera_parameter_path (String with the folder where the camera parameters
are located. If there is only 1 XML file (for single video, webcam, or
images from the same camera), you must specify the whole XML file path
(ending in .xml).) type: string default: "models/cameraParameters/flir/"
-camera_resolution (Set the camera resolution (either `--camera` or
`--flir_camera`). `-1x-1` will use the default 1280x720 for `--camera`,
or the maximum flir camera resolution available for `--flir_camera`)
type: string default: "-1x-1"
-cli_verbose (If -1, it will be disabled (default). If it is a positive
integer number, it will print on the command line every `verbose` frames.
If number in the range (0,1), it will print the progress every `verbose`
times the total of frames.) type: double default: -1
-disable_blending (If enabled, it will render the results (keypoint
skeletons or heatmaps) on a black background, instead of being rendered
into the original image. Related: `part_to_show`, `alpha_pose`, and
`alpha_pose`.) type: bool default: false
-disable_multi_thread (It would slightly reduce the frame rate in order to
highly reduce the lag. Mainly useful for 1) Cases where it is needed a
low latency (e.g., webcam in real-time scenarios with low-range GPU
devices); and 2) Debugging OpenPose when it is crashing to locate the
error.) type: bool default: false
-display (Display mode: -1 for automatic selection; 0 for no display
(useful if there is no X server and/or to slightly speed up the
processing if visual output is not required); 2 for 2-D display; 3 for
3-D display (if `--3d` enabled); and 1 for both 2-D and 3-D display.)
type: int32 default: -1
-face (Enables face keypoint detection. It will share some parameters from
the body pose, e.g. `model_folder`. Note that this will considerable slow
down the performance and increase the required GPU memory. In addition,
the greater number of people on the image, the slower OpenPose will be.)
type: bool default: false
-face_alpha_heatmap (Analogous to `alpha_heatmap` but applied to face.)
type: double default: 0.69999999999999996
-face_alpha_pose (Analogous to `alpha_pose` but applied to face.)
type: double default: 0.59999999999999998
-face_detector (Kind of face rectangle detector. Select 0 (default) to
select OpenPose body detector (most accurate one and fastest one if body
is enabled), 1 to select OpenCV face detector (not implemented for
hands), 2 to indicate that it will be provided by the user, or 3 to also
apply hand tracking (only for hand). Hand tracking might improve hand
keypoint detection for webcam (if the frame rate is high enough, i.e., >7
FPS per GPU) and video. This is not person ID tracking, it simply looks
for hands in positions at which hands were located in previous frames,
but it does not guarantee the same person ID among frames.) type: int32
default: 0
-face_net_resolution (Multiples of 16 and squared. Analogous to
`net_resolution` but applied to the face keypoint detector. 320x320
usually works fine while giving a substantial speed up when multiple
faces on the image.) type: string default: "368x368"
-face_render (Analogous to `render_pose` but applied to the face. Extra
option: -1 to use the same configuration that `render_pose` is using.)
type: int32 default: -1
-face_render_threshold (Analogous to `render_threshold`, but applied to the
face keypoints.) type: double default: 0.40000000000000002
-flir_camera (Whether to use FLIR (Point-Grey) stereo camera.) type: bool
default: false
-flir_camera_index (Select -1 (default) to run on all detected flir cameras
at once. Otherwise, select the flir camera index to run, where 0
corresponds to the detected flir camera with the lowest serial number,
and `n` to the `n`-th lowest serial number camera.) type: int32
default: -1
-fps_max (Maximum processing frame rate. By default (-1), OpenPose will
process frames as fast as possible. Example usage: If OpenPose is
displaying images too quickly, this can reduce the speed so the user can
analyze better each frame from the GUI.) type: double default: -1
-frame_first (Start on desired frame number. Indexes are 0-based, i.e., the
first frame has index 0.) type: uint64 default: 0
-frame_flip (Flip/mirror each frame (e.g., for real time webcam
demonstrations).) type: bool default: false
-frame_last (Finish on desired frame number. Select -1 to disable. Indexes
are 0-based, e.g., if set to 10, it will process 11 frames (0-10).)
type: uint64 default: 18446744073709551615
-frame_rotate (Rotate each frame, 4 possible values: 0, 90, 180, 270.)
type: int32 default: 0
-frame_step (Step or gap between processed frames. E.g., `--frame_step 5`
would read and process frames 0, 5, 10, etc..) type: uint64 default: 1
-frame_undistort (If false (default), it will not undistort the image, if
true, it will undistortionate them based on the camera parameters found
in `camera_parameter_path`) type: bool default: false
-frames_repeat (Repeat frames when finished.) type: bool default: false
-fullscreen (Run in full-screen mode (press f during runtime to toggle).)
type: bool default: false
-hand (Enables hand keypoint detection. It will share some parameters from
the body pose, e.g. `model_folder`. Analogously to `--face`, it will also
slow down the performance, increase the required GPU memory and its speed
depends on the number of people.) type: bool default: false
-hand_alpha_heatmap (Analogous to `alpha_heatmap` but applied to hand.)
type: double default: 0.69999999999999996
-hand_alpha_pose (Analogous to `alpha_pose` but applied to hand.)
type: double default: 0.59999999999999998
-hand_detector (Kind of hand rectangle detector. Analogous to
`--face_detector`.) type: int32 default: 0
-hand_net_resolution (Multiples of 16 and squared. Analogous to
`net_resolution` but applied to the hand keypoint detector.) type: string
default: "368x368"
-hand_render (Analogous to `render_pose` but applied to the hand. Extra
option: -1 to use the same configuration that `render_pose` is using.)
type: int32 default: -1
-hand_render_threshold (Analogous to `render_threshold`, but applied to the
hand keypoints.) type: double default: 0.20000000000000001
-hand_scale_number (Analogous to `scale_number` but applied to the hand
keypoint detector. Our best results were found with `hand_scale_number` =
6 and `hand_scale_range` = 0.4.) type: int32 default: 1
-hand_scale_range (Analogous purpose than `scale_gap` but applied to the
hand keypoint detector. Total range between smallest and biggest scale.
The scales will be centered in ratio 1. E.g., if scaleRange = 0.4 and
scalesNumber = 2, then there will be 2 scales, 0.8 and 1.2.) type: double
default: 0.40000000000000002
-heatmaps_add_PAFs (Same functionality as `add_heatmaps_parts`, but adding
the PAFs.) type: bool default: false
-heatmaps_add_bkg (Same functionality as `add_heatmaps_parts`, but adding
the heatmap corresponding to background.) type: bool default: false
-heatmaps_add_parts (If true, it will fill op::Datum::poseHeatMaps array
with the body part heatmaps, and analogously face & hand heatmaps to
op::Datum::faceHeatMaps & op::Datum::handHeatMaps. If more than one
`add_heatmaps_X` flag is enabled, it will place then in sequential memory
order: body parts + bkg + PAFs. It will follow the order on
POSE_BODY_PART_MAPPING in `src/openpose/pose/poseParameters.cpp`. Program
speed will considerably decrease. Not required for OpenPose, enable it
only if you intend to explicitly use this information later.) type: bool
default: false
-heatmaps_scale (Set 0 to scale op::Datum::poseHeatMaps in the range
[-1,1], 1 for [0,1]; 2 for integer rounded [0,255]; and 3 for no
scaling.) type: int32 default: 2
-identification (Experimental, not available yet. Whether to enable people
identification across frames.) type: bool default: false
-ik_threads (Experimental, not available yet. Whether to enable inverse
kinematics (IK) from 3-D keypoints to obtain 3-D joint angles. By default
(0 threads), it is disabled. Increasing the number of threads will
increase the speed but also the global system latency.) type: int32
default: 0
-image_dir (Process a directory of images. Use `examples/media/` for our
default example folder with 20 images. Read all standard formats (jpg,
png, bmp, etc.).) type: string default: ""
-ip_camera (String with the IP camera URL. It supports protocols like RTSP
and HTTP.) type: string default: ""
-keypoint_scale (Scaling of the (x,y) coordinates of the final pose data
array, i.e., the scale of the (x,y) coordinates that will be saved with
the `write_json` & `write_keypoint` flags. Select `0` to scale it to the
original source resolution; `1`to scale it to the net output size (set
with `net_resolution`); `2` to scale it to the final output size (set
with `resolution`); `3` to scale it in the range [0,1], where (0,0) would
be the top-left corner of the image, and (1,1) the bottom-right one; and
4 for range [-1,1], where (-1,-1) would be the top-left corner of the
image, and (1,1) the bottom-right one. Non related with `scale_number`
and `scale_gap`.) type: int32 default: 0
-logging_level (The logging level. Integer in the range [0, 255]. 0 will
output any opLog() message, while 255 will not output any. Current
OpenPose library messages are in the range 0-4: 1 for low priority
messages and 4 for important ones.) type: int32 default: 3
-maximize_positives (It reduces the thresholds to accept a person
candidate. It highly increases both false and true positives. I.e., it
maximizes average recall but could harm average precision.) type: bool
default: false
-model_folder (Folder path (absolute or relative) where the models (pose,
face, ...) are located.) type: string default: "models/"
-model_pose (Model to be used. E.g., `BODY_25` (fastest for CUDA version,
most accurate, and includes foot keypoints), `COCO` (18 keypoints), `MPI`
(15 keypoints, least accurate model but fastest on CPU), `MPI_4_layers`
(15 keypoints, even faster but less accurate).) type: string
default: "BODY_25"
-net_resolution (Multiples of 16. If it is increased, the accuracy
potentially increases. If it is decreased, the speed increases. For
maximum speed-accuracy balance, it should keep the closest aspect ratio
possible to the images or videos to be processed. Using `-1` in any of
the dimensions, OP will choose the optimal aspect ratio depending on the
user's input value. E.g., the default `-1x368` is equivalent to `656x368`
in 16:9 resolutions, e.g., full HD (1980x1080) and HD (1280x720)
resolutions.) type: string default: "-1x368"
-net_resolution_dynamic (This flag only applies to images or custom inputs
(not to video or webcam). If it is zero or a negative value, it means
that using `-1` in `net_resolution` will behave as explained in its
description. Otherwise, and to avoid out of memory errors, the `-1` in
`net_resolution` will clip to this value times the default 16/9 aspect
ratio value (which is 656 width for a 368 height). E.g.,
`net_resolution_dynamic 10 net_resolution -1x368` will clip to 6560x368
(10 x 656). Recommended 1 for small GPUs (to avoid out of memory errors
but maximize speed) and 0 for big GPUs (for maximum accuracy and speed).)
type: double default: 1
-no_gui_verbose (Do not write text on output images on GUI (e.g., number of
current frame and people). It does not affect the pose rendering.)
type: bool default: false
-num_gpu (The number of GPU devices to use. If negative, it will use all
the available GPUs in your machine.) type: int32 default: -1
-num_gpu_start (GPU device start number.) type: int32 default: 0
-number_people_max (This parameter will limit the maximum number of people
detected, by keeping the people with top scores. The score is based in
person area over the image, body part score, as well as joint score
(between each pair of connected body parts). Useful if you know the exact
number of people in the scene, so it can remove false positives (if all
the people have been detected. However, it might also include false
negatives by removing very small or highly occluded people. -1 will keep
them all.) type: int32 default: -1
-output_resolution (The image resolution (display and output). Use "-1x-1"
to force the program to use the input image resolution.) type: string
default: "-1x-1"
-part_candidates (Also enable `write_json` in order to save this
information. If true, it will fill the op::Datum::poseCandidates array
with the body part candidates. Candidates refer to all the detected body
parts, before being assembled into people. Note that the number of
candidates is equal or higher than the number of final body parts (i.e.,
after being assembled into people). The empty body parts are filled with
0s. Program speed will slightly decrease. Not required for OpenPose,
enable it only if you intend to explicitly use this information.)
type: bool default: false
-part_to_show (Prediction channel to visualize: 0 (default) for all the
body parts, 1 for the background heat map, 2 for the superposition of
heatmaps, 3 for the superposition of PAFs, 4-(4+#keypoints) for each body
part heat map, the following ones for each body part pair PAF.)
type: int32 default: 0
-process_real_time (Enable to keep the original source frame rate (e.g.,
for video). If the processing time is too long, it will skip frames. If
it is too fast, it will slow it down.) type: bool default: false
-profile_speed (If PROFILER_ENABLED was set in CMake or Makefile.config
files, OpenPose will show some runtime statistics at this frame number.)
type: int32 default: 1000
-prototxt_path (The combination `--model_folder` + `--prototxt_path`
represents the whole path to the prototxt file. If empty, it will use the
default OpenPose ProtoTxt file.) type: string default: ""
-render_pose (Set to 0 for no rendering, 1 for CPU rendering (slightly
faster), and 2 for GPU rendering (slower but greater functionality, e.g.,
`alpha_X` flags). If -1, it will pick CPU if CPU_ONLY is enabled, or GPU
if CUDA is enabled. If rendering is enabled, it will render both
`outputData` and `cvOutputData` with the original image and desired body
part to be shown (i.e., keypoints, heat maps or PAFs).) type: int32
default: -1
-render_threshold (Only estimated keypoints whose score confidences are
higher than this threshold will be rendered. Note: Rendered refers only
to visual display in the OpenPose basic GUI, not in the saved results.
Generally, a high threshold (> 0.5) will only render very clear body
parts; while small thresholds (~0.1) will also output guessed and
occluded keypoints, but also more false positives (i.e., wrong
detections).) type: double default: 0.050000000000000003
-scale_gap (Scale gap between scales. No effect unless scale_number > 1.
Initial scale is always 1. If you want to change the initial scale, you
actually want to multiply the `net_resolution` by your desired initial
scale.) type: double default: 0.25
-scale_number (Number of scales to average.) type: int32 default: 1
-tracking (Experimental, not available yet. Whether to enable people
tracking across frames. The value indicates the number of frames where
tracking is run between each OpenPose keypoint detection. Select -1
(default) to disable it or 0 to run simultaneously OpenPose keypoint
detector and tracking for potentially higher accuracy than only
OpenPose.) type: int32 default: -1
-udp_host (Experimental, not available yet. IP for UDP communication. E.g.,
`192.168.0.1`.) type: string default: ""
-udp_port (Experimental, not available yet. Port number for UDP
communication.) type: string default: "8051"
-upsampling_ratio (Upsampling ratio between the `net_resolution` and the
output net results. A value less or equal than 0 (default) will use the
network default value (recommended).) type: double default: 0
-video (Use a video file instead of the camera. Use
`examples/media/video.avi` for our default example video.) type: string
default: ""
-write_bvh (Experimental, not available yet. E.g.,
`~/Desktop/mocapResult.bvh`.) type: string default: ""
-write_coco_json (Full file path to write people pose data with JSON COCO
validation format. If foot, face, hands, etc. JSON is also desired
(`--write_coco_json_variants`), they are saved with different file name
suffix.) type: string default: ""
-write_coco_json_variant (Currently, this option is experimental and only
makes effect on car JSON generation. It selects the COCO variant for
cocoJsonSaver.) type: int32 default: 0
-write_coco_json_variants (Add 1 for body, add 2 for foot, 4 for face,
and/or 8 for hands. Use 0 to use all the possible candidates. E.g., 7
would mean body+foot+face COCO JSON.) type: int32 default: 1
-write_heatmaps (Directory to write body pose heatmaps in PNG format. At
least 1 `add_heatmaps_X` flag must be enabled.) type: string default: ""
-write_heatmaps_format (File extension and format for `write_heatmaps`,
analogous to `write_images_format`. For lossless compression, recommended
`png` for integer `heatmaps_scale` and `float` for floating values. See
`doc/02_output.md` for more details.) type: string default: "png"
-write_images (Directory to write rendered frames in `write_images_format`
image format.) type: string default: ""
-write_images_format (File extension and format for `write_images`, e.g.,
png, jpg or bmp. Check the OpenCV function cv::imwrite for all compatible
extensions.) type: string default: "png"
-write_json (Directory to write OpenPose output in JSON format. It includes
body, hand, and face pose keypoints (2-D and 3-D), as well as pose
candidates (if `--part_candidates` enabled).) type: string default: ""
-write_keypoint ((Deprecated, use `write_json`) Directory to write the
people pose keypoint data. Set format with `write_keypoint_format`.)
type: string default: ""
-write_keypoint_format ((Deprecated, use `write_json`) File extension and
format for `write_keypoint`: json, xml, yaml & yml. Json not available
for OpenCV < 3.0, use `write_json` instead.) type: string default: "yml"
-write_video (Full file path to write rendered frames in motion JPEG video
format. It might fail if the final path does not finish in `.avi`. It
internally uses cv::VideoWriter. Flag `write_video_fps` controls FPS.
Alternatively, the video extension can be `.mp4`, resulting in a file
with a much smaller size and allowing `--write_video_with_audio`.
However, that would require: 1) Ubuntu or Mac system, 2) FFmpeg library
installed (`sudo apt-get install ffmpeg`), 3) the creation temporarily of
a folder with the same file path than the final video (without the
extension) to storage the intermediate frames that will later be used to
generate the final MP4 video.) type: string default: ""
-write_video_3d (Analogous to `--write_video`, but applied to the 3D
output.) type: string default: ""
-write_video_adam (Experimental, not available yet. Analogous to
`--write_video`, but applied to Adam model.) type: string default: ""
-write_video_fps (Frame rate for the recorded video. By default, it will
try to get the input frames producer frame rate (e.g., input video or
webcam frame rate). If the input frames producer does not have a set FPS
(e.g., image_dir or webcam if OpenCV not compiled with its support), set
this value accordingly (e.g., to the frame rate displayed by the OpenPose
GUI).) type: double default: -1
-write_video_with_audio (If the input is video and the output is so too, it
will save the video with audio. It requires the output video file path
finishing in `.mp4` format (see `write_video` for details).) type: bool
default: false

1563
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

31
package.json Normal file
View File

@ -0,0 +1,31 @@
{
"name": "openpose_api",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"compile": "./node_modules/.bin/tsc -p tsconfig.json",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "mattmcw",
"license": "MIT",
"description": "",
"devDependencies": {
"@types/body-parser": "^1.19.5",
"@types/express": "^4.17.21",
"@types/multer": "^1.4.12",
"@types/node": "^22.7.5",
"@types/uuid": "^10.0.0",
"typescript": "^5.6.3"
},
"dependencies": {
"body-parser": "^1.20.3",
"dotenv": "^16.4.5",
"express": "^4.18.2",
"lodash": "^4.17.21",
"mime": "^4.0.4",
"multer": "^1.4.5-lts.1",
"triple-beam": "^1.4.1",
"uuid": "^10.0.0",
"winston": "^3.15.0"
}
}

9
scripts/test.sh Normal file
View File

@ -0,0 +1,9 @@
#!/bin/bash
# -F "hand=true" \
# -F "face=true" \
curl \
-m 240 \
-F "image=@examples/two_standing.jpg" \
'http://localhost:7474/openpose'

13
src/env/index.ts vendored Normal file
View File

@ -0,0 +1,13 @@
export function envString (variable : string, defaultString : string) : string {
return typeof process.env[variable] !== 'undefined' ? process.env[variable] : defaultString;
}
export function envFloat (variable : string, defaultFloat : number ) : number {
return typeof process.env[variable] !== 'undefined' ? parseFloat(process.env[variable]) : defaultFloat;
}
export function envInt (variable : string, defaultInt : number ) : number {
return typeof process.env[variable] !== 'undefined' ? parseInt(process.env[variable]) : defaultInt;
}
module.exports = { envString, envFloat, envInt };

166
src/index.ts Normal file
View File

@ -0,0 +1,166 @@
import 'dotenv/config';
import express from 'express';
import { Express, Request, Response, NextFunction } from 'express'
import { access, copyFile, unlink, mkdir, readFile, rm } from 'fs/promises';
import { tmpdir } from 'os';
import { join, basename, resolve, extname } from 'path';
import bodyParser from 'body-parser';
import multer, { FileFilterCallback } from 'multer';
import { v4 as uuid } from 'uuid';
import getType from 'mime';
import { createLog } from './log'
import type { Logger } from 'winston';
import { envInt, envString } from './env';
import { Shell } from './shell';
const PORT : number = envInt('PORT', 7474);
const BIN : string = envString('BIN', 'openpose');
const CWD : string = envString('CWD', '.');
const log : Logger = createLog('openpose_api');
const app : Express = express();
const tmp : string = tmpdir();
const storage = multer.diskStorage({
destination: function (req : Request, file : Express.Multer.File, cb : Function) {
cb(null, tmp)
},
filename: function (req : Request, file : Express.Multer.File, cb : Function) {
cb(null, `${+new Date()}_${file.originalname}`)
}
});
async function exists (path : string) : Promise<boolean> {
try {
await access(path);
return true;
} catch {
return false;
}
}
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
const uploadImage : any = multer({ storage });
app.get('/', (req: Request, res: Response, next : NextFunction) => {
return res.send('/');
});
//./build/examples/openpose/openpose.bin -v 1 -image_dir ./examples/media/ -write_images ./output -write_json ./json --face --hand
app.post('/openpose', uploadImage.single('image'), async (req: Request, res: Response, next : NextFunction) => {
const id : string = uuid();
const dir : string = join(tmp, id);
const image_dir : string = join(dir, 'image');
const json_dir : string = join(dir, 'json');
const json_file : string = join(json_dir, 'openpose_keypoints.json');
let json_exists : boolean = false;
let openpose_str : string = '{}';
let output : any;
let image_file : string;
let image_ext : string;
let sh : Shell;
//
const args : string[] = [
BIN,
'--render_pose', '0',
'-display', '0',
'-image_dir', image_dir,
'-write_json', json_dir
];
if (typeof req.file === 'undefined') {
return next(new Error('No image uploaded'));
}
image_ext = extname(basename(req.file.path));
image_file = join(image_dir, `openpose${image_ext}`);
if (typeof req.body.face !== 'undefined' && req.body.face === 'true') {
args.push('--face');
}
if (typeof req.body.hand !== 'undefined' && req.body.hand === 'true') {
args.push('--hand');
}
try {
await mkdir(dir);
} catch (err) {
log.error(`Error making directory ${dir}`, err);
return next(new Error('Error making dir'));
}
try {
await mkdir(image_dir);
} catch (err) {
log.error(`Error making directory ${image_dir}`, err);
return next(new Error('Error making image_dir'));
}
try {
await mkdir(json_dir);
} catch (err) {
log.error(`Error making directory ${image_dir}`, err);
return next(new Error('Error making image_dir'));
}
try {
await copyFile(req.file.path, image_file);
} catch (err) {
log.error(`Error copying file ${req.file.path}`, err);
return next(new Error('Error copying temp image'));
}
sh = new Shell(args, CWD, null, null, null, true);
try {
await sh.execute();
} catch (err) {
log.error(`Error executing ${args.join(' ')}`, err);
return next(new Error('Error running openpose'));
}
try {
json_exists = await exists(json_file);
} catch (err) {
log.error(`Error checking json ${json_file} exists`, err);
return next(new Error('Error checking json exists'));
}
if (json_exists) {
try {
openpose_str = await readFile(json_file, 'utf8');
} catch (err) {
log.error(`Error reading json file ${json_file}`, err);
return next(new Error('Error reading json file'));
}
} else {
log.error(`The json file ${json_file} does not exist`);
return next(new Error('Error running openpose'));
}
try {
output = JSON.parse(openpose_str);
} catch (err) {
log.error(`Error parsing openpose json`, err);
return next(new Error('Error parsing openpose json'));
}
output.image = req.file.originalname;
try {
await rm(dir, { recursive: true });
} catch (err) {
log.error(`Error unlinking ${dir}`, err);
return next(new Error('Error unlinking temp dir'));
}
return res.json(output);
});
app.listen(PORT, () => {
log.info(`Openpose API server running on port ${PORT}`);
log.info(`Using openpose binary ${BIN}`);
});

61
src/log/index.ts Normal file
View File

@ -0,0 +1,61 @@
'use strict'
/** @module log */
/** Wrapper for winston that tags streams and optionally writes files with a simple interface. */
/** Module now also supports optional papertrail integration, other services to follow */
import { format, transports, createLogger } from 'winston';
const { SPLAT } = require('triple-beam');
const { isObject } = require('lodash');
const APP_NAME : string = process.env.APP_NAME || 'default';
function formatObject (param : any) {
if (isObject(param)) {
return JSON.stringify(param);
}
return param;
}
const all = format((info : any) => {
const splat = info[SPLAT] || [];
const message = formatObject(info.message);
const rest = splat.map(formatObject).join(' ');
info.message = `${message} ${rest}`;
return info;
});
const myFormat = format.printf(({ level, message, label, timestamp } : any) => {
return `${timestamp} [${label}] ${level}: ${message}`;
});
/**
* Returns a winston logger configured to service
*
* @param {string} label Label appearing on logger
* @param {string} filename Optional file to write log to
*
* @returns {object} Winston logger
*/
export function createLog (label : string, filename : string = null) {
const tports : any[] = [ new (transports.Console)() ];
const fmat : any = format.combine(
all(),
format.label({ label }),
format.timestamp({format: 'YYYY-MM-DD HH:mm:ss.SSS'}),
format.colorize(),
myFormat,
);
let papertrailOpts : any;
if (filename !== null) {
tports.push( new (transports.File)({ filename }) );
}
return createLogger({
format : fmat,
transports : tports
});
}
module.exports = { createLog };

77
src/shell/index.ts Normal file
View File

@ -0,0 +1,77 @@
import { spawn, ChildProcessWithoutNullStreams } from 'child_process';
import { createLog } from '../log';
import type { Logger } from 'winston';
import { EOL } from 'os';
export class Shell {
private child : ChildProcessWithoutNullStreams;
private log : Logger;
private bin : string;
private args : any[];
private cwd : string;
private lines : string[] = [];
private stdio : Function = null;
private stderr : Function = null;
private after : Function = null;
private silent : boolean = false;
constructor (args : any[], cwd : string = null, stdio : Function = null, stderr : Function = null, after : Function = null, silent : boolean = false) {
const bin : string = args.shift();
this.bin = bin;
this.args = args;
this.stdio = stdio;
this.stderr = stderr;
this.silent = silent;
this.after = after;
this.cwd = cwd;
this.log = createLog(bin);
}
public async execute () : Promise<number> {
return new Promise((resolve : Function, reject : Function) => {
const options : any = {};
if (this.cwd !== null) {
options.cwd = this.cwd;
}
this.child = spawn(this.bin, this.args, options);
this.log.info(`Shell: ${this.bin} ${this.args.join(' ')}`);
this.child.stdout.on('data', (data : string) => {
if (!this.silent) this.log.info(data);
if (this.after !== null) this.lines.push(data);
if (this.stdio !== null) {
this.stdio(data);
}
});
this.child.stderr.on('data', (data : string) => {
if (!this.silent) this.log.warn(data);
if (this.stderr !== null) {
this.stderr(data);
}
});
this.child.on('close', (code : number) => {
if (this.after !== null) {
this.after(this.lines.join(EOL));
}
if (code === 0) {
this.log.info(`Complete: ${this.bin} ${this.args.join(' ')}`);
return resolve(code);
} else {
this.log.error(`Error executing: ${this.bin} ${this.args.join(' ')}`);
return reject(code);
}
});
});
}
public kill () {
this.log.warn(`Killing: ${this.bin} ${this.args.join(' ')}`);
//this.child.stdin.pause();
this.child.kill();
}
}
module.exports = { Shell };

20
tsconfig.json Normal file
View File

@ -0,0 +1,20 @@
{
"compilerOptions": {
"module": "commonjs",
"esModuleInterop": true,
"target": "ES2020",
"noImplicitAny": true,
"moduleResolution": "node",
"sourceMap": true,
"removeComments" : false,
"baseUrl" : "dist",
"outDir": "./dist/",
"rootDir" : "./src/",
"paths" : {
}
},
"exclude" : [
"./dist",
"./node_modules"
]
}