| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494 |
- const caffe2 = {};
- caffe2.ModelFactory = class {
- async match(context) {
- const identifier = context.identifier.toLowerCase();
- const extension = identifier.lastIndexOf('.') > 0 ? identifier.split('.').pop().toLowerCase() : '';
- switch (extension) {
- case 'pbtxt':
- case 'prototxt': {
- const tags = await context.tags('pbtxt');
- if (tags.has('op') && !tags.has('op.attr') && !tags.has('op.graph_op_name') && !tags.has('op.endpoint')) {
- return context.set('caffe2.pbtxt');
- }
- break;
- }
- case 'pb': {
- const tags = await context.tags('pb');
- if (tags.size > 0 &&
- Array.from(tags.keys()).every((tag) => tag <= 9) &&
- Array.from(tags.values()).every((type) => type <= 4)) {
- if (tags.size === 1 && tags.get(2) === 2 && identifier.endsWith('saved_model.pb')) {
- return null;
- }
- const schema = [[1,2],[2,2],[3,2],[4,0],[5,2],[6,2],[7,2],[8,2],[9,2]];
- if (schema.every(([key, value]) => !tags.has(key) || tags.get(key) === value)) {
- const stream = context.stream;
- if (stream.length > 3) {
- const buffer = stream.peek(Math.min(stream.length, 67));
- const [signature, size] = buffer;
- switch (signature) {
- case 0x0A:
- if (size < 64 &&
- buffer.length > 2 + size + 1 &&
- buffer.slice(2, 2 + size).every((c) => c >= 32 && c <= 127) &&
- buffer[2 + size] === 0x12) {
- return context.set('caffe2.pb');
- }
- break;
- case 0x12:
- return context.set('caffe2.pb');
- default:
- break;
- }
- }
- }
- }
- break;
- }
- default: {
- break;
- }
- }
- return null;
- }
- async open(context) {
- caffe2.proto = await context.require('./caffe2-proto');
- caffe2.proto = caffe2.proto.caffe2;
- const metadata = await context.metadata('caffe2-metadata.json');
- const identifier = context.identifier;
- const parts = identifier.split('.');
- const extension = parts.pop().toLowerCase();
- const base = parts.join('.');
- let predict = null;
- let init = null;
- switch (context.type) {
- case 'caffe2.pbtxt': {
- if (base.toLowerCase().endsWith('init_net') || base.toLowerCase().startsWith('init_net')) {
- init = context;
- try {
- const name = identifier.replace('init_net', 'predict_net');
- predict = await context.fetch(name);
- predict.set(context.type);
- } catch {
- // continue regardless of error
- }
- } else if (base.toLowerCase().endsWith('predict_net') || base.toLowerCase().startsWith('predict_net')) {
- predict = context;
- const name = identifier.replace('predict_net', 'init_net');
- try {
- init = await context.fetch(name.replace(/\.pbtxt/, '.pb'));
- init.set('caffe2.pb');
- } catch {
- try {
- init = await context.fetch(name);
- init.set('caffe2.pbtxt');
- } catch {
- // continue regardless of error
- }
- }
- } else {
- predict = context;
- try {
- init = await context.fetch(`${base}_init.pb`);
- init.set('caffe2.pb');
- } catch {
- // continue regardless of error
- }
- }
- break;
- }
- case 'caffe2.pb': {
- if (base.toLowerCase().endsWith('init_net')) {
- init = context;
- const extensions = new Set([extension, 'pb', 'pbtxt']);
- for (const extension of extensions) {
- try {
- const name = `${base.replace(/init_net$/, '')}predict_net.${extension}`;
- /* eslint-disable no-await-in-loop */
- predict = await context.fetch(name);
- /* eslint-enable no-await-in-loop */
- predict.set(`caffe2.${extension}`);
- break;
- } catch {
- // continue regardless of error
- }
- }
- } else if (base.toLowerCase().endsWith('_init')) {
- try {
- const name = `${base.replace(/_init$/, '')}.${extension}`;
- predict = await context.fetch(name);
- predict.set(context.type);
- } catch {
- // continue regardless of error
- }
- } else if (base.toLowerCase().endsWith('predict_net') || base.toLowerCase().startsWith('predict_net')) {
- predict = context;
- try {
- const name = identifier.replace('predict_net', 'init_net');
- init = await context.fetch(name);
- init.set(context.type);
- } catch {
- // continue regardless of error
- }
- } else {
- predict = context;
- try {
- const file = `${base}_init.${extension}`;
- init = await context.fetch(file, null);
- init.set(context.type);
- } catch {
- // continue regardless of error
- }
- }
- break;
- }
- default: {
- throw new caffe2.Error(`Unsupported Caffe2 format '${context.type}'.`);
- }
- }
- if (!predict && !init) {
- throw new caffe2.Error(`Caffe2 model does not contain predict or init data.`);
- }
- const open = async (context) => {
- if (context) {
- switch (context.type) {
- case 'caffe2.pb':
- try {
- const reader = await context.read('protobuf.binary');
- return caffe2.proto.NetDef.decode(reader);
- } catch (error) {
- const message = error && error.message ? error.message : error.toString();
- throw new caffe2.Error(`File format is not caffe2.NetDef (${message.replace(/\.$/, '')}).`);
- }
- case 'caffe2.pbtxt':
- try {
- const reader = await context.read('protobuf.text');
- reader.field = function(tag, message) {
- if (message instanceof caffe2.proto.DeviceOption) {
- message[tag] = this.read();
- return;
- }
- throw new Error(`Unknown field '${tag}' ${this.location()}`);
- };
- return caffe2.proto.NetDef.decodeText(reader);
- } catch (error) {
- const message = error && error.message ? error.message : error.toString();
- throw new caffe2.Error(`File format is not caffe2.NetDef (${message.replace(/\.$/, '')}).`);
- }
- default:
- throw new caffe2.Error(`Unsupported Caffe2 predict format '${context.type}'.`);
- }
- }
- return null;
- };
- const predict_net = await open(predict);
- const init_net = await open(init);
- return new caffe2.Model(metadata, predict_net, init_net);
- }
- };
- caffe2.Model = class {
- constructor(metadata, predict_net, init_net) {
- const net = predict_net || init_net;
- this.format = 'Caffe2';
- this.domain = net.domain || null;
- this.modules = [new caffe2.Graph(metadata, predict_net, init_net)];
- }
- };
- caffe2.Graph = class {
- constructor(metadata, predict_net, init_net) {
- const net = predict_net || init_net;
- init_net = predict_net ? init_net : null;
- this.name = net.name || '';
- this.nodes = [];
- this.description = net.type;
- const initializers = new Set();
- const tensors = new Map();
- for (const name of net.external_input) {
- tensors.set(name, new caffe2.Tensor(name, {}));
- }
- if (init_net) {
- const dataTypes = new Map([
- ['GivenTensorFill', 'float32'],
- ['GivenTensorDoubleFill', 'float64'],
- ['GivenTensorBoolFill', 'boolean'],
- ['GivenTensorByteStringToUInt8Fill', 'uint8'],
- ['GivenTensorInt16Fill', 'int16'],
- ['GivenTensorSInt16Fill', 'int16'],
- ['GivenTensorIntFill', 'int32'],
- ['GivenTensorInt64Fill', 'int64'],
- ['GivenTensorStringFill', 'string'],
- ['Int8GivenIntTensorFill', 'int32'],
- ['Int8GivenTensorFill', 'int8'],
- ['XavierFill', null],
- ['ConstantFill', null]
- ]);
- for (const op of init_net.op) {
- if (op.output && op.output.length === 1) {
- const [name] = op.output;
- const tensor = {};
- for (const arg of op.arg) {
- tensor[arg.name] = arg;
- }
- if (!dataTypes.has(op.type)) {
- throw new caffe2.Error(`Unsupported init op '${op.type}'.`);
- }
- tensor.dataType = dataTypes.get(op.type);
- if (tensor.values && tensor.values.floats && (tensor.values.floats.length !== 1 || tensor.values.floats[0] !== 0)) {
- initializers.add(name);
- }
- tensors.set(name, new caffe2.Tensor(name, tensor));
- }
- }
- }
- const scope = {};
- for (let i = 0; i < net.op.length; i++) {
- const op = net.op[i];
- op.input = op.input.map((input) => scope[input] ? scope[input] : input);
- op.output = op.output.map((output) => {
- if (scope[output]) {
- const next = `${output}\n${i}`; // custom argument id
- scope[output] = next;
- return next;
- }
- scope[output] = output;
- return output;
- });
- }
- const values = new Map();
- values.map = (name, type, tensor) => {
- if (!values.has(name)) {
- values.set(name, new caffe2.Value(name, type || null, tensor || null));
- } else if (type || tensor) {
- throw new caffe2.Value(`Duplicate value '${name}'.`);
- }
- return values.get(name);
- };
- for (const op of net.op) {
- let index = 0;
- for (const name of op.input) {
- if (index > 0 && tensors.has(name)) {
- if (!values.has(name)) {
- values.set(name, new caffe2.Value(name, null, tensors.get(name)));
- }
- initializers.add(name);
- }
- index++;
- }
- }
- for (const op of net.op) {
- for (const name of op.output) {
- if (tensors.has(name)) {
- initializers.add(name);
- }
- }
- }
- let lastNode = null;
- let lastOutput = null;
- for (const op of net.op) {
- const node = new caffe2.Node(metadata, op, values);
- if (op.input.length === 1 &&
- op.output.length >= 1 &&
- op.input[0].split('\n').shift() === op.output[0].split('\n').shift() &&
- lastNode &&
- lastOutput === op.input[0].split('\n').shift()) {
- lastNode.chain.push(node);
- } else {
- this.nodes.push(node);
- lastNode = null;
- lastOutput = null;
- if (op.output.length === 1) {
- lastNode = node;
- lastOutput = op.output[0].split('\n').shift();
- }
- }
- }
- this.inputs = [];
- for (const input of net.external_input) {
- if (net.external_input.length > 1 && initializers.has(input)) {
- continue;
- }
- const argument = new caffe2.Argument(input, [values.map(input)]);
- this.inputs.push(argument);
- }
- this.outputs = [];
- for (const output of net.external_output) {
- const argument = new caffe2.Argument(output, [values.map(output)]);
- this.outputs.push(argument);
- }
- }
- };
- caffe2.Argument = class {
- constructor(name, value, type = null, visible = true) {
- this.name = name;
- this.value = value;
- this.type = type;
- this.visible = visible;
- }
- };
- caffe2.Value = class {
- constructor(name, type, initializer = null) {
- if (typeof name !== 'string') {
- throw new caffe2.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
- }
- this.name = name;
- this.type = !type && initializer ? initializer.type : type;
- this.quantization = initializer && initializer.quantization ? initializer.quantization : null;
- this.initializer = initializer;
- }
- };
- caffe2.Node = class {
- constructor(metadata, op, values) {
- this.name = op.name || '';
- this.device = op.engine || '';
- this.chain = [];
- this.type = metadata.type(op.type);
- this.attributes = op.arg.map((arg) => {
- const schema = metadata.attribute(op.type, arg.name);
- const name = arg.name;
- let value = null;
- let type = null;
- let visible = true;
- if (arg.floats && arg.floats.length > 0) {
- value = arg.floats;
- } else if (arg.ints && arg.ints.length > 0) {
- value = arg.ints;
- } else if (arg.nets && arg.nets.length > 0) {
- value = arg.nets.map((net) => new caffe2.Graph(metadata, net, null));
- type = 'graph[]';
- } else if (arg.n) {
- value = new caffe2.Graph(metadata, arg.n, null);
- type = 'graph';
- } else {
- value = arg.i;
- }
- if (schema) {
- type = !type && schema.type ? schema.type : type;
- if (type === 'boolean') {
- value = value !== 0 && value.toString() !== '0' ? true : false;
- }
- if (schema.visible === false) {
- visible = false;
- } else if (schema.default !== undefined) {
- if (value === metadata.default || (value && value.toString() === schema.default.toString())) {
- visible = false;
- }
- }
- }
- return new caffe2.Argument(name, value, type, visible);
- });
- const inputs = op.input;
- const outputs = op.output;
- this.inputs = [];
- let inputIndex = 0;
- if (this.type && this.type.inputs) {
- for (const inputDef of this.type.inputs) {
- if (inputIndex < inputs.length || inputDef.option !== 'optional') {
- const inputCount = (inputDef.option === 'variadic') ? (inputs.length - inputIndex) : 1;
- const inputArguments = inputs.slice(inputIndex, inputIndex + inputCount).filter((id) => id !== '' || inputDef.option !== 'optional').map((id) => values.map(id));
- this.inputs.push(new caffe2.Argument(inputDef.name, inputArguments));
- inputIndex += inputCount;
- }
- }
- } else {
- this.inputs.push(...inputs.slice(inputIndex).map((input, index) => {
- const inputName = ((inputIndex + index) === 0) ? 'input' : (inputIndex + index).toString();
- return new caffe2.Argument(inputName, [values.map(input)]);
- }));
- }
- this.outputs = [];
- let outputIndex = 0;
- if (this.type && this.type.outputs) {
- for (const outputDef of this.type.outputs) {
- if (outputIndex < outputs.length || outputDef.option !== 'optional') {
- const outputCount = (outputDef.option === 'variadic') ? (outputs.length - outputIndex) : 1;
- const outputArguments = outputs.slice(outputIndex, outputIndex + outputCount).map((id) => values.map(id));
- this.outputs.push(new caffe2.Argument(outputDef.name, outputArguments));
- outputIndex += outputCount;
- }
- }
- } else {
- this.outputs.push(...outputs.slice(outputIndex).map((output, index) => {
- const outputName = ((outputIndex + index) === 0) ? 'output' : (outputIndex + index).toString();
- return new caffe2.Argument(outputName, [values.map(output)]);
- }));
- }
- }
- };
- caffe2.Tensor = class {
- constructor(name, tensor) {
- this.name = name;
- const shape = tensor.shape && tensor.shape.ints ? tensor.shape.ints : null;
- this.type = new caffe2.TensorType(tensor.dataType, new caffe2.TensorShape(shape));
- this.values = null;
- this.category = 'Initializer';
- this.encoding = '|';
- if (tensor.Y_scale !== undefined || tensor.Y_zero_point !== undefined) {
- this.quantization = {
- type: 'linear',
- scale: [tensor.Y_scale ? tensor.Y_scale.f : 0],
- offset: [tensor.Y_zero_point && typeof tensor.Y_zero_point.i === 'bigint' ? tensor.Y_zero_point.i.toNumber() : 0]
- };
- }
- if (tensor.values) {
- switch (this.type.dataType) {
- case 'float32': this.values = tensor.values.floats; break;
- case 'boolean': this.values = tensor.values.ints; break;
- case 'int8': this.values = new Int8Array(tensor.values.s); break;
- case 'int32': this.values = tensor.values.ints; break;
- default: break;
- }
- }
- }
- };
- caffe2.TensorType = class {
- constructor(dataType, shape) {
- this.dataType = dataType || '?';
- this.shape = shape;
- }
- toString() {
- return this.dataType + this.shape.toString();
- }
- };
- caffe2.TensorShape = class {
- constructor(dimensions) {
- this.dimensions = Array.isArray(dimensions) ? dimensions.map((dim) => typeof dim === 'bigint' ? dim.toNumber() : dim) : dimensions;
- }
- toString() {
- if (Array.isArray(this.dimensions) && this.dimensions.length > 0) {
- return `[${this.dimensions.map((dim) => dim.toString()).join(',')}]`;
- }
- return '';
- }
- };
- caffe2.Error = class extends Error {
- constructor(message) {
- super(message);
- this.name = 'Error loading Caffe2 model.';
- }
- };
- export const ModelFactory = caffe2.ModelFactory;
|