فهرست منبع

Add Darknet .cfg prototype

Lutz Roeder 7 سال پیش
والد
کامیت
b5fe4f6883
9فایلهای تغییر یافته به همراه664 افزوده شده و 1 حذف شده
  1. 1 0
      setup.py
  2. 1 0
      src/app.js
  3. 110 0
      src/darknet-metadata.json
  4. 462 0
      src/darknet.js
  5. 1 1
      src/view-browser.html
  6. 1 0
      src/view.js
  7. 42 0
      test/models.json
  8. 45 0
      tools/darknet
  9. 1 0
      tools/update

+ 1 - 0
setup.py

@@ -113,6 +113,7 @@ setuptools.setup(
             'caffe2.js', 'caffe2-metadata.json', 'caffe2-proto.js',
             'cntk.js', 'cntk-metadata.json', 'cntk-proto.js',
             'coreml.js', 'coreml-metadata.json', 'coreml-proto.js',
+            'darknet.js', 'darknet-metadata.json',
             'keras.js', 'keras-metadata.json', 'hdf5.js',
             'mxnet.js', 'mxnet-metadata.json',
             'openvino.js', 'openvino-parser.js',

+ 1 - 0
src/app.js

@@ -123,6 +123,7 @@ class Application {
                     'pt', 'pth', 't7',
                     'pkl', 'joblib',
                     'pbtxt', 'prototxt',
+                    'cfg',
                     'xml' ] }
             ]
         };

+ 110 - 0
src/darknet-metadata.json

@@ -0,0 +1,110 @@
+[
+  {
+      "name": "convolutional",
+      "schema": {
+          "category": "Layer",
+          "attributes": [
+            { "name": "filters", "visible": false },
+            { "name": "stride", "default": 1 },
+            { "name": "pad", "type": "boolean", "default": false },
+            { "name": "batch_normalize", "type": "boolean", "default": false },
+            { "name": "activation", "default": "logistic" }
+          ]
+      }
+  },
+  {
+    "name": "dropout",
+    "schema": {
+        "category": "Dropout",
+        "attributes": [
+          { "name": "probability", "default": 0.5 }
+        ]
+    }
+  },
+  {
+    "name": "maxpool",
+    "schema": {
+        "category": "Pool",
+        "attributes": [
+          { "name": "stride", "default": 2 }
+        ]
+    }
+  },
+  {
+    "name": "avgpool",
+    "schema": {
+        "category": "Pool"
+    }
+  },
+  {
+    "name": "connected",
+    "schema": {
+        "category": "Layer",
+        "attributes": [
+          { "name": "output", "visible": false },
+          { "name": "activation", "default": "logistic" }
+        ]
+    }
+  },
+  {
+    "name": "softmax",
+    "schema": {
+        "category": "Activation",
+        "attributes": [
+          { "name": "groups", "default": 1 }
+        ]
+    }
+  },
+  {
+    "name": "gru",
+    "schema": {
+        "category": "Pool",
+        "attributes": [
+          { "name": "output", "visible": false }
+        ]
+    }
+  },
+  {
+    "name": "rnn",
+    "schema": {
+        "category": "Layer",
+        "attributes": [
+          { "name": "output", "visible": false },
+          { "name": "hidden", "visible": false },
+          { "name": "batch_normalize", "type": "boolean", "default": false }
+        ]
+      }
+  },
+  {
+    "name": "crop",
+    "schema": {
+      "category": "Shape",
+      "attributes": [
+        { "name": "flip", "type": "boolean" },
+        { "name": "exposure", "default": 1 },
+        { "name": "saturation", "default": 1 },
+        { "name": "angle", "default": 0 }
+      ]
+    }
+  },
+  {
+    "name": "reorg",
+    "schema": {
+      "attributes": [
+        { "name": "stride", "default": 1 }
+      ]
+    }
+  },
+  {
+    "name": "shortcut",
+    "schema": {
+      "category": "Tensor"
+    }
+  },
+  {
+    "name": "route",
+    "schema": {
+      "category": "Tensor"
+    }
+  }
+]

+ 462 - 0
src/darknet.js

