|
|
@@ -69,12 +69,10 @@ openvino.ModelFactory = class {
|
|
|
if (errors || xmlDoc.documentElement == null || xmlDoc.getElementsByTagName('parsererror').length > 0) {
|
|
|
throw new openvino.Error("File format is not OpenVINO.");
|
|
|
}
|
|
|
- const net = xmlDoc.documentElement;
|
|
|
- if (!net || net.nodeName != 'net' ||
|
|
|
- openvino.Node.children(net, 'layers').length != 1 ||
|
|
|
- openvino.Node.children(net, 'edges').length != 1) {
|
|
|
+ if (!xmlDoc.documentElement || xmlDoc.documentElement.nodeName != 'net') {
|
|
|
throw new openvino.Error("File format is not OpenVINO IR.");
|
|
|
}
|
|
|
+ const net = openvino.XmlReader.read(xmlDoc.documentElement);
|
|
|
return new openvino.Model(metadata, net, bin);
|
|
|
}
|
|
|
catch (error) {
|
|
|
@@ -89,8 +87,12 @@ openvino.ModelFactory = class {
|
|
|
openvino.Model = class {
|
|
|
|
|
|
constructor(metadata, net, bin) {
|
|
|
- let graph = new openvino.Graph(metadata, net, bin);
|
|
|
- this._graphs = [ graph ];
|
|
|
+ this._name = net.name || '';
|
|
|
+ this._graphs = [ new openvino.Graph(metadata, net, bin) ];
|
|
|
+ }
|
|
|
+
|
|
|
+ get name() {
|
|
|
+ return this._name;
|
|
|
}
|
|
|
|
|
|
get format() {
|
|
|
@@ -106,55 +108,35 @@ openvino.Model = class {
|
|
|
openvino.Graph = class {
|
|
|
|
|
|
constructor(metadata, net, bin) {
|
|
|
- this._name = net.getAttribute('name') || '';
|
|
|
- this._batch = net.getAttribute('batch') || '';
|
|
|
- this._version = net.getAttribute('version') || '';
|
|
|
+ this._name = net.name || '';
|
|
|
this._nodes = [];
|
|
|
this._inputs = [];
|
|
|
this._outputs = [];
|
|
|
this._arguments = {};
|
|
|
|
|
|
- const layersElement = openvino.Node.children(net, 'layers')[0];
|
|
|
- const edgesElement = openvino.Node.children(net, 'edges')[0];
|
|
|
-
|
|
|
- const layers = openvino.Node.children(layersElement, 'layer');
|
|
|
- const edges = openvino.Node.children(edgesElement, 'edge');
|
|
|
-
|
|
|
- const edgeMap = this._collectEdges(edges);
|
|
|
-
|
|
|
- for (const layer of layers) {
|
|
|
- const operator = layer.getAttribute('type');
|
|
|
- switch (operator) {
|
|
|
+ for (const layer of net.layers) {
|
|
|
+ const inputs = layer.inputs.map((input) => this._argument(layer.id, layer.precision, input, net.edges));
|
|
|
+ const outputs = layer.outputs.map((output) => this._argument(layer.id, output.precision || layer.precision, output, null));
|
|
|
+ switch (layer.type) {
|
|
|
case 'Input': {
|
|
|
- let args = [];
|
|
|
- const name = layer.getAttribute('name') || '';
|
|
|
+ const name = layer.name || '';
|
|
|
// precision is a part of OpenVINO IR layers of IR v6 and earlier
|
|
|
// in IR v7 and newer the port is no longer an attribute of the layer but of each output port
|
|
|
- const precision = layer.getAttribute('precision');
|
|
|
- const id = layer.getAttribute('id');
|
|
|
- for (const outputElement of openvino.Node.children(layer, 'output')) {
|
|
|
- for (const portElement of openvino.Node.children(outputElement, 'port')) {
|
|
|
- const portPrecision = portElement.getAttribute('precision') || precision;
|
|
|
- args.push(this._argument(id, portPrecision, portElement, null));
|
|
|
- }
|
|
|
- }
|
|
|
// IR input is not just a placeholder, it is conceptually the legitimate layer
|
|
|
// in order not to break compatibility with the overall approach
|
|
|
// with openvino.Parameter for inputs and openvino.Node for outputs
|
|
|
// input openvino.Node would be stored as an optional attribute of openvino.Parameter
|
|
|
- const inputNode = new openvino.Node(this, metadata, bin, layer, edgeMap);
|
|
|
- const inputParameter = new openvino.Parameter(name, args);
|
|
|
- inputParameter._realNode = inputNode;
|
|
|
- this._inputs.push(inputParameter);
|
|
|
+ this._inputs.push(new openvino.Parameter(name, outputs));
|
|
|
break;
|
|
|
}
|
|
|
- default:
|
|
|
- this._nodes.push(new openvino.Node(this, metadata, bin, layer, edgeMap));
|
|
|
+ default: {
|
|
|
+ this._nodes.push(new openvino.Node(this, metadata, bin, layer, inputs, outputs));
|
|
|
break;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- this._replaceTensorIteratorWithSubgraph(metadata, bin, layers, edges, edgeMap);
|
|
|
+ this._replaceTensorIteratorWithSubgraph(metadata, bin, net.layers, net.edges);
|
|
|
delete this._arguments;
|
|
|
|
|
|
// Validation
|
|
|
@@ -163,25 +145,32 @@ openvino.Graph = class {
|
|
|
// "Input" layers are already moved to inputs when we parse a graph
|
|
|
// if there are any layers that do not have input arguments and they are no Const ones
|
|
|
// this means that this graph was not properly processed by the graph building logic
|
|
|
- const allNodesOutputs = this._nodes.reduce((acc, node) => {
|
|
|
- const nodesRes = this._collectConnectionsIds(node._outputs);
|
|
|
- acc = acc.concat(nodesRes);
|
|
|
- return acc;
|
|
|
- }, []);
|
|
|
- const allInputsOutputs = this._collectConnectionsIds(this._inputs);
|
|
|
- const outputSet = new Set([...allNodesOutputs, ...allInputsOutputs]);
|
|
|
- const nodesWithNonExistentInputs = this._nodes.reduce((acc, node) => {
|
|
|
- const nodesInputs = this._collectConnectionsIds(node._inputs);
|
|
|
- if (nodesInputs.filter((value) => !outputSet.has(value)).length > 0) {
|
|
|
- acc.push(node);
|
|
|
+ let outputSet = new Set();
|
|
|
+ for (const node of this._nodes) {
|
|
|
+ for (const output of node.outputs) {
|
|
|
+ for (const argument of output.arguments) {
|
|
|
+ outputSet.add(argument.id);
|
|
|
+ }
|
|
|
}
|
|
|
- return acc;
|
|
|
- }, []);
|
|
|
-
|
|
|
- if (nodesWithNonExistentInputs.length !== 0){
|
|
|
- const layerNames = nodesWithNonExistentInputs.map((n) => n.name).join(',');
|
|
|
- const message = `Graph seems to contain ${nodesWithNonExistentInputs.length} connected components. Not connected layers: ${layerNames}`;
|
|
|
- throw new openvino.Error(message);
|
|
|
+ }
|
|
|
+ for (const input of this.inputs) {
|
|
|
+ for (const argument of input.arguments) {
|
|
|
+ outputSet.add(argument.id);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ let nodesWithNonExistentInputs = new Set();
|
|
|
+ for (const node of this._nodes) {
|
|
|
+ for (const input of node.inputs) {
|
|
|
+ for (const argument of input.arguments) {
|
|
|
+ if (!argument.initializer && !outputSet.has(argument.id)) {
|
|
|
+ nodesWithNonExistentInputs.add(node);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (nodesWithNonExistentInputs.size !== 0){
|
|
|
+ const layerNames = Array.from(nodesWithNonExistentInputs).map((node) => node.name);
|
|
|
+ throw new openvino.Error('Graph contains not connected layers ' + JSON.stringify(layerNames) + '.');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -202,50 +191,41 @@ openvino.Graph = class {
|
|
|
}
|
|
|
|
|
|
_argument(layer, precision, port, map) {
|
|
|
- let id = layer + ':' + port.getAttribute('id');
|
|
|
+ let id = layer + ':' + port.id;
|
|
|
if (map) {
|
|
|
id = map[id];
|
|
|
}
|
|
|
let argument = this._arguments[id];
|
|
|
if (!argument) {
|
|
|
- let dimensions = [];
|
|
|
- for (const dimElement of Array.prototype.slice.call(port.getElementsByTagName('dim'))) {
|
|
|
- dimensions.push(parseInt(dimElement.textContent.trim()));
|
|
|
- }
|
|
|
- const shape = (dimensions.length == 0) ? null : new openvino.TensorShape(dimensions);
|
|
|
+ const shape = port.dims.length == 0 ? null : new openvino.TensorShape(port.dims);
|
|
|
argument = new openvino.Argument(id, new openvino.TensorType(precision, shape), null);
|
|
|
}
|
|
|
return argument;
|
|
|
}
|
|
|
|
|
|
- _replaceTensorIteratorWithSubgraph(metadata, bin, layers, edges, edgeMap) {
|
|
|
- const tiNodes = layers.filter((node) => node.getAttribute('type') === 'TensorIterator');
|
|
|
- for (const singleTensorIteratorNode of tiNodes) {
|
|
|
- const singleTensorIteratorNodeId = singleTensorIteratorNode.getAttribute("id");
|
|
|
- const tiNode = this._nodes.find((n) => n._id === `${singleTensorIteratorNodeId}`);
|
|
|
- const body = openvino.Node.children(singleTensorIteratorNode, 'body')[0];
|
|
|
- const layersContainer = openvino.Node.children(body, 'layers')[0];
|
|
|
- const edgesContainer = openvino.Node.children(body, 'edges')[0];
|
|
|
- const iteratorLayers = openvino.Node.children(layersContainer, 'layer');
|
|
|
- const iteratorEdges = openvino.Node.children(edgesContainer, 'edge');
|
|
|
- const iteratorEdgeMap = this._collectEdges(iteratorEdges);
|
|
|
- const iteratorBackEdgesContainer = openvino.Node.children(singleTensorIteratorNode, 'back_edges')[0];
|
|
|
- const iteratorBackEdges = openvino.Node.children(iteratorBackEdgesContainer, 'edge')
|
|
|
- const iteratorBackEdgesMap = this._collectEdges(iteratorBackEdges);
|
|
|
+ _replaceTensorIteratorWithSubgraph(metadata, bin, layers, edges) {
|
|
|
+ const tiNodes = layers.filter((node) => node.type === 'TensorIterator');
|
|
|
+ for (const singleTensorIteratorNode of tiNodes) {
|
|
|
+ const singleTensorIteratorNodeId = singleTensorIteratorNode.id;
|
|
|
+ const tiNode = this._nodes.find((n) => n._id === singleTensorIteratorNodeId);
|
|
|
+ const iteratorLayers = singleTensorIteratorNode.body.layers;
|
|
|
+ const iteratorEdgeMap = singleTensorIteratorNode.body.edges;
|
|
|
+ const iteratorBackEdgesMap = singleTensorIteratorNode.back_edges;
|
|
|
const iteratorAllEdges = Object.assign({}, iteratorEdgeMap, iteratorBackEdgesMap);
|
|
|
- const mappingForNestedIR = this._parseMappingBlock(singleTensorIteratorNode);
|
|
|
+ const mappingForNestedIR = singleTensorIteratorNode.port_map;
|
|
|
for (const nestedLayer of iteratorLayers) {
|
|
|
- let nestedNode = new openvino.Node(this, metadata, bin, nestedLayer, iteratorAllEdges);
|
|
|
- nestedNode._id = `${singleTensorIteratorNodeId}_${nestedLayer.getAttribute('id')}`;
|
|
|
+ const inputs = nestedLayer.inputs.map((input) => this._argument(nestedLayer.id, nestedLayer.precision, input, iteratorAllEdges));
|
|
|
+ const outputs = nestedLayer.outputs.map((output) => this._argument(nestedLayer.id, nestedLayer.precision || output.precision, output, null));
|
|
|
+ let nestedNode = new openvino.Node(this, metadata, bin, nestedLayer, inputs, outputs);
|
|
|
+ nestedNode._id = singleTensorIteratorNodeId + '_' + nestedLayer.id;
|
|
|
for (const input of nestedNode._inputs) {
|
|
|
for (const input_argument of input.arguments) {
|
|
|
// we had a argument with id: 0:1 - meaning from layer "0" and its port "1"
|
|
|
// now as we rename all internal nodes to have an id of the TI included
|
|
|
// e.g. internal layer with id "0" and TI with id "14" results in internal layer to get id "14_0"
|
|
|
- if (!input_argument._id){
|
|
|
- continue;
|
|
|
+ if (input_argument.id){
|
|
|
+ input_argument._id = singleTensorIteratorNodeId + '_' + input_argument.id;
|
|
|
}
|
|
|
- input_argument._id = `${singleTensorIteratorNodeId}_${input_argument._id}`;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -254,31 +234,25 @@ openvino.Graph = class {
|
|
|
// we had a argument with id: 1:1 - meaning from me with id "1" and my port "1"
|
|
|
// now as we rename all internal nodes to have an id of the TI included
|
|
|
// e.g. my layer with id "1" and TI with id "14" results in internal layer to get id "14_1"
|
|
|
- if (!output_argument._id){
|
|
|
- continue;
|
|
|
+ if (output_argument.id){
|
|
|
+ output_argument._id = singleTensorIteratorNodeId + '_' + output_argument.id;
|
|
|
}
|
|
|
- output_argument._id = `${singleTensorIteratorNodeId}_${output_argument._id}`;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
this._nodes.push(nestedNode);
|
|
|
}
|
|
|
|
|
|
- // We know for sure that edges that appeared in the nested IR are not
|
|
|
- // aware of the external context
|
|
|
+ // We know for sure that edges that appeared in the nested IR are not aware of the external context
|
|
|
for (const nestedInput of mappingForNestedIR.input) {
|
|
|
- const nestedNode = this._nodes.find((n) => n._id === `${singleTensorIteratorNodeId}_${nestedInput.internal_layer_id}`);
|
|
|
- const candidate_edges = edges.filter((edge) => {
|
|
|
- return edge.getAttribute('to-layer') === singleTensorIteratorNodeId && edge.getAttribute('to-port') === nestedInput.external_port_id;
|
|
|
- });
|
|
|
- if (!candidate_edges.length){
|
|
|
- continue;
|
|
|
- }
|
|
|
- for (const candidate_edge of candidate_edges) {
|
|
|
- const parentLayerID = candidate_edge.getAttribute('from-layer');
|
|
|
- const parentPortID = candidate_edge.getAttribute('from-port');
|
|
|
-
|
|
|
- const parentNode = this._nodes.find((n) => n._id === `${parentLayerID}`);
|
|
|
+ const nestedNode = this._nodes.find((n) => n._id === singleTensorIteratorNodeId + '_' + nestedInput.internal_layer_id);
|
|
|
+
|
|
|
+ const candidate_edge = edges[singleTensorIteratorNodeId + ':' + nestedInput.external_port_id];
|
|
|
+ if (candidate_edge) {
|
|
|
+ const parts = candidate_edge.split(':');
|
|
|
+ const parentLayerID = parts[0];
|
|
|
+ const parentPortID = parts[1];
|
|
|
+ const parentNode = this._nodes.find((n) => n._id === parentLayerID);
|
|
|
if (!parentNode) {
|
|
|
// its parent is a TensorIterator that was removed on the previous cycle
|
|
|
// information is still present in the inputs of the current TensorIterator node
|
|
|
@@ -287,10 +261,10 @@ openvino.Graph = class {
|
|
|
return;
|
|
|
}
|
|
|
const inputWithoutId = nestedNode._inputs.find((input) => {
|
|
|
- return Boolean(input._arguments.find((argument) => !argument._id));
|
|
|
+ return Boolean(input.arguments.find((argument) => !argument.id));
|
|
|
});
|
|
|
if (inputWithoutId) {
|
|
|
- const argumentWithoutId = inputWithoutId._arguments.find((argument) => !argument._id);
|
|
|
+ const argumentWithoutId = inputWithoutId.arguments.find((argument) => !argument.id);
|
|
|
if (argumentWithoutId){
|
|
|
argumentWithoutId._id = potentialParentInput.arguments[0].id;
|
|
|
}
|
|
|
@@ -298,10 +272,10 @@ openvino.Graph = class {
|
|
|
}
|
|
|
else {
|
|
|
if (!nestedNode._inputs){
|
|
|
- throw new openvino.Error(`Tensor Iterator node with name ${nestedNode._name} does not have inputs.`);
|
|
|
+ throw new openvino.Error("Tensor Iterator node with name '" + nestedNode._name + "' does not have inputs.");
|
|
|
}
|
|
|
|
|
|
- const newId = `${parentLayerID}:${parentPortID}`;
|
|
|
+ const newId = parentLayerID + ':' + parentPortID;
|
|
|
const inputWithoutId = nestedNode._inputs.find((input) => {
|
|
|
return Boolean(input._arguments.find((argument) => !argument._id));
|
|
|
});
|
|
|
@@ -313,12 +287,9 @@ openvino.Graph = class {
|
|
|
}
|
|
|
else {
|
|
|
// TODO: no tensor information in the new argument - passed as null for now
|
|
|
- const inputNode = new openvino.Node(this, metadata, bin, singleTensorIteratorNode, edgeMap);
|
|
|
- const inputParameter = new openvino.Parameter((nestedNode._inputs.length+1).toString(), [
|
|
|
+ nestedNode._inputs.push(new openvino.Parameter((nestedNode._inputs.length + 1).toString(), [
|
|
|
new openvino.Argument(newId, null, null)
|
|
|
- ]);
|
|
|
- inputParameter._realNode = inputNode;
|
|
|
- nestedNode._inputs.push(inputParameter);
|
|
|
+ ]));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -326,14 +297,10 @@ openvino.Graph = class {
|
|
|
|
|
|
for (const nestedOutput of mappingForNestedIR.output) {
|
|
|
const nestedNode = this._nodes.find((n) => n._id === `${singleTensorIteratorNodeId}_${nestedOutput.internal_layer_id}`);
|
|
|
- const candidate_edges = edges.filter((edge) => {
|
|
|
- return edge.getAttribute('from-layer') === singleTensorIteratorNodeId && edge.getAttribute('from-port') === nestedOutput.external_port_id;
|
|
|
- });
|
|
|
- if (candidate_edges.length === 0){
|
|
|
- continue;
|
|
|
- }
|
|
|
+ const toEdge = singleTensorIteratorNodeId + ':' + nestedOutput.external_port_id;
|
|
|
+ const candidate_edges = Object.keys(edges).filter((key) => edges[key] === toEdge)
|
|
|
for (const candidate_edge of candidate_edges) {
|
|
|
- const childLayerID = candidate_edge.getAttribute('to-layer');
|
|
|
+ const childLayerID = candidate_edge.split(':')[0];
|
|
|
const child = this._nodes.find((layer) => layer._id === childLayerID);
|
|
|
if (!child._inputs || (child._inputs && child._inputs.length === 0)){
|
|
|
continue;
|
|
|
@@ -345,7 +312,7 @@ openvino.Graph = class {
|
|
|
continue;
|
|
|
}
|
|
|
const myPort = nestedNode._outputs[0]._arguments[0]._id.split(':')[1];
|
|
|
- argument._id = `${nestedNode.id}:${myPort}`;
|
|
|
+ argument._id = nestedNode.id + ':' + myPort;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -355,154 +322,99 @@ openvino.Graph = class {
|
|
|
this._nodes = this._nodes.filter((node) => node.id !== singleTensorIteratorNode.id);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- _collectEdges(edges){
|
|
|
- let edgeMap = {};
|
|
|
- for (const edge of edges) {
|
|
|
- const fromLayer = edge.getAttribute('from-layer');
|
|
|
- const fromPort = edge.getAttribute('from-port');
|
|
|
- const toLayer = edge.getAttribute('to-layer');
|
|
|
- const toPort = edge.getAttribute('to-port');
|
|
|
- edgeMap[toLayer + ':' + toPort] = fromLayer + ':' + fromPort;
|
|
|
- }
|
|
|
- return edgeMap;
|
|
|
- }
|
|
|
-
|
|
|
- _collectPortsInformation(ports) {
|
|
|
- return ports.reduce((acc, port) => {
|
|
|
- acc.push({
|
|
|
- axis: port.getAttribute("axis"),
|
|
|
- external_port_id: port.getAttribute("external_port_id"),
|
|
|
- internal_layer_id: port.getAttribute("internal_layer_id"),
|
|
|
- internal_port_id: port.getAttribute("internal_port_id")
|
|
|
- });
|
|
|
- return acc;
|
|
|
- }, []);
|
|
|
- }
|
|
|
-
|
|
|
- _parseMappingBlock(singleTensorIteratorNode) {
|
|
|
- const portMap = openvino.Node.children(singleTensorIteratorNode, 'port_map')[0];
|
|
|
- const inputs = openvino.Node.children(portMap, 'input');
|
|
|
- const outputs = openvino.Node.children(portMap, 'output');
|
|
|
- return {
|
|
|
- input: this._collectPortsInformation(inputs),
|
|
|
- output: this._collectPortsInformation(outputs)
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- _collectConnectionsIds(where) {
|
|
|
- return where.reduce((accOutput, output) => {
|
|
|
- const res = output._arguments.reduce((accConn, argument) => {
|
|
|
- accConn.push(argument._id);
|
|
|
- return accConn;
|
|
|
- }, []);
|
|
|
- accOutput = accOutput.concat(res);
|
|
|
- return accOutput;
|
|
|
- }, []);
|
|
|
- }
|
|
|
};
|
|
|
|
|
|
openvino.Node = class {
|
|
|
|
|
|
- constructor(graph, metadata, bin, layer, edgeMap) {
|
|
|
+ constructor(graph, metadata, bin, layer, inputs, outputs) {
|
|
|
this._metadata = metadata;
|
|
|
- this._type = layer.getAttribute('type');
|
|
|
- this._name = layer.getAttribute('name') || '';
|
|
|
- this._id = layer.getAttribute('id');
|
|
|
+ this._type = layer.type;
|
|
|
+ this._name = layer.name || '';
|
|
|
+ this._id = layer.id;
|
|
|
this._inputs = [];
|
|
|
this._outputs = [];
|
|
|
this._initializers = [];
|
|
|
this._attributes = [];
|
|
|
- const precision = layer.getAttribute('precision');
|
|
|
+ const precision = layer.precision;
|
|
|
let inputIndex = 0;
|
|
|
- const input = openvino.Node.children(layer, 'input')[0];
|
|
|
- if (input) {
|
|
|
- for (const port of openvino.Node.children(input, 'port')) {
|
|
|
- const inputName = (inputIndex == 0) ? 'input' : inputIndex.toString();
|
|
|
- this._inputs.push(new openvino.Parameter(inputName, [
|
|
|
- graph._argument(this._id, precision, port, edgeMap)
|
|
|
- ]));
|
|
|
- inputIndex++;
|
|
|
- }
|
|
|
+ for (const input of inputs) {
|
|
|
+ const inputName = (inputIndex == 0) ? 'input' : inputIndex.toString();
|
|
|
+ this._inputs.push(new openvino.Parameter(inputName, [ input ]));
|
|
|
+ inputIndex++;
|
|
|
}
|
|
|
let outputIndex = 0;
|
|
|
- const output = openvino.Node.children(layer, 'output')[0];
|
|
|
- if (output) {
|
|
|
- for (const portElement of openvino.Node.children(output, 'port')) {
|
|
|
- const outputName = (outputIndex == 0) ? 'output' : outputIndex.toString();
|
|
|
- const portPrecision = portElement.getAttribute('precision') || precision;
|
|
|
- this._outputs.push(new openvino.Parameter(outputName, [
|
|
|
- graph._argument(this._id, portPrecision, portElement, null)
|
|
|
- ]));
|
|
|
- outputIndex++;
|
|
|
- }
|
|
|
+ for (const output of outputs) {
|
|
|
+ const outputName = (outputIndex == 0) ? 'output' : outputIndex.toString();
|
|
|
+ this._outputs.push(new openvino.Parameter(outputName, [ output ]));
|
|
|
+ outputIndex++;
|
|
|
}
|
|
|
let attributes = {};
|
|
|
- const data = openvino.Node.children(layer, 'data')[0];
|
|
|
- if (data && data.attributes) {
|
|
|
- for (const attribute of Array.from(data.attributes)) {
|
|
|
- attributes[attribute.name] = attribute.value;
|
|
|
- this._attributes.push(new openvino.Attribute(metadata, this, attribute.name, attribute.value));
|
|
|
- }
|
|
|
+ for (const attribute of layer.data) {
|
|
|
+ attributes[attribute.name] = attribute.value;
|
|
|
+ const attributeSchema = metadata.attribute(this.operator, attribute.name);
|
|
|
+ this._attributes.push(new openvino.Attribute(attributeSchema, attribute.name, attribute.value));
|
|
|
}
|
|
|
- const blobs = openvino.Node.children(layer, 'blobs')[0];
|
|
|
- if (blobs){
|
|
|
- for (const blob of Array.from(blobs.childNodes).filter((node) => node.nodeName != '#text')) {
|
|
|
- if (blob.getAttribute && typeof blob.getAttribute === 'function') {
|
|
|
- const name = blob.nodeName;
|
|
|
- let data = null;
|
|
|
- let shape = null;
|
|
|
- const blobPrecision = blob.getAttribute('precision') || precision;
|
|
|
- if (bin) {
|
|
|
- const offset = parseInt(blob.getAttribute('offset'));
|
|
|
- const size = parseInt(blob.getAttribute('size'));
|
|
|
- if ((offset + size) <= bin.length) {
|
|
|
- data = bin.slice(offset, offset + size);
|
|
|
- }
|
|
|
- const precisionMap = {
|
|
|
- 'FP16': 2, 'FP32': 4,
|
|
|
- 'I8': 1, 'I16': 2, 'I32': 4, 'I64': 8,
|
|
|
- 'U8': 1, 'U16': 2, 'U32': 4, 'U64': 8
|
|
|
- };
|
|
|
- if (precisionMap[blobPrecision]) {
|
|
|
- let itemSize = precisionMap[blobPrecision];
|
|
|
- switch (this._type + ':' + name) {
|
|
|
- case 'FullyConnected:weights': {
|
|
|
- const outSize = parseInt(attributes['out-size'], 10);
|
|
|
- shape = [ size / (outSize * itemSize), outSize ];
|
|
|
- break;
|
|
|
- }
|
|
|
- case 'FullyConnected:biases': {
|
|
|
- shape = [ parseInt(attributes['out-size'], 10) ];
|
|
|
- break;
|
|
|
- }
|
|
|
- case 'ScaleShift:weights':
|
|
|
- case 'ScaleShift:biases':
|
|
|
- case 'Convolution:biases': {
|
|
|
- shape = [ Math.floor(size / itemSize) ];
|
|
|
- break;
|
|
|
- }
|
|
|
- case 'Const:custom': {
|
|
|
- if (this._outputs.length > 0 &&
|
|
|
- this._outputs[0].arguments.length > 0 &&
|
|
|
- this._outputs[0].arguments[0].type &&
|
|
|
- this._outputs[0].arguments[0].type.shape &&
|
|
|
- this._outputs[0].arguments[0].type.shape.dimensions) {
|
|
|
- shape = this._outputs[0].arguments[0].type.shape.dimensions;
|
|
|
- }
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ for (const blob of layer.blobs) {
|
|
|
+ const name = blob.name;
|
|
|
+ const offset = blob.offset;
|
|
|
+ const size = blob.size;
|
|
|
+ const data = (bin && (offset + size) <= bin.length) ? bin.slice(offset, offset + size) : null;
|
|
|
+ let shape = null;
|
|
|
+ const blobPrecision = blob.precision || precision;
|
|
|
+ const precisionMap = {
|
|
|
+ 'FP16': 2, 'FP32': 4,
|
|
|
+ 'I8': 1, 'I16': 2, 'I32': 4, 'I64': 8,
|
|
|
+ 'U8': 1, 'U16': 2, 'U32': 4, 'U64': 8
|
|
|
+ };
|
|
|
+ if (precisionMap[blobPrecision]) {
|
|
|
+ let itemSize = precisionMap[blobPrecision];
|
|
|
+ switch (this._type + ':' + name) {
|
|
|
+ case 'FullyConnected:weights': {
|
|
|
+ const outSize = parseInt(attributes['out-size'], 10);
|
|
|
+ shape = [ size / (outSize * itemSize), outSize ];
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case 'FullyConnected:biases': {
|
|
|
+ shape = [ parseInt(attributes['out-size'], 10) ];
|
|
|
+ break;
|
|
|
}
|
|
|
- if (shape) {
|
|
|
- shape = new openvino.TensorShape(shape);
|
|
|
+ case 'Convolution:weights':
|
|
|
+ case 'Deconvolution:weights': {
|
|
|
+ const c = this.inputs[0].arguments[0].type.shape.dimensions[1];
|
|
|
+ const group = parseInt(attributes['group'] || '1', 10);
|
|
|
+ const kernel = attributes['kernel-x'] && attributes['kernel-y'] ?
|
|
|
+ [ parseInt(attributes['kernel-x'], 10), parseInt(attributes['kernel-y'], 10) ] :
|
|
|
+ attributes['kernel'].split(',').map((v) => parseInt(v.trim(), 10));
|
|
|
+ const n = parseInt(attributes['output'], 10);
|
|
|
+ shape = [ Math.floor(c / group), n ].concat(kernel);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case 'ScaleShift:weights':
|
|
|
+ case 'ScaleShift:biases':
|
|
|
+ case 'Convolution:biases':
|
|
|
+ case 'Normalize:weights':
|
|
|
+ case 'PReLU:weights': {
|
|
|
+ shape = [ Math.floor(size / itemSize) ];
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case 'Const:custom': {
|
|
|
+ if (this._outputs.length > 0 &&
|
|
|
+ this._outputs[0].arguments.length > 0 &&
|
|
|
+ this._outputs[0].arguments[0].type &&
|
|
|
+ this._outputs[0].arguments[0].type.shape &&
|
|
|
+ this._outputs[0].arguments[0].type.shape.dimensions) {
|
|
|
+ shape = this._outputs[0].arguments[0].type.shape.dimensions;
|
|
|
+ }
|
|
|
+ break;
|
|
|
}
|
|
|
- this._initializers.push(new openvino.Parameter(name, [
|
|
|
- new openvino.Argument('', null, new openvino.Tensor(blobPrecision, shape, data))
|
|
|
- ]));
|
|
|
}
|
|
|
}
|
|
|
+ if (shape) {
|
|
|
+ shape = new openvino.TensorShape(shape);
|
|
|
+ }
|
|
|
+ this._initializers.push(new openvino.Parameter(name, [
|
|
|
+ new openvino.Argument('', null, new openvino.Tensor(blobPrecision, shape, data))
|
|
|
+ ]));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -537,18 +449,6 @@ openvino.Node = class {
|
|
|
get outputs() {
|
|
|
return this._outputs;
|
|
|
}
|
|
|
-
|
|
|
- static children(element, name) {
|
|
|
- let children = [];
|
|
|
- let child = element.firstChild;
|
|
|
- while (child != null) {
|
|
|
- if (child.nodeType == 1 && child.nodeName == name) {
|
|
|
- children.push(child);
|
|
|
- }
|
|
|
- child = child.nextSibling;
|
|
|
- }
|
|
|
- return children;
|
|
|
- }
|
|
|
};
|
|
|
|
|
|
openvino.Parameter = class {
|
|
|
@@ -556,7 +456,6 @@ openvino.Parameter = class {
|
|
|
constructor(name, args) {
|
|
|
this._name = name;
|
|
|
this._arguments = args;
|
|
|
- this._realNode = null;
|
|
|
}
|
|
|
|
|
|
get name() {
|
|
|
@@ -575,6 +474,9 @@ openvino.Parameter = class {
|
|
|
openvino.Argument = class {
|
|
|
|
|
|
constructor(id, type, initializer) {
|
|
|
+ if (typeof id !== 'string') {
|
|
|
+ // throw new openvino.Error("Invalid argument identifier '" + JSON.stringify(id) + "'.");
|
|
|
+ }
|
|
|
this._id = id;
|
|
|
this._type = type || null;
|
|
|
this._initializer = initializer || null;
|
|
|
@@ -598,11 +500,9 @@ openvino.Argument = class {
|
|
|
|
|
|
openvino.Attribute = class {
|
|
|
|
|
|
- constructor(metadata, node, name, value) {
|
|
|
- this._node = node;
|
|
|
+ constructor(schema, name, value) {
|
|
|
this._name = name;
|
|
|
this._value = value;
|
|
|
- const schema = metadata.attribute(node.operator, name);
|
|
|
if (schema) {
|
|
|
if (Object.prototype.hasOwnProperty.call(schema, 'type')) {
|
|
|
this._type = schema.type;
|
|
|
@@ -963,6 +863,120 @@ openvino.Metadata = class {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+openvino.XmlReader = class {
|
|
|
+
|
|
|
+ static read(element) {
|
|
|
+ const children = (parent, name) => {
|
|
|
+ let children = [];
|
|
|
+ let child = parent.firstChild;
|
|
|
+ while (child != null) {
|
|
|
+ if (child.nodeType == 1 && child.nodeName == name) {
|
|
|
+ children.push(child);
|
|
|
+ }
|
|
|
+ child = child.nextSibling;
|
|
|
+ }
|
|
|
+ return children;
|
|
|
+ };
|
|
|
+ const child = (parent, name) => {
|
|
|
+ const elements = children(parent, name);
|
|
|
+ if (elements.length > 1) {
|
|
|
+ throw new openvino.Error("Element '" + parent.nodeName + "' has multiple '" + name + "' elements.");
|
|
|
+ }
|
|
|
+ return elements.length > 0 ? elements[0] : null;
|
|
|
+ }
|
|
|
+ const ports = (parent, name) => {
|
|
|
+ const elements = child(parent, name);
|
|
|
+ if (elements) {
|
|
|
+ return children(elements, 'port').map((element) => {
|
|
|
+ return {
|
|
|
+ id: element.getAttribute('id'),
|
|
|
+ precision: element.getAttribute('precision'),
|
|
|
+ dims: Array.prototype.slice.call(element.getElementsByTagName('dim')).map((dim) => parseInt(dim.textContent.trim(), 10))
|
|
|
+ };
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return [];
|
|
|
+ };
|
|
|
+ const layers = (parent) => {
|
|
|
+ const elements = child(parent, 'layers');
|
|
|
+ if (elements) {
|
|
|
+ return children(elements, 'layer').map((element) => {
|
|
|
+ const data = child(element, 'data');
|
|
|
+ const blobs = child(element, 'blobs');
|
|
|
+ let layer = {
|
|
|
+ id: element.getAttribute('id'),
|
|
|
+ name: element.getAttribute('name'),
|
|
|
+ type: element.getAttribute('type'),
|
|
|
+ precision: element.getAttribute('precision'),
|
|
|
+ data: !data ? [] : Array.from(data.attributes).map((attribute) => {
|
|
|
+ return { name: attribute.name, value: attribute.value};
|
|
|
+ }),
|
|
|
+ blobs: !blobs ? [] : Array.from(blobs.childNodes).filter((node) => node.nodeType === 1).map((blob) => {
|
|
|
+ return {
|
|
|
+ name: blob.nodeName,
|
|
|
+ precision: blob.getAttribute('precision'),
|
|
|
+ offset: parseInt(blob.getAttribute('offset'), 10),
|
|
|
+ size: parseInt(blob.getAttribute('size'), 10)
|
|
|
+ }
|
|
|
+ }),
|
|
|
+ inputs: ports(element, 'input'),
|
|
|
+ outputs: ports(element, 'output'),
|
|
|
+ };
|
|
|
+ if (layer.type === 'TensorIterator') {
|
|
|
+ layer.back_edges = edges(element, 'back_edges');
|
|
|
+ const body = child(element, 'body');
|
|
|
+ if (body) {
|
|
|
+ layer.body = {
|
|
|
+ layers: layers(body),
|
|
|
+ edges: edges(body)
|
|
|
+ };
|
|
|
+ }
|
|
|
+ const port_map = child(element, 'port_map');
|
|
|
+ if (port_map) {
|
|
|
+ layer.port_map = { input: [], output: [] };
|
|
|
+ for (const port of Array.from(port_map.childNodes).filter((element) => element.nodeType === 1)) {
|
|
|
+ const item = {
|
|
|
+ axis: port.getAttribute("axis"),
|
|
|
+ external_port_id: port.getAttribute("external_port_id"),
|
|
|
+ internal_layer_id: port.getAttribute("internal_layer_id"),
|
|
|
+ internal_port_id: port.getAttribute("internal_port_id")
|
|
|
+ };
|
|
|
+ switch (port.nodeName) {
|
|
|
+ case 'input': layer.port_map.input.push(item); break;
|
|
|
+ case 'output': layer.port_map.output.push(item); break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return layer;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+ const edges = (parent, name) => {
|
|
|
+ let map = {};
|
|
|
+ const elements = child(parent, name || 'edges');
|
|
|
+ if (elements) {
|
|
|
+ for (const element of children(elements, 'edge')) {
|
|
|
+ const fromLayer = element.getAttribute('from-layer');
|
|
|
+ const fromPort = element.getAttribute('from-port');
|
|
|
+ const toLayer = element.getAttribute('to-layer');
|
|
|
+ const toPort = element.getAttribute('to-port');
|
|
|
+ map[toLayer + ':' + toPort] = fromLayer + ':' + fromPort;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return map;
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ name: element.getAttribute('name'),
|
|
|
+ batch: element.getAttribute('batch'),
|
|
|
+ version: element.getAttribute('version'),
|
|
|
+ layers: layers(element),
|
|
|
+ edges: edges(element)
|
|
|
+ };
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
openvino.Error = class extends Error {
|
|
|
|
|
|
constructor(message) {
|