From 595827bdbb18c134e3754c8c3c7229c14ea9ce3b Mon Sep 17 00:00:00 2001 From: Lutz Roeder Date: Tue, 14 May 2024 22:22:00 -0500 Subject: [PATCH] Add Core ML test file (#193) --- source/coreml.js | 29 +++++++++++++++++++++++++++++ source/darknet.js | 2 +- source/openvino.js | 5 +++-- source/view.js | 2 +- test/models.json | 9 +++++++++ 5 files changed, 43 insertions(+), 4 deletions(-) diff --git a/source/coreml.js b/source/coreml.js index 8c8a616abc..e10d1d59b2 100644 --- a/source/coreml.js +++ b/source/coreml.js @@ -1,5 +1,6 @@ import * as base from './base.js'; +import * as text from './text.js'; const coreml = {}; @@ -45,6 +46,24 @@ coreml.ModelFactory = class { context.type = 'coreml.metadata'; return; } + if (Array.isArray(obj) && obj.some((item) => item && item.metadataOutputVersion && item.specificationVersion)) { + context.type = 'coreml.metadata.mlmodelc'; + return; + } + } + if (identifier === 'model.mil') { + try { + const reader = text.Reader.open(context.stream, 2048); + const signature = reader.read(); + if (signature !== undefined) { + if (signature.trim().startsWith('program')) { + context.type = 'coreml.mil'; + return; + } + } + } catch { + // continue regardless of error + } } if (identifier === 'featuredescriptions.json') { const obj = context.peek('json'); @@ -65,6 +84,10 @@ coreml.ModelFactory = class { } } + filter(context, type) { + return context.type !== 'coreml.manifest.mlmodelc' && type !== 'coreml.mil'; + } + async open(context) { coreml.proto = await context.require('./coreml-proto'); coreml.proto = coreml.proto.CoreML.Specification; @@ -171,6 +194,12 @@ coreml.ModelFactory = class { case 'coreml.metadata': { return openManifestStream(context, '../../'); } + case 'coreml.metadata.mlmodelc': { + throw new coreml.Error('Core ML Model Archive format is not supported.'); + } + case 'coreml.mil': { + throw new coreml.Error('Core ML MIL format is not supported.'); + } case 'coreml.weights': { return openManifestStream(context, '../../../'); } diff --git a/source/darknet.js b/source/darknet.js index b3be7a9a28..8c8bccd4e7 100644 --- a/source/darknet.js +++ b/source/darknet.js @@ -8,7 +8,7 @@ darknet.ModelFactory = class { match(context) { const identifier = context.identifier; const extension = identifier.split('.').pop().toLowerCase(); - if (extension === 'weights') { + if (extension === 'weights' && !identifier.toLowerCase().endsWith('.espresso.weights')) { const weights = darknet.Weights.open(context); if (weights) { context.type = 'darknet.weights'; diff --git a/source/openvino.js b/source/openvino.js index 1e598eba5e..55e3084284 100644 --- a/source/openvino.js +++ b/source/openvino.js @@ -35,8 +35,9 @@ openvino.ModelFactory = class { const identifiers = new Set(['config.bin', 'model.bin', '__model__.bin', 'weights.bin', 'programs.bin', 'best.bin', 'ncnn.bin']); if (!identifiers.has(identifier)) { const size = Math.min(stream.length, 1024) & 0xFFFC; - const buffer = stream.peek(size); - const array = new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength); + const buffer = stream.peek(size).slice(0); + const length = buffer.length / 4; + const array = new Float32Array(buffer.buffer, buffer.byteOffset, length); const values = Array.from(array); if (values.every((value) => !Number.isNaN(value) && Number.isFinite(value) && value > -10.0 && value < 10.0)) { context.type = 'openvino.bin'; diff --git a/source/view.js b/source/view.js index 7c1a4b2a9d..2f013f554c 100644 --- a/source/view.js +++ b/source/view.js @@ -5488,7 +5488,7 @@ view.ModelFactoryService = class { this.register('./pytorch', ['.pt', '.pth', '.ptl', '.pt1', '.pyt', '.pyth', '.pkl', '.pickle', '.h5', '.t7', '.model', '.dms', '.tar', '.ckpt', '.chkpt', '.tckpt', '.bin', '.pb', '.zip', '.nn', '.torchmodel', '.torchscript', '.pytorch', '.ot', '.params', '.trt', '.ff', '.ptmf', '.jit', '.pte', '.bin.index.json', 'serialized_exported_program.json'], ['.model', '.pt2']); this.register('./onnx', ['.onnx', '.onnx.data', '.onn', '.pb', '.onnxtxt', '.pbtxt', '.prototxt', '.txt', '.model', '.pt', '.pth', '.pkl', '.ort', '.ort.onnx', '.ngf', '.json', '.bin', 'onnxmodel']); this.register('./mxnet', ['.json', '.params'], ['.mar']); - this.register('./coreml', ['.mlmodel', '.bin', 'manifest.json', 'metadata.json', 'featuredescriptions.json', '.pb', '.pbtxt'], ['.mlpackage']); + this.register('./coreml', ['.mlmodel', '.bin', 'manifest.json', 'metadata.json', 'featuredescriptions.json', '.pb', '.pbtxt', '.mil'], ['.mlpackage', '.mlmodelc']); this.register('./caffe', ['.caffemodel', '.pbtxt', '.prototxt', '.pt', '.txt']); this.register('./caffe2', ['.pb', '.pbtxt', '.prototxt']); this.register('./torch', ['.t7', '.net']); diff --git a/test/models.json b/test/models.json index 44087cfc08..290cc32c80 100644 --- a/test/models.json +++ b/test/models.json @@ -1547,6 +1547,15 @@ "tags": "quantization", "link": "https://github.com/CapTechMobile/blogfest-coreml" }, + { + "type": "coreml", + "target": "segmentation.mlmodelc.zip", + "source": "https://github.com/user-attachments/files/15315833/segmentation.mlmodelc.zip", + "error": "Core ML Model Archive format is not supported.", + "format": "Core ML Model Archive", + "link": "https://github.com/lutzroeder/netron/issues/193" + }, + { "type": "coreml", "target": "SentimentPolarity.mlmodel",