@@ -0,0 +1,462 @@
+
+/*jshint esversion: 6 */
+
+var darknet = darknet || {};
+var base = base || require('./base');
+
+darknet.ModelFactory = class {
+
+    match(context, host) {
+        var extension = context.identifier.split('.').pop().toLowerCase();
+        if (extension == 'cfg') {
+            return true;
+        }
+        return false;
+    }
+
+    open(context, host, callback) {
+        darknet.Metadata.open(host, (err, metadata) => {
+            var identifier = context.identifier;
+            try {
+                var reader = new darknet.CfgReader(context.text);
+                var cfg = reader.read();
+                var model = new darknet.Model(metadata, cfg);
+                callback(null, model);
+                return;
+            }
+            catch (error) {
+                var message = error && error.message ? error.message : error.toString();
+                message = message.endsWith('.') ? message.substring(0, message.length - 1) : message;
+                callback(new darknet.Error(message + " in '" + identifier + "'."), null);
+                return;
+            }
+        });
+    }
+};
+
+darknet.Model = class {
+
+    constructor(metadata, cfg) {
+        this._graphs = [];
+        this._graphs.push(new darknet.Graph(metadata, cfg));
+    }
+
+    get format() {
+        return 'Darknet';
+    }
+
+    get graphs() {
+        return this._graphs;
+    }
+};
+
+darknet.Graph = class {
+    
+    constructor(metadata, cfg) {
+        this._inputs = [];
+        this._outputs = [];
+        this._nodes = [];
+
+        var net = cfg.shift();
+
+        var inputType = null;
+        if (net && net.hasOwnProperty('width') && net.hasOwnProperty('height') && net.hasOwnProperty('channels')) {
+            var width = Number.parseInt(net.width);
+            var height = Number.parseInt(net.height);
+            var channels = Number.parseInt(net.channels);
+            inputType = new darknet.TensorType('float32', new darknet.TensorShape([ width, height, channels ]));
+        }
+
+        var input = 'input';
+        this._inputs.push(new darknet.Argument(input, true, [
+            new darknet.Connection(input, inputType, null)
+        ]));
+
+        cfg.forEach((layer, index) => {
+            layer._outputs = [ index.toString() ];
+        });
+
+        var inputs = [ 'input' ];
+        cfg.forEach((layer, index) => {
+            layer._inputs = inputs;
+            inputs = [ index.toString() ];
+            switch (layer.__type__) {
+                case 'shortcut':
+                    var shortcut = cfg[index + Number.parseInt(layer.from, 10)]._outputs[0];
+                    layer._inputs.push(shortcut);
+                    break;
+                case 'route':
+                    var routes = layer.layers.split(',').map((route) => Number.parseInt(route.trim(), 10));
+                    layer._inputs = routes.map((route) => {
+                        var layer = (route < 0) ? index + route : route;
+                        return cfg[layer]._outputs[0];
+                    });
+                    break;
+            }
+        });
+        cfg.forEach((layer, index) => {
+            this._nodes.push(new darknet.Node(metadata, layer));
+        });
+
+        if (cfg.length > 0) {
+            var lastLayer = cfg[cfg.length - 1];
+            lastLayer._outputs.forEach((output, index) => {
+                this._outputs.push(new darknet.Argument('output' + (index > 1 ? index.toString() : ''), true, [
+                    new darknet.Connection(output, null, null)
+                ]));
+            });
+        }
+    }
+
+    get inputs() {
+        return this._inputs;
+    }
+
+    get outputs() {
+        return this._outputs;
+    }
+
+    get nodes() {
+        return this._nodes;
+    }
+};
+
+darknet.Argument = class {
+
+    constructor(name, visible, connections) {
+        this._name = name;
+        this._visible = visible;
+        this._connections = connections;
+    }
+
+    get name() {
+        return this._name;
+    }
+
+    get visible() {
+        return this._visible;
+    }
+
+    get connections() {
+        return this._connections;
+    }
+};
+
+darknet.Connection = class {
+
+    constructor(id, type, initializer) {
+        this._id = id;
+        this._type = type;
+        this._initializer = initializer;
+    }
+
+    get id() {
+        return this._id;
+    }
+
+    get type() {
+        if (this._initializer) {
+            return this._initializer.type;
+        }
+        return this._type;
+    }
+
+    get initializer() {
+        return this._initializer;
+    }
+};
+
+darknet.Node = class {
+
+    constructor(metadata, layer) {
+        this._metadata = metadata;
+        this._operator = layer.__type__;
+        this._attributes = [];
+        this._inputs = [];
+        this._outputs = [];
+        if (layer._inputs && layer._inputs.length > 0) {
+            this._inputs.push(new darknet.Argument(layer._inputs.length <= 1 ? 'input' : 'inputs', true, layer._inputs.map((input) => {
+                return new darknet.Connection(input, null, null);
+            })));
+        }
+        if (layer._outputs && layer._outputs.length > 0) {
+            this._outputs.push(new darknet.Argument(layer._outputs.length <= 1 ? 'output' : 'outputs', true, layer._outputs.map((output) => {
+                return new darknet.Connection(output, null, null);
+            })));
+        }
+        switch (layer.__type__) {
+            case 'convolutional':
+                this._initializer('biases');
+                if (layer.batch_normalize == 1) {
+                    this._initializer('gamma');
+                    this._initializer('mean');
+                    this._initializer('var');
+                }
+                this._initializer('kernel');
+                break;
+            case 'connected':
+                this._initializer('biases');
+                this._initializer('weights');
+                break;
+            case 'connected':
+                this._initializer('biases');
+                this._initializer('kernels');
+                break;
+        }
+
+        switch (layer.__type__) {
+            case 'shortcut':
+                delete layer.from;
+                break;
+            case 'route':
+                delete layer.layers;
+                break;
+        }
+        Object.keys(layer).forEach((key) => {
+            if (key != '__type__' && key != '_inputs' && key != '_outputs') {
+                this._attributes.push(new darknet.Attribute(metadata, this._operator, key, layer[key]));
+            }
+        });
+    }
+
+    get operator() {
+        return this._operator;
+    }
+
+    get category() {
+        var schema = this._metadata.getSchema(this._operator);
+        return (schema && schema.category) ? schema.category : null;
+    }
+
+    get attributes() {
+        return this._attributes;
+    }
+
+    get inputs() {
+        return this._inputs;
+    }
+
+    get outputs() {
+        return this._outputs;
+    }
+
+    _initializer(name) {
+        this._inputs.push(new darknet.Argument(name, true, [
+            new darknet.Connection(name, null, new darknet.Tensor())
+        ]));
+    }
+};
+
+darknet.Attribute = class {
+
+    constructor(metadata, operator, name, value) {
+        this._name = name;
+        this._value = value;
+
+        var intValue = Number.parseInt(this._value, 10);
+        if (!Number.isNaN(this._value - intValue)) {
+            this._value = intValue;
+        }
+        else {
+            var floatValue = Number.parseFloat(this._value);
+            if (!Number.isNaN(this._value - floatValue)) {
+                this._value = floatValue;
+            }
+        }
+
+        var schema = metadata.getAttributeSchema(operator, name);
+        if (schema) {
+            if (schema.type == 'boolean') {
+                switch (this._value) {
+                    case 0: this._value = false; break;
+                    case 1: this._value = true; break;
+                }
+            }
+
+            if (schema.hasOwnProperty('visible') && !schema.visible) {
+                this._visible = false;
+            }
+            else if (schema.hasOwnProperty('default'))
+            {
+                if (this._value == schema.default) {
+                    this._visible = false;
+                }
+            }
+        }
+    }
+
+    get name() {
+        return this._name;
+    }
+
+    get value() {
+        return this._value;
+    }
+
+    get visible() {
+        return this._visible == false ? false : true;
+    }
+};
+
+darknet.Tensor = class {
+
+    constructor() {
+    }
+
+    get type() {
+        return null;
+    }
+
+    get state() {
+        return 'Tensor data not implemented.';
+    }
+
+    get value() {
+        return null;
+    }
+
+    toString() {
+        return '';
+    }
+};
+
+darknet.TensorType = class {
+
+    constructor(dataType, shape) {
+        this._dataType = dataType;
+        this._shape = shape;
+    }
+
+    get dataType() {
+        return this._dataType;
+    }
+
+    get shape() {
+        return this._shape;
+    }
+
+    toString() {
+        return (this.dataType || '?') + this._shape.toString();
+    }
+};
+
+darknet.TensorShape = class {
+
+    constructor(dimensions) {
+        this._dimensions = dimensions;
+    }
+
+    get dimensions() {
+        return this._dimensions;
+    }
+
+    toString() {
+        if (this._dimensions) {
+            if (this._dimensions.length == 0) {
+                return '';
+            }
+            return '[' + this._dimensions.map((dimension) => dimension.toString()).join(',') + ']';
+        }
+        return '';
+    }
+};
+
+darknet.Metadata = class {
+
+    static open(host, callback) {
+        if (darknet.Metadata._metadata) {
+            callback(null, darknet.Metadata._metadata);
+            return;
+        }
+        host.request(null, 'darknet-metadata.json', 'utf-8', (err, data) => {
+            darknet.Metadata._metadata = new darknet.Metadata(data);
+            callback(null, darknet.Metadata._metadata);
+            return;
+        });
+    }
+
+    constructor(data) {
+        this._map = {};
+        if (data) {
+            var items = JSON.parse(data);
+            if (items) {
+                items.forEach((item) => {
+                    if (item.name && item.schema) {
+                        this._map[item.name] = item.schema;
+                    }
+                });
+            }
+        }
+    }
+
+    getSchema(operator) {
+        return this._map[operator] || null;
+    }
+
+    getAttributeSchema(operator, name) {
+        var schema = this._map[operator];
+        if (schema && schema.attributes && schema.attributes.length > 0) {
+            if (!schema.__attributesMap) {
+                schema.__attributesMap = {};
+                schema.attributes.forEach((attribute) => {
+                    schema.__attributesMap[attribute.name] = attribute;
+                });
+            }
+            return schema.__attributesMap[name];
+        }
+        return null;
+    }
+};
+
+darknet.CfgReader = class {
+
+    constructor(text) {
+        this._lines = text.split('\n');
+        this._line = 0;
+    }
+
+    read() {
+        var array = [];
+        var item = {};
+        while (this._line < this._lines.length) {
+            var line = this._lines[this._line];
+            line = line.split('#')[0].trim();
+            if (line.length > 0) {
+                if (line.length > 3 && line[0] == '[' && line[line.length - 1] == ']') {
+                    if (item.__type__) {
+                        array.push(item);
+                        item = {};
+                    }
+                    item.__type__ = line.substring(1, line.length - 1);
+                }
+                else {
+                    var property = line.split('=');
+                    if (property.length == 2) {
+                        var key = property[0].trim();
+                        var value = property[1].trim();
+                        item[key] = value;
+                    }
+                    else {
+                        throw new darknet.Error("Invalid cfg \'" + line + "\' at line " + (this._line + 1).toString() + ".");
+                    }
+                }
+            }
+            this._line++;
+        }
+        if (item.__type__) {
+            array.push(item);
+        }
+        return array;
+    }
+}
+
+darknet.Error = class extends Error {
+    constructor(message) {
+        super(message);
+        this.name = 'Error loading Darknet model.';
+    }
+};
+
+if (typeof module !== 'undefined' && typeof module.exports === 'object') {
+    module.exports.ModelFactory = darknet.ModelFactory;
+}

+ 1 - 1
src/view-browser.html

@@ -69,7 +69,7 @@
         </svg>
     </a>
     <button id='open-file-button' class='center' style='top: 200px; width: 125px; opacity: 0;'>Open Model...</button>
-    <input type='file' id='open-file-dialog' style='display:none' multiple='false' accept='.onnx, .pb, .meta, .tflite, .keras, .h5, .hdf5, .json, .model, .dnn, .cmf, .mlmodel, .caffemodel, .pbtxt, .prototxt, .pkl, .pt, .pth, .t7, .joblib, .xml'>
+    <input type='file' id='open-file-dialog' style='display:none' multiple='false' accept='.onnx, .pb, .meta, .tflite, .keras, .h5, .hdf5, .json, .model, .dnn, .cmf, .mlmodel, .caffemodel, .pbtxt, .prototxt, .pkl, .pt, .pth, .t7, .joblib, .cfg, .xml'>
     <!-- Preload fonts to workaround Chrome SVG layout issue -->
     <div style='font-weight: normal; color: rgba(0, 0, 0, 0.01); user-select: none;'>.</div>
     <div style='font-weight: 600; color: rgba(0, 0, 0, 0.01); user-select: none;'>.</div>

+ 1 - 0
src/view.js

@@ -1080,6 +1080,7 @@ view.ModelFactoryService = class {
         this.register('./sklearn', [ '.pkl', '.joblib' ]);
         this.register('./cntk', [ '.model', '.cntk', '.cmf', '.dnn' ]);
         this.register('./openvino', [ '.xml', '.dot' ]);
+        this.register('./darknet', [ '.cfg' ]);
         this.register('./paddle', [ '.paddle', '__model__' ]);
     }
 

+ 42 - 0
test/models.json

@@ -1583,6 +1583,48 @@
     "format": "CoreML v1",
     "link":   "https://github.com/UnusualWolf/coreML"
   },
+  {
+    "type":   "darknet",
+    "target": "alexnet.cfg,alexnet.weights",
+    "source": "https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/alexnet.cfg,https://pjreddie.com/media/files/alexnet.weights",
+    "format": "Darknet",
+    "link":   "https://pjreddie.com/darknet/imagenet"
+  },
+  {
+    "type":   "darknet",
+    "target": "darknet53_448.cfg,darknet53_448.weights",
+    "source": "https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/darknet53_448.cfg,https://pjreddie.com/media/files/darknet53_448.weights",
+    "format": "Darknet",
+    "link":   "https://pjreddie.com/darknet/imagenet"
+  },
+  {
+    "type":   "darknet",
+    "target": "go.cfg,go.weights",
+    "source": "https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/go.cfg,https://pjreddie.com/media/files/go.weights",
+    "format": "Darknet",
+    "link":   "https://pjreddie.com/darknet/darkgo-go-in-darknet"
+  },
+  {
+    "type":   "darknet",
+    "target": "jnet-conv.cfg,jnet-conv.weights",
+    "source": "https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/yolov3-spp.cfg,https://pjreddie.com/media/files/yolov3-spp.weights",
+    "format": "Darknet",
+    "link":   "https://pjreddie.com/darknet/nightmare"
+  },
+  {
+    "type":   "darknet",
+    "target": "grrm.cfg,grrm.weights",
+    "source": "https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/rnn.cfg,https://pjreddie.com/media/files/grrm.weights",
+    "format": "Darknet",
+    "link":   "https://pjreddie.com/darknet/rnns-in-darknet"
+  },
+  {
+    "type":   "darknet",
+    "target": "yolov3-spp.cfg,yolov3-spp.weights",
+    "source": "https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/jnet-conv.cfg,https://pjreddie.com/media/files/jnet-conv.weights",
+    "format": "Darknet",
+    "link":   "https://pjreddie.com/darknet/yolo"
+  },
   {
     "type":   "keras",
     "target": "babi_rnn.h5",

+ 45 - 0
tools/darknet

@@ -0,0 +1,45 @@
+#!/bin/bash
+
+set -e
+
+root=$(cd $(dirname ${0})/..; pwd)
+node_modules=${root}/node_modules
+src=${root}/src
+tools=${root}/tools
+third_party=${root}/third_party
+
+identifier=darknet
+
+bold() {
+    echo "$(tty -s && tput bold)$1$(tty -s && tput sgr0)" 
+}
+
+git_sync() {
+    mkdir -p "${third_party}"
+    if [ -d "${third_party}/${1}" ]; then
+        git -C "${third_party}/${1}" fetch -p --quiet
+        git -C "${third_party}/${1}" reset --quiet --hard origin/master
+    else
+        echo "Clone ${2}..."
+        git -C "${third_party}" clone --recursive ${2}
+    fi
+    git -C "${third_party}" submodule update --init
+}
+
+clean() {
+    bold "darknet clean"
+    rm -rf ${third_party}/${identifier}
+}
+
+sync() {
+    bold "darknet sync"
+    git_sync darknet https://github.com/pjreddie/darknet.git
+}
+
+while [ "$#" != 0 ]; do
+    command="$1" && shift
+    case "${command}" in
+        "clean") clean;;
+        "sync") sync;;
+    esac
+done

+ 1 - 0
tools/update

@@ -7,6 +7,7 @@ tools=$(cd $(dirname ${0})/..; pwd)/tools
 ${tools}/caffe sync schema
 ${tools}/coreml sync install schema
 ${tools}/cntk sync schema
+${tools}/darknet sync
 ${tools}/keras sync install metadata
 ${tools}/mxnet sync metadata
 ${tools}/onnx sync install schema metadata