| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590 |
- const caffe = {};
- caffe.ModelFactory = class {
- async match(context) {
- const identifier = context.identifier;
- const extension = identifier.lastIndexOf('.') > 0 ? identifier.split('.').pop().toLowerCase() : '';
- if (extension === 'caffemodel') {
- return context.set('caffe.pb');
- }
- if (identifier === 'saved_model.pbtxt' || identifier === 'saved_model.prototxt' ||
- identifier.endsWith('predict_net.pbtxt') || identifier.endsWith('predict_net.prototxt') ||
- identifier.endsWith('init_net.pbtxt') || identifier.endsWith('init_net.prototxt')) {
- return null;
- }
- const tags = await context.tags('pbtxt');
- if (tags.has('layer') || tags.has('layers')) {
- return context.set('caffe.pbtxt');
- } else if (tags.has('net') || tags.has('train_net') || tags.has('net_param')) {
- return context.set('caffe.pbtxt.solver');
- }
- return null;
- }
- async open(context) {
- caffe.proto = await context.require('./caffe-proto');
- caffe.proto = caffe.proto.caffe;
- const openModel = async (context, netParameter) => {
- const metadata = await context.metadata('caffe-metadata.json');
- return new caffe.Model(metadata, netParameter);
- };
- const openNetParameterText = async (context, identifier, content) => {
- let netParameter = null;
- try {
- const reader = await content.read('protobuf.text');
- reader.field = function(tag, message) {
- const type = message.constructor.name;
- if (tag.endsWith('_param') && (type === 'LayerParameter' || type === 'V1LayerParameter' || type === 'V0LayerParameter')) {
- message[tag] = caffe.ModelFactory._decodeText(reader);
- return;
- } else if (message.constructor.name.endsWith('Parameter') || message.constructor.name === 'ParamSpec') {
- if (message[tag]) {
- if (!Array.isArray(message[tag])) {
- message[tag] = [message[tag]];
- }
- message[tag].push(this.read());
- } else {
- message[tag] = this.read();
- }
- return;
- }
- throw new Error(`Unknown field '${tag}' ${this.location()}`);
- };
- reader.enum = function(type) {
- const token = this.token();
- this.next();
- this.semicolon();
- if (!Object.prototype.hasOwnProperty.call(type, token)) {
- const value = Number.parseInt(token, 10);
- if (!Number.isNaN(token - value)) {
- return value;
- }
- return token;
- }
- return type[token];
- };
- if (/MobileNetSSD_train_template.prototxt/.exec(identifier)) {
- reader.integer = function() {
- const token = this.token();
- const value = Number.parseInt(token, 10);
- this.next();
- this.semicolon();
- if (Number.isNaN(token - value)) {
- return token;
- }
- return value;
- };
- }
- netParameter = caffe.proto.NetParameter.decodeText(reader);
- } catch (error) {
- const message = error && error.message ? error.message : error.toString();
- throw new caffe.Error(`File text format is not caffe.NetParameter (${message.replace(/\.$/, '')}).`);
- }
- return openModel(context, netParameter);
- };
- switch (context.type) {
- case 'caffe.pbtxt.solver': {
- const reader = await context.read('protobuf.text');
- reader.field = function(tag, message) {
- if (message instanceof caffe.proto.SolverParameter) {
- message[tag] = this.read();
- return;
- }
- throw new Error(`Unknown field '${tag}'${this.location()}`);
- };
- const solver = caffe.proto.SolverParameter.decodeText(reader);
- if (solver.net_param) {
- return openModel(context, solver.net_param);
- }
- let name = solver.net || solver.train_net;
- name = name.split('/').pop();
- try {
- const content = await context.fetch(name);
- return await openNetParameterText(context, name, content);
- } catch (error) {
- const message = error.message ? error.message : error.toString();
- throw new caffe.Error(`Failed to load '${name}' (${message.replace(/\.$/, '')}).`);
- }
- }
- case 'caffe.pbtxt': {
- return await openNetParameterText(context, context.identifier, context);
- }
- case 'caffe.pb': {
- let netParameter = null;
- try {
- const reader = await context.read('protobuf.binary');
- netParameter = caffe.proto.NetParameter.decode(reader);
- } catch (error) {
- const message = error && error.message ? error.message : error.toString();
- throw new caffe.Error(`File format is not caffe.NetParameter (${message.replace(/\.$/, '')}).`);
- }
- return await openModel(context, netParameter);
- }
- default: {
- throw new caffe.Error(`Unsupported Caffe format '${context.type}'.`);
- }
- }
- }
- static _decodeText(reader) {
- const message = {};
- reader.start();
- while (!reader.end()) {
- const tag = reader.tag();
- const value = reader.read();
- if (message[tag]) {
- if (!Array.isArray(message[tag])) {
- message[tag] = [message[tag]];
- }
- message[tag].push(value);
- } else {
- message[tag] = value;
- }
- }
- return message;
- }
- };
- caffe.Model = class {
- constructor(metadata, net) {
- this.name = net.name;
- this.format = 'Caffe';
- this.modules = [];
- let version = -1;
- if (net.layers && net.layers.length > 0) {
- if (net.layers.every((layer) => Object.prototype.hasOwnProperty.call(layer, 'layer'))) {
- version = 0;
- net.layer = net.layers;
- } else {
- version = 1;
- net.layer = net.layers;
- }
- } else if (net.layer && net.layer.length > 0) {
- version = 2;
- }
- this.format = `Caffe v${version}`;
- const phases = new Set();
- for (const layer of net.layer) {
- for (const include of layer.include) {
- if (include.phase !== undefined) {
- phases.add(include.phase);
- }
- }
- }
- if (phases.size === 0) {
- phases.add(-1);
- }
- for (const phase of phases) {
- const graph = new caffe.Graph(metadata, phase, net, version);
- this.modules.push(graph);
- }
- }
- };
- caffe.Graph = class {
- constructor(metadata, phase, net, version) {
- switch (phase) {
- case 0: this.name = 'TRAIN'; break;
- case 1: this.name = 'TEST'; break;
- case -1: this.name = ''; break;
- default: this.name = phase.toString(); break;
- }
- this.nodes = [];
- this.inputs = [];
- this.outputs = [];
- for (const layer of net.layer) {
- layer.input = layer.bottom.slice(0);
- layer.output = layer.top.slice(0);
- layer.chain = [];
- }
- const layers = [];
- for (const layer of net.layer) {
- if (phase === -1 || layer.include.every((include) => include.phase === phase)) {
- layers.push(layer);
- }
- }
- const scopes = new Map();
- for (let i = 0; i < layers.length; i++) {
- const layer = layers[i];
- layer.input = layer.input.map((input) => scopes.has(input) ? scopes.get(input) : input);
- layer.output = layer.output.map((output) => {
- const value = scopes.has(output) ? `${output}\n${i}` : output;
- scopes.set(output, value);
- return value;
- });
- }
- // Graph Inputs
- const usedOutputs = new Set();
- for (const layer of layers) {
- for (const output of layer.output) {
- usedOutputs.add(output);
- }
- }
- const unusedInputs = [];
- for (const layer of layers) {
- for (const input of layer.input) {
- if (!usedOutputs.has(input)) {
- unusedInputs.push(input);
- }
- }
- }
- const values = new Map();
- const value = (name, type) => {
- if (!values.has(name)) {
- values.set(name, new caffe.Value(name, type));
- } else if (type) {
- throw new caffe.Error(`Duplicate value '${name}'.`);
- }
- return values.get(name);
- };
- const nodes = [];
- let lastLayer = null;
- let lastTop = null;
- while (layers.length > 0) {
- let layer = layers.shift();
- if (layer.output.length === 1 && layer.input.length === 1 &&
- layer.output[0].split('\n').shift() === layer.input[0].split('\n').shift() &&
- lastLayer &&
- lastTop === layer.output[0].split('\n').shift()) {
- lastLayer.chain = lastLayer.chain || [];
- lastLayer.chain.push(layer);
- } else {
- if (layer.type === 'Input' && layer.input.length === 0) {
- for (let i = 0; i < layer.output.length; i++) {
- const output = layer.output[i];
- const dim = layer.input_param && layer.input_param.shape && i < layer.input_param.shape.length ? layer.input_param.shape[i].dim : null;
- const shape = dim ? new caffe.TensorShape(dim.map((dim) => dim.toNumber())) : null;
- const type = shape ? new caffe.TensorType(null, shape) : null;
- const argument = new caffe.Argument(output, [value(output, type)]);
- this.inputs.push(argument);
- }
- layer = null;
- }
- if (layer) {
- nodes.push(layer);
- lastLayer = null;
- lastTop = null;
- if (layer.output.length === 1) {
- lastLayer = layer;
- lastTop = layer.output[0].split('\n').shift();
- }
- }
- }
- }
- if (net.input) {
- for (let i = 0; i < net.input.length; i++) {
- const input = net.input[i];
- if (this.inputs.some((item) => item.name === input)) {
- continue;
- }
- let inputType = null;
- if (net.input_shape && i < net.input_shape.length) {
- const blobShape = net.input_shape[i];
- if (blobShape && blobShape.dim) {
- const shape = new caffe.TensorShape(blobShape.dim.map((dim) => dim.toNumber()));
- inputType = new caffe.TensorType(null, shape);
- }
- }
- const dim = i * 4;
- if (!inputType && net.input_dim && net.input_dim.length >= dim) {
- const shape = new caffe.TensorShape(net.input_dim.slice(dim, dim + 4));
- inputType = new caffe.TensorType(null, shape);
- }
- this.inputs.push(new caffe.Argument(input, [value(input, inputType, null)]));
- }
- }
- for (const layer of nodes) {
- const node = new caffe.Node(metadata, layer, version, value);
- if (layer.chain && layer.chain.length > 0) {
- for (const chain of layer.chain) {
- node.chain.push(new caffe.Node(metadata, chain, version, value));
- }
- }
- this.nodes.push(node);
- }
- if (this.inputs.length === 0 && unusedInputs.length === 1) {
- this.inputs.push(new caffe.Argument(unusedInputs[0], [value(unusedInputs[0], null)]));
- }
- }
- };
- caffe.Argument = class {
- constructor(name, value, type = null, visible = true) {
- this.name = name;
- this.value = value;
- this.type = type;
- this.visible = visible;
- }
- };
- caffe.Value = class {
- constructor(name, type = null, initializer = null) {
- if (typeof name !== 'string') {
- throw new caffe.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
- }
- this.name = name;
- this.type = type;
- this.initializer = initializer;
- }
- };
- caffe.Node = class {
- constructor(metadata, layer, version, value) {
- this.attributes = [];
- this.chain = [];
- let type = '';
- switch (version) {
- case 0: {
- this.name = layer.layer.name;
- type = layer.layer.type;
- break;
- }
- case 1: {
- this.name = layer.name;
- type = caffe.Utility.layerType(layer.type);
- break;
- }
- case 2: {
- this.name = layer.name;
- type = layer.type;
- break;
- }
- default: {
- throw new caffe.Error(`Unsupported Caffe version '${version}'.`);
- }
- }
- this.type = metadata.type(type) || { name: type };
- let initializers = [];
- const attributes = [];
- switch (version) {
- case 0: {
- for (const name of Object.keys(layer.layer)) {
- if (name !== 'type' && name !== 'name' && name !== 'blobs' && name !== 'blobs_lr') {
- const value = layer.layer[name];
- const schema = metadata.attribute(type, name);
- attributes.push([schema, name, value]);
- }
- }
- initializers = layer.layer.blobs.map((blob) => new caffe.Tensor(blob));
- break;
- }
- case 1:
- case 2: {
- for (const layer_kind of Object.keys(layer)) {
- if (layer_kind.endsWith('_param') || layer_kind === 'transform_param') {
- const param = layer[layer_kind];
- if (type === 'Deconvolution') {
- type = 'Convolution';
- }
- const prototype = Object.getPrototypeOf(param);
- for (const name of Object.keys(param)) {
- const defaultValue = prototype[name];
- const value = param[name];
- const schema = metadata.attribute(type, name);
- attributes.push([schema, name, value, defaultValue]);
- }
- }
- }
- if (layer.include && layer.include.length > 0) {
- const schema = metadata.attribute(type, 'include');
- attributes.push([schema, 'include', layer.include]);
- }
- if (layer.exclude && layer.exclude.length > 0) {
- const schema = metadata.attribute(type, 'exclude');
- attributes.push([schema, 'exclude', layer.exclude]);
- }
- if (this.type === 'Data' && layer.input_param && layer.input_param.shape) {
- const schema = metadata.attribute(type, 'shape');
- attributes.push([schema, 'shape', layer.input_param.shape]);
- }
- initializers = layer.blobs.map((blob) => new caffe.Tensor(blob));
- break;
- }
- default: {
- throw new caffe.Error(`Unsupported Caffe version '${version}'.`);
- }
- }
- this.inputs = [];
- const inputs = layer.input.concat(initializers);
- let inputIndex = 0;
- if (this.type && this.type.inputs) {
- for (const inputDef of this.type.inputs) {
- if (inputIndex < inputs.length || inputDef.option !== 'optional') {
- const count = inputDef.option === 'variadic' ? inputs.length - inputIndex : 1;
- const values = inputs.slice(inputIndex, inputIndex + count).filter((input) => input !== '' || inputDef.option !== 'optional').map((input) => {
- return input instanceof caffe.Tensor ? new caffe.Value('', input.type, input) : value(input, null, null);
- });
- const argument = new caffe.Argument(inputDef.name, values);
- this.inputs.push(argument);
- inputIndex += count;
- }
- }
- }
- this.inputs.push(...inputs.slice(inputIndex).map((input) => {
- return new caffe.Argument(inputIndex.toString(), [
- input instanceof caffe.Tensor ? new caffe.Value('', input.type, input) : value(input, null, null)
- ]);
- }));
- this.outputs = [];
- const outputs = layer.output;
- let outputIndex = 0;
- if (this.type && this.type.outputs) {
- for (const outputDef of this.type.outputs) {
- if (outputIndex < outputs.length) {
- const count = (outputDef.option === 'variadic') ? (outputs.length - outputIndex) : 1;
- const values = outputs.slice(outputIndex, outputIndex + count).map((output) => value(output, null, null));
- const argument = new caffe.Argument(outputDef.name, values);
- this.outputs.push(argument);
- outputIndex += count;
- }
- }
- }
- this.outputs.push(...outputs.slice(outputIndex).map((output, index) => {
- return new caffe.Argument((outputIndex + index).toString(), [value(output, null, null)]);
- }));
- this.attributes = attributes.map(([metadata, name, value, defaultValue]) => {
- let visible = true;
- let type = null;
- if (metadata && metadata.type) {
- type = metadata.type;
- }
- if (value instanceof caffe.proto.BlobShape) {
- value = new caffe.TensorShape(value.dim.map((dim) => dim.toNumber()));
- type = 'shape';
- }
- if (metadata && metadata.visible === false) {
- visible = false;
- }
- if (metadata && metadata.default !== undefined) {
- defaultValue = metadata.default;
- }
- if (defaultValue !== undefined) {
- if (value === defaultValue) {
- visible = false;
- } else if (Array.isArray(value) && Array.isArray(defaultValue)) {
- if (value.length === defaultValue.length && value.every((item, index) => item === defaultValue[index])) {
- visible = false;
- }
- }
- }
- value = type ? caffe.Utility.enum(type, value) : value;
- return new caffe.Argument(name, value, type, visible);
- });
- }
- };
- caffe.Tensor = class {
- constructor(blob) {
- let shape = [];
- if (Object.prototype.hasOwnProperty.call(blob, 'num') &&
- Object.prototype.hasOwnProperty.call(blob, 'channels') &&
- Object.prototype.hasOwnProperty.call(blob, 'width') &&
- Object.prototype.hasOwnProperty.call(blob, 'height')) {
- if (blob.num !== 1) {
- shape.push(blob.num);
- }
- if (blob.channels !== 1) {
- shape.push(blob.channels);
- }
- if (blob.height !== 1) {
- shape.push(blob.height);
- }
- if (blob.width !== 1) {
- shape.push(blob.width);
- }
- } else if (Object.prototype.hasOwnProperty.call(blob, 'shape')) {
- shape = blob.shape.dim.map((dim) => Number(dim));
- }
- let dataType = '?';
- if (blob.data.length > 0) {
- dataType = 'float32';
- this.values = blob.data;
- } else if (blob.double_data.length > 0) {
- dataType = 'float64';
- this.values = blob.double_data;
- }
- this.category = 'Blob';
- this.encoding = '|';
- this.type = new caffe.TensorType(dataType, new caffe.TensorShape(shape));
- }
- };
- caffe.TensorType = class {
- constructor(dataType, shape) {
- this.dataType = dataType;
- this.shape = shape;
- }
- toString() {
- return (this.dataType || '?') + this.shape.toString();
- }
- };
- caffe.TensorShape = class {
- constructor(dimensions) {
- this.dimensions = dimensions;
- }
- toString() {
- return this.dimensions ? (`[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`) : '';
- }
- };
- caffe.Utility = class {
- static layerType(type) {
- type = type || 0;
- if (!caffe.Utility._layerTypeMap) {
- caffe.Utility._layerTypeMap = new Map();
- const known = { 'BNLL': 'BNLL', 'HDF5': 'HDF5', 'LRN': 'LRN', 'RELU': 'ReLU', 'TANH': 'TanH', 'ARGMAX': 'ArgMax', 'MVN': 'MVN', 'ABSVAL': 'AbsVal' };
- for (const key of Object.keys(caffe.proto.V1LayerParameter.LayerType)) {
- const value = caffe.proto.V1LayerParameter.LayerType[key];
- caffe.Utility._layerTypeMap.set(value, key.split('_').map((item) => known[item] || item.substring(0, 1) + item.substring(1).toLowerCase()).join(''));
- }
- }
- return caffe.Utility._layerTypeMap.has(type) ? caffe.Utility._layerTypeMap.get(type) : type.toString();
- }
- static enum(name, value) {
- let type = caffe.proto;
- const parts = name.split('.');
- while (type && parts.length > 0) {
- type = type[parts.shift()];
- }
- if (type) {
- caffe.Utility._enumKeyMap = caffe.Utility._enumKeyMap || new Map();
- if (!caffe.Utility._enumKeyMap.has(name)) {
- const map = new Map(Object.entries(type).map(([name, value]) => [value, name]));
- caffe.Utility._enumKeyMap.set(name, map);
- }
- const map = caffe.Utility._enumKeyMap.get(name);
- if (map.has(value)) {
- return map.get(value);
- }
- }
- return value;
- }
- };
- caffe.Error = class extends Error {
- constructor(message) {
- super(message);
- this.name = 'Error loading Caffe model.';
- }
- };
- export const ModelFactory = caffe.ModelFactory;
|