chore: import upstream snapshot with attribution
Build / Build (macos-latest) (push) Has been cancelled
Build / Build (ubuntu-latest) (push) Has been cancelled
Build / Build (windows-latest) (push) Has been cancelled
Build / Analyze (javascript) (push) Has been cancelled
Build / Analyze (python) (push) Has been cancelled
@@ -0,0 +1,15 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
labels: bug
|
||||
---
|
||||
|
||||
- Netron app and version: <!-- For example: 3.8.2 Desktop app, 2.9.0 Website or 3.9.2 Python Server -->
|
||||
- OS and browser version: <!-- For example macOS 10.15.6 (19G73) + Chrome 84.0.4147.89 -->
|
||||
|
||||
Steps to Reproduce:
|
||||
|
||||
1.
|
||||
2.
|
||||
|
||||
Please attach or link model files to reproduce the issue.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
labels: feature
|
||||
---
|
||||
<!-- Please search existing issues to avoid creating duplicates. -->
|
||||
<!-- Describe the feature you'd like. -->
|
||||
@@ -0,0 +1,55 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1120" height="280" viewBox="0 0 1120 280">
|
||||
<g transform="matrix(2.08182,0,0,2.08182,-121.251,-49.4127)">
|
||||
<g transform="matrix(100,0,0,100,60.9965,126)">
|
||||
<path d="M0.089,0L0.089,-0.745L0.595,-0.147L0.595,-0.715L0.656,-0.715L0.656,0.021L0.15,-0.578L0.15,0L0.089,0Z" fill="#C9D1D9" />
|
||||
</g>
|
||||
<g transform="matrix(100,0,0,100,164.341,126)">
|
||||
<path d="M0.089,0L0.089,-0.715L0.443,-0.715L0.443,-0.654L0.154,-0.654L0.154,-0.43L0.443,-0.43L0.443,-0.369L0.154,-0.369L0.154,-0.061L0.443,-0.061L0.443,0L0.089,0Z" fill="#C9D1D9" />
|
||||
</g>
|
||||
<g transform="matrix(100,0,0,100,244.491,126)">
|
||||
<path d="M0.216,0L0.216,-0.654L0.019,-0.654L0.019,-0.715L0.478,-0.715L0.478,-0.654L0.281,-0.654L0.281,0L0.216,0Z" fill="#C9D1D9" />
|
||||
</g>
|
||||
<g transform="matrix(100,0,0,100,323.031,126)">
|
||||
<path d="M0.154,-0.658L0.154,-0.394L0.219,-0.394C0.28,-0.394 0.322,-0.404 0.346,-0.423C0.37,-0.442 0.382,-0.475 0.382,-0.522C0.382,-0.571 0.369,-0.606 0.345,-0.627C0.32,-0.648 0.278,-0.658 0.219,-0.658L0.154,-0.658ZM0.523,0L0.444,0L0.193,-0.341L0.154,-0.341L0.154,0L0.089,0L0.089,-0.715L0.22,-0.715C0.298,-0.715 0.356,-0.699 0.394,-0.667C0.433,-0.634 0.452,-0.585 0.452,-0.52C0.452,-0.464 0.436,-0.421 0.403,-0.389C0.37,-0.357 0.324,-0.341 0.266,-0.341L0.523,0Z" fill="#C9D1D9" />
|
||||
</g>
|
||||
<g transform="matrix(0.10084,0,0,0.10084,398.318,38.8234)">
|
||||
<circle cx="512" cy="512" r="431" fill="none" stroke="#C9D1D9" stroke-width="42px"/>
|
||||
<path d="M296,392L540,280" stroke="#C9D1D9" stroke-width="12px"/>
|
||||
<path d="M296,632L540,280" stroke="#C9D1D9" stroke-width="12px"/>
|
||||
<path d="M296,392L540,435" stroke="#C9D1D9" stroke-width="12px"/>
|
||||
<path d="M296,632L540,435" stroke="#C9D1D9" stroke-width="12px"/>
|
||||
<path d="M296,392L540,590" stroke="#C9D1D9" stroke-width="12px"/>
|
||||
<path d="M296,632L540,590" stroke="#C9D1D9" stroke-width="12px"/>
|
||||
<path d="M296,392L540,744" stroke="#C9D1D9" stroke-width="12px"/>
|
||||
<path d="M296,632L540,744" stroke="#C9D1D9" stroke-width="12px"/>
|
||||
<path d="M540,280L785,512" stroke="#C9D1D9" stroke-width="12px"/>
|
||||
<path d="M540,590L785,512" stroke="#C9D1D9" stroke-width="12px"/>
|
||||
<path d="M540,435L785,512" stroke="#C9D1D9" stroke-width="12px"/>
|
||||
<path d="M540,744L785,512" stroke="#C9D1D9" stroke-width="12px"/>
|
||||
<g transform="translate(296,392)">
|
||||
<circle cx="0" cy="0" r="51" fill="#C9D1D9" />
|
||||
</g>
|
||||
<g transform="translate(296,632)">
|
||||
<circle cx="0" cy="0" r="51" fill="#C9D1D9" />
|
||||
</g>
|
||||
<g transform="translate(540,280)">
|
||||
<circle cx="0" cy="0" r="51" fill="#C9D1D9" />
|
||||
</g>
|
||||
<g transform="translate(540,435)">
|
||||
<circle cx="0" cy="0" r="51" fill="#C9D1D9" />
|
||||
</g>
|
||||
<g transform="translate(540,590)">
|
||||
<circle cx="0" cy="0" r="51" fill="#C9D1D9" />
|
||||
</g>
|
||||
<g transform="translate(540,744)">
|
||||
<circle cx="0" cy="0" r="51" fill="#C9D1D9" />
|
||||
</g>
|
||||
<g transform="translate(785,512)">
|
||||
<circle cx="0" cy="0" r="51" fill="#C9D1D9" />
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(100,0,0,100,520.979,126)">
|
||||
<path d="M0.089,0L0.089,-0.745L0.595,-0.147L0.595,-0.715L0.656,-0.715L0.656,0.021L0.15,-0.578L0.15,0L0.089,0Z" fill="#C9D1D9" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
@@ -0,0 +1,55 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1120" height="280" viewBox="0 0 1120 280">
|
||||
<g transform="matrix(2.08182,0,0,2.08182,-121.251,-49.4127)">
|
||||
<g transform="matrix(100,0,0,100,60.9965,126)">
|
||||
<path d="M0.089,0L0.089,-0.745L0.595,-0.147L0.595,-0.715L0.656,-0.715L0.656,0.021L0.15,-0.578L0.15,0L0.089,0Z" fill="#24292F" />
|
||||
</g>
|
||||
<g transform="matrix(100,0,0,100,164.341,126)">
|
||||
<path d="M0.089,0L0.089,-0.715L0.443,-0.715L0.443,-0.654L0.154,-0.654L0.154,-0.43L0.443,-0.43L0.443,-0.369L0.154,-0.369L0.154,-0.061L0.443,-0.061L0.443,0L0.089,0Z" fill="#24292F" />
|
||||
</g>
|
||||
<g transform="matrix(100,0,0,100,244.491,126)">
|
||||
<path d="M0.216,0L0.216,-0.654L0.019,-0.654L0.019,-0.715L0.478,-0.715L0.478,-0.654L0.281,-0.654L0.281,0L0.216,0Z" fill="#24292F" />
|
||||
</g>
|
||||
<g transform="matrix(100,0,0,100,323.031,126)">
|
||||
<path d="M0.154,-0.658L0.154,-0.394L0.219,-0.394C0.28,-0.394 0.322,-0.404 0.346,-0.423C0.37,-0.442 0.382,-0.475 0.382,-0.522C0.382,-0.571 0.369,-0.606 0.345,-0.627C0.32,-0.648 0.278,-0.658 0.219,-0.658L0.154,-0.658ZM0.523,0L0.444,0L0.193,-0.341L0.154,-0.341L0.154,0L0.089,0L0.089,-0.715L0.22,-0.715C0.298,-0.715 0.356,-0.699 0.394,-0.667C0.433,-0.634 0.452,-0.585 0.452,-0.52C0.452,-0.464 0.436,-0.421 0.403,-0.389C0.37,-0.357 0.324,-0.341 0.266,-0.341L0.523,0Z" fill="#24292F" />
|
||||
</g>
|
||||
<g transform="matrix(0.10084,0,0,0.10084,398.318,38.8234)">
|
||||
<circle cx="512" cy="512" r="431" fill="none" stroke="#24292F" stroke-width="42px"/>
|
||||
<path d="M296,392L540,280" stroke="#24292F" stroke-width="12px"/>
|
||||
<path d="M296,632L540,280" stroke="#24292F" stroke-width="12px"/>
|
||||
<path d="M296,392L540,435" stroke="#24292F" stroke-width="12px"/>
|
||||
<path d="M296,632L540,435" stroke="#24292F" stroke-width="12px"/>
|
||||
<path d="M296,392L540,590" stroke="#24292F" stroke-width="12px"/>
|
||||
<path d="M296,632L540,590" stroke="#24292F" stroke-width="12px"/>
|
||||
<path d="M296,392L540,744" stroke="#24292F" stroke-width="12px"/>
|
||||
<path d="M296,632L540,744" stroke="#24292F" stroke-width="12px"/>
|
||||
<path d="M540,280L785,512" stroke="#24292F" stroke-width="12px"/>
|
||||
<path d="M540,590L785,512" stroke="#24292F" stroke-width="12px"/>
|
||||
<path d="M540,435L785,512" stroke="#24292F" stroke-width="12px"/>
|
||||
<path d="M540,744L785,512" stroke="#24292F" stroke-width="12px"/>
|
||||
<g transform="translate(296,392)">
|
||||
<circle cx="0" cy="0" r="51" fill="#24292F" />
|
||||
</g>
|
||||
<g transform="translate(296,632)">
|
||||
<circle cx="0" cy="0" r="51" fill="#24292F" />
|
||||
</g>
|
||||
<g transform="translate(540,280)">
|
||||
<circle cx="0" cy="0" r="51" fill="#24292F" />
|
||||
</g>
|
||||
<g transform="translate(540,435)">
|
||||
<circle cx="0" cy="0" r="51" fill="#24292F" />
|
||||
</g>
|
||||
<g transform="translate(540,590)">
|
||||
<circle cx="0" cy="0" r="51" fill="#24292F" />
|
||||
</g>
|
||||
<g transform="translate(540,744)">
|
||||
<circle cx="0" cy="0" r="51" fill="#24292F" />
|
||||
</g>
|
||||
<g transform="translate(785,512)">
|
||||
<circle cx="0" cy="0" r="51" fill="#24292F" />
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(100,0,0,100,520.979,126)">
|
||||
<path d="M0.089,0L0.089,-0.745L0.595,-0.147L0.595,-0.715L0.656,-0.715L0.656,0.021L0.15,-0.578L0.15,0L0.089,0Z" fill="#24292F" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 917 KiB |
@@ -0,0 +1,86 @@
|
||||
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ '**' ]
|
||||
tags-ignore: [ '**' ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 30
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ macos-latest, ubuntu-latest, windows-latest ]
|
||||
|
||||
steps:
|
||||
- name: Clone Git repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '24.15'
|
||||
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
||||
- name: Install Packages
|
||||
run: npm run install
|
||||
|
||||
- name: Validate
|
||||
run: npm run validate
|
||||
|
||||
- name: Build Python Server
|
||||
run: npm run build python
|
||||
|
||||
- name: Build Electron
|
||||
shell: bash
|
||||
run: |
|
||||
npx electron-builder install-app-deps
|
||||
case "${{ matrix.os }}" in
|
||||
macos*)
|
||||
npm run build electron mac
|
||||
;;
|
||||
ubuntu*)
|
||||
sudo apt-get install rpm --yes
|
||||
npm run build electron linux
|
||||
;;
|
||||
windows*)
|
||||
npm run build electron windows
|
||||
;;
|
||||
esac
|
||||
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'javascript', 'python' ]
|
||||
|
||||
steps:
|
||||
- name: Clone Git repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
@@ -0,0 +1,96 @@
|
||||
|
||||
name: Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: [ 'v**' ]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ macos-latest, ubuntu-latest, windows-latest ]
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Git credentials
|
||||
run: |
|
||||
git config --global user.name ${{ secrets.PUBLISH_GITHUB_USER_NAME }}
|
||||
git config --global user.email ${{ secrets.PUBLISH_GITHUB_USER_EMAIL }}
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
||||
- name: Install Packages
|
||||
run: npm run install
|
||||
|
||||
- name: Publish Electron
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PUBLISH_GITHUB_TOKEN }}
|
||||
APPLE_API_KEY: ~/.private_keys/AuthKey_${{ secrets.APPLE_API_KEY_ID }}.p8
|
||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
||||
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
|
||||
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||
run: |
|
||||
npx electron-builder install-app-deps
|
||||
case "${{ matrix.os }}" in
|
||||
macos*)
|
||||
mkdir -p ~/.private_keys
|
||||
echo '${{ secrets.APPLE_API_KEY }}' > ~/.private_keys/AuthKey_${{ secrets.APPLE_API_KEY_ID }}.p8
|
||||
npm run publish electron mac
|
||||
;;
|
||||
ubuntu*)
|
||||
sudo apt-get install rpm --yes
|
||||
npm run publish electron linux
|
||||
;;
|
||||
windows*)
|
||||
unset CSC_LINK;
|
||||
unset CSC_KEY_PASSWORD;
|
||||
npm run publish electron windows
|
||||
;;
|
||||
esac
|
||||
|
||||
- if: startsWith(matrix.os, 'ubuntu')
|
||||
name: Publish Python
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
|
||||
run: npm run publish python
|
||||
|
||||
- if: startsWith(matrix.os, 'ubuntu')
|
||||
name: Publish Web
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PUBLISH_GITHUB_TOKEN }}
|
||||
GITHUB_USER: ${{ secrets.PUBLISH_GITHUB_USER }}
|
||||
run: git push origin HEAD:release --force
|
||||
|
||||
- if: false # startsWith(matrix.os, 'macos')
|
||||
name: Publish cask
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PUBLISH_GITHUB_TOKEN }}
|
||||
GITHUB_USER: ${{ secrets.PUBLISH_GITHUB_USER }}
|
||||
run: npm run publish cask
|
||||
|
||||
- if: startsWith(matrix.os, 'windows')
|
||||
name: Publish winget
|
||||
env:
|
||||
GITHUB_USER: ${{ secrets.PUBLISH_GITHUB_USER }}
|
||||
GITHUB_TOKEN: ${{ secrets.PUBLISH_GITHUB_TOKEN }}
|
||||
WINGET_TOKEN: ${{ secrets.WINGET_TOKEN }}
|
||||
run: npm run publish winget
|
||||
@@ -0,0 +1,13 @@
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
.claude
|
||||
.eslintcache
|
||||
.ruff_cache
|
||||
.specify
|
||||
.yarn
|
||||
dist/*
|
||||
node_modules/*
|
||||
test-results/*
|
||||
third_party/*
|
||||
yarn.lock
|
||||
*.pyc
|
||||
@@ -0,0 +1,106 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Desktop View",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
|
||||
},
|
||||
"runtimeArgs": [
|
||||
"${workspaceRoot}",
|
||||
"--enable-logging",
|
||||
"--remote-debugging-port=9222"
|
||||
],
|
||||
"webRoot": "${workspaceRoot}"
|
||||
},
|
||||
{
|
||||
"name": "Desktop App",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/source/app.js",
|
||||
"runtimeExecutable": "electron",
|
||||
"osx": {
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/electron/dist/Electron.app/Contents/MacOS/Electron"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Browser",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/package.py",
|
||||
"args": [ "build", "start" ],
|
||||
"justMyCode": true,
|
||||
"pathMappings": [
|
||||
{
|
||||
"localRoot": "${workspaceFolder}/source",
|
||||
"remoteRoot": "${workspaceFolder}/dist/pypi/netron",
|
||||
}
|
||||
],
|
||||
"serverReadyAction": {
|
||||
"action": "debugWithChrome",
|
||||
"pattern": "Serving .*at http://localhost:([0-9]+)",
|
||||
"uriFormat": "http://localhost:%s",
|
||||
"webRoot": "${workspaceFolder}/source",
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "Test Models",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/test/models.js",
|
||||
"args": [ "tag:validation" ],
|
||||
"console": "integratedTerminal",
|
||||
},
|
||||
{
|
||||
"name": "Test Desktop",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeArgs": ["run", "test", "desktop"],
|
||||
"console": "integratedTerminal",
|
||||
},
|
||||
{
|
||||
"name": "Test Browser",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeArgs": ["run", "test", "browser"],
|
||||
"console": "integratedTerminal",
|
||||
},
|
||||
{
|
||||
"name": "Test Backend",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/test/backend.py",
|
||||
"args": [],
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true,
|
||||
"serverReadyAction": {
|
||||
"action": "debugWithChrome",
|
||||
"pattern": "Serving .*at http://localhost:([0-9]+)",
|
||||
"uriFormat": "http://localhost:%s",
|
||||
"webRoot": "${workspaceFolder}/source",
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "Tools Python",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"args": [],
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true
|
||||
},
|
||||
{
|
||||
"name": "Tools JavaScript",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"args": [],
|
||||
"console": "integratedTerminal",
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"eslint.lintTask.enable": true,
|
||||
"files.exclude": {
|
||||
"dist": true,
|
||||
"node_modules": true,
|
||||
"third_party": true
|
||||
},
|
||||
"search.exclude": {
|
||||
"dist": true,
|
||||
"node_modules": true,
|
||||
"third_party": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
cff-version: 1.2.0
|
||||
title: "Netron, Visualizer for neural network, deep learning, and machine learning models"
|
||||
authors:
|
||||
- family-names: "Roeder"
|
||||
given-names: "Lutz"
|
||||
orcid: "https://orcid.org/0000-0003-4994-7839"
|
||||
doi: "10.5281/zenodo.5854962"
|
||||
date-released: 2017-12-04
|
||||
license: "MIT"
|
||||
url: "https://github.com/lutzroeder/netron"
|
||||
repository-code: "https://github.com/lutzroeder/netron"
|
||||
message: "If you use Netron in your research, please cite it using these metadata."
|
||||
@@ -0,0 +1,31 @@
|
||||
# How to Develop Netron
|
||||
|
||||
## Debugging
|
||||
|
||||
Netron can run as both an [Electron](https://electronjs.org) app or a web app.
|
||||
|
||||
To start the Electron app, install [Node.js](https://nodejs.org) and run:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/lutzroeder/netron
|
||||
cd netron
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
To debug the Electron app, open the folder in [Visual Studio Code](https://code.visualstudio.com) and press <kbd>F5</kbd>. To attach the debugger to the render process select the `Debug` tab and pick `Desktop View` before launching.
|
||||
|
||||
To build and launch the web app, pick `Browser` in the `Debug` tab or run this command:
|
||||
|
||||
```bash
|
||||
python package.py build start --browse
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
To validate changes run:
|
||||
|
||||
```bash
|
||||
npm run lint
|
||||
npm test [format] # e.g. npm test onnx
|
||||
```
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Lutz Roeder
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,39 @@
|
||||
<div align="center">
|
||||
<img width="400px" height="100px" src="https://github.com/lutzroeder/netron/raw/main/.github/logo-light.svg#gh-light-mode-only">
|
||||
<img width="400px" height="100px" src="https://github.com/lutzroeder/netron/raw/main/.github/logo-dark.svg#gh-dark-mode-only">
|
||||
</div>
|
||||
|
||||
Netron is a viewer for neural network, deep learning and machine learning models.
|
||||
|
||||
Netron supports ONNX, TensorFlow Lite, PyTorch, torch.export, ExecuTorch, TorchScript, TensorFlow, Core ML, OpenVINO, Keras, Caffe, Darknet, Safetensors and NumPy.
|
||||
|
||||
Netron has experimental support for MLIR, JAX, GGUF, RKNN, ncnn, MNN, PaddlePaddle and scikit-learn.
|
||||
|
||||
<p align='center'><a href='https://www.lutzroeder.com/ai'><img src='.github/screenshot.png' width='800'></a></p>
|
||||
|
||||
## Install
|
||||
|
||||
**Browser**: [**Start**](https://netron.app) the browser version.
|
||||
|
||||
**macOS**: [**Download**](https://github.com/lutzroeder/netron/releases/latest) the `.dmg` file or run `brew install --cask netron`.
|
||||
|
||||
**Linux**: [**Download**](https://github.com/lutzroeder/netron/releases/latest) the `.deb` or `.rpm` file.
|
||||
|
||||
**Windows**: [**Download**](https://github.com/lutzroeder/netron/releases/latest) the `.exe` installer or run `winget install -s winget netron`.
|
||||
|
||||
**Python**: `pip install netron`, then run `netron [FILE]` or `netron.start('[FILE]')`.
|
||||
|
||||
## Models
|
||||
|
||||
Sample model files to download or open using the browser version:
|
||||
|
||||
* **ONNX**: [squeezenet](https://github.com/onnx/models/raw/main/validated/vision/classification/squeezenet/model/squeezenet1.0-3.onnx) [[open](https://netron.app?url=https://github.com/onnx/models/raw/main/validated/vision/classification/squeezenet/model/squeezenet1.0-3.onnx)]
|
||||
* **TorchScript**: [traced_online_pred_layer](https://github.com/ApolloAuto/apollo/raw/master/modules/prediction/data/traced_online_pred_layer.pt) [[open](https://netron.app?url=https://github.com/ApolloAuto/apollo/raw/master/modules/prediction/data/traced_online_pred_layer.pt)]
|
||||
* **TensorFlow Lite**: [yamnet](https://huggingface.co/thelou1s/yamnet/resolve/main/lite-model_yamnet_tflite_1.tflite) [[open](https://netron.app?url=https://huggingface.co/thelou1s/yamnet/blob/main/lite-model_yamnet_tflite_1.tflite)]
|
||||
* **TensorFlow**: [chessbot](https://github.com/srom/chessbot/raw/master/model/chessbot.pb) [[open](https://netron.app?url=https://github.com/srom/chessbot/raw/master/model/chessbot.pb)]
|
||||
* **Keras**: [mobilenet](https://github.com/aio-libs/aiohttp-demos/raw/master/demos/imagetagger/tests/data/mobilenet.h5) [[open](https://netron.app?url=https://github.com/aio-libs/aiohttp-demos/raw/master/demos/imagetagger/tests/data/mobilenet.h5)]
|
||||
|
||||
* **MLIR**: [edge_detection](https://github.com/iree-org/iree/raw/main/tests/e2e/stablehlo_models/edge_detection.mlir) [[open](https://netron.app?url=https://github.com/iree-org/iree/blob/main/tests/e2e/stablehlo_models/edge_detection.mlir)]
|
||||
|
||||
* **Core ML**: [exermote](https://github.com/Lausbert/Exermote/raw/master/ExermoteInference/ExermoteCoreML/ExermoteCoreML/Model/Exermote.mlmodel) [[open](https://netron.app?url=https://github.com/Lausbert/Exermote/raw/master/ExermoteInference/ExermoteCoreML/ExermoteCoreML/Model/Exermote.mlmodel)]
|
||||
* **Darknet**: [yolo](https://github.com/AlexeyAB/darknet/raw/master/cfg/yolo.cfg) [[open](https://netron.app?url=https://github.com/AlexeyAB/darknet/raw/master/cfg/yolo.cfg)]
|
||||
@@ -0,0 +1,7 @@
|
||||
# WeHub 来源说明
|
||||
|
||||
- 原始项目:`lutzroeder/netron`
|
||||
- 原始仓库:https://github.com/lutzroeder/netron
|
||||
- 导入方式:上游默认分支的最新快照
|
||||
- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准
|
||||
- 本文件仅用于记录来源,不代表 WeHub 是原项目作者
|
||||
@@ -0,0 +1,256 @@
|
||||
|
||||
export default [
|
||||
{
|
||||
ignores: [
|
||||
'dist/**',
|
||||
'third_party/**',
|
||||
]
|
||||
},
|
||||
{
|
||||
languageOptions: {
|
||||
globals: {
|
||||
atob: 'readonly',
|
||||
BigInt: 'readonly',
|
||||
console: 'readonly',
|
||||
global: 'readonly',
|
||||
process: 'readonly',
|
||||
self: 'readonly',
|
||||
TextDecoder: 'readonly',
|
||||
TextEncoder: 'readonly',
|
||||
window: 'readonly',
|
||||
},
|
||||
sourceType: 'module'
|
||||
},
|
||||
rules: {
|
||||
'accessor-pairs': 'error',
|
||||
'array-bracket-spacing': 'error',
|
||||
'array-callback-return': 'error',
|
||||
// 'arrow-body-style': 'error',
|
||||
'arrow-parens': 'error',
|
||||
'block-scoped-var': 'error',
|
||||
'brace-style': 'error',
|
||||
// 'camelcase': 'error',
|
||||
// 'capitalized-comments': 'error',
|
||||
// 'class-methods-use-this': 'error',
|
||||
// 'complexity': 'error',
|
||||
'computed-property-spacing': 'error',
|
||||
'consistent-return': 'error',
|
||||
'consistent-this': 'error',
|
||||
'constructor-super': 'error',
|
||||
'curly': 'error',
|
||||
'default-case': 'error',
|
||||
'default-case-last': 'error',
|
||||
'default-param-last': 'error',
|
||||
'dot-notation': 'error',
|
||||
'eqeqeq': 'error',
|
||||
'for-direction': 'error',
|
||||
'func-name-matching': 'error',
|
||||
// 'func-names': 'error',
|
||||
'func-style': 'error',
|
||||
'getter-return': 'error',
|
||||
'grouped-accessor-pairs': 'error',
|
||||
'guard-for-in': 'error',
|
||||
'id-denylist': 'error',
|
||||
// 'id-length': 'error',
|
||||
'id-match': 'error',
|
||||
'indent': ['error', 4, { 'SwitchCase': 1 }],
|
||||
'init-declarations': 'error',
|
||||
'keyword-spacing': 'error',
|
||||
// 'line-comment-position': 'error',
|
||||
// 'logical-assignment-operators': 'error',
|
||||
// 'max-classes-per-file': 'error',
|
||||
// 'max-depth': 'error',
|
||||
// 'max-lines': 'error',
|
||||
// 'max-lines-per-function': 'error',
|
||||
'max-nested-callbacks': 'error',
|
||||
// 'max-params': 'error',
|
||||
// 'max-statements': 'error',
|
||||
// 'multiline-comment-style': 'error',
|
||||
// 'new-cap': 'error',
|
||||
'no-alert': 'error',
|
||||
'no-array-constructor': 'error',
|
||||
'no-async-promise-executor': 'error',
|
||||
'no-await-in-loop': 'error',
|
||||
// 'no-bitwise': 'error',
|
||||
'no-caller': 'error',
|
||||
'no-case-declarations': 'error',
|
||||
'no-class-assign': 'error',
|
||||
'no-compare-neg-zero': 'error',
|
||||
'no-cond-assign': 'error',
|
||||
'no-console': 'error',
|
||||
'no-const-assign': 'error',
|
||||
'no-constant-binary-expression': 'error',
|
||||
'no-constant-condition': 'error',
|
||||
'no-constructor-return': 'error',
|
||||
// 'no-continue': 'error',
|
||||
'no-control-regex': 'error',
|
||||
'no-debugger': 'error',
|
||||
'no-delete-var': 'error',
|
||||
'no-div-regex': 'error',
|
||||
'no-dupe-args': 'error',
|
||||
'no-dupe-class-members': 'error',
|
||||
'no-dupe-else-if': 'error',
|
||||
'no-dupe-keys': 'error',
|
||||
'no-duplicate-case': 'error',
|
||||
'no-duplicate-imports': 'error',
|
||||
'no-else-return': 'error',
|
||||
'no-empty': 'error',
|
||||
'no-empty-character-class': 'error',
|
||||
// 'no-empty-function': 'error',
|
||||
'no-empty-pattern': 'error',
|
||||
'no-empty-static-block': 'error',
|
||||
'no-eq-null': 'error',
|
||||
'no-eval': 'error',
|
||||
'no-ex-assign': 'error',
|
||||
'no-extend-native': 'error',
|
||||
'no-extra-bind': 'error',
|
||||
'no-extra-boolean-cast': 'error',
|
||||
'no-extra-label': 'error',
|
||||
'no-extra-semi': 'error',
|
||||
'no-fallthrough': 'error',
|
||||
'no-func-assign': 'error',
|
||||
'no-global-assign': 'error',
|
||||
'no-implicit-coercion': 'error',
|
||||
'no-implicit-globals': 'error',
|
||||
'no-implied-eval': 'error',
|
||||
'no-import-assign': 'error',
|
||||
// 'no-inline-comments': 'error',
|
||||
'no-inner-declarations': 'error',
|
||||
'no-invalid-regexp': 'error',
|
||||
'no-invalid-this': 'error',
|
||||
'no-irregular-whitespace': 'error',
|
||||
'no-iterator': 'error',
|
||||
'no-label-var': 'error',
|
||||
'no-labels': 'error',
|
||||
'no-lone-blocks': 'error',
|
||||
'no-lonely-if': 'error',
|
||||
'no-loop-func': 'error',
|
||||
'no-loss-of-precision': 'error',
|
||||
// 'no-magic-numbers': 'error',
|
||||
'no-misleading-character-class': 'error',
|
||||
'no-multi-assign': 'error',
|
||||
'no-multi-str': 'error',
|
||||
'no-multiple-empty-lines': ['error', { 'max': 1 }],
|
||||
'no-negated-condition': 'error',
|
||||
'no-nested-ternary': 'error',
|
||||
'no-new': 'error',
|
||||
'no-new-func': 'error',
|
||||
'no-new-native-nonconstructor': 'error',
|
||||
'no-new-wrappers': 'error',
|
||||
'no-nonoctal-decimal-escape': 'error',
|
||||
'no-obj-calls': 'error',
|
||||
'no-object-constructor': 'error',
|
||||
'no-octal': 'error',
|
||||
'no-octal-escape': 'error',
|
||||
// 'no-param-reassign': 'error',
|
||||
// 'no-plusplus': 'error',
|
||||
'no-promise-executor-return': 'error',
|
||||
'no-proto': 'error',
|
||||
'no-prototype-builtins': 'error',
|
||||
'no-redeclare': 'error',
|
||||
'no-regex-spaces': 'error',
|
||||
'no-restricted-exports': 'error',
|
||||
'no-restricted-globals': 'error',
|
||||
'no-restricted-imports': 'error',
|
||||
'no-restricted-properties': 'error',
|
||||
'no-restricted-syntax': 'error',
|
||||
'no-return-assign': 'error',
|
||||
'no-script-url': 'error',
|
||||
'no-self-assign': 'error',
|
||||
'no-self-compare': 'error',
|
||||
'no-sequences': 'error',
|
||||
'no-setter-return': 'error',
|
||||
// 'no-shadow': 'error',
|
||||
'no-shadow-restricted-names': 'error',
|
||||
'no-sparse-arrays': 'error',
|
||||
'no-template-curly-in-string': 'error',
|
||||
// 'no-ternary': 'error',
|
||||
'no-this-before-super': 'error',
|
||||
'no-throw-literal': 'error',
|
||||
'no-trailing-spaces': 'error',
|
||||
'no-undef': 'error',
|
||||
'no-undef-init': 'error',
|
||||
// 'no-undefined': 'error',
|
||||
// 'no-underscore-dangle': 'error',
|
||||
'no-unexpected-multiline': 'error',
|
||||
'no-unmodified-loop-condition': 'error',
|
||||
// 'no-unneeded-ternary': 'error',
|
||||
'no-unreachable': 'error',
|
||||
'no-unreachable-loop': 'error',
|
||||
'no-unsafe-finally': 'error',
|
||||
'no-unsafe-negation': 'error',
|
||||
'no-unsafe-optional-chaining': 'error',
|
||||
'no-unused-expressions': 'error',
|
||||
'no-unused-labels': 'error',
|
||||
'no-unused-private-class-members': 'error',
|
||||
'no-unused-vars': 'error',
|
||||
'no-use-before-define': 'error',
|
||||
// 'no-useless-assignment': 'error',
|
||||
'no-useless-backreference': 'error',
|
||||
'no-useless-call': 'error',
|
||||
'no-useless-catch': 'error',
|
||||
'no-useless-computed-key': 'error',
|
||||
'no-useless-concat': 'error',
|
||||
'no-useless-constructor': 'error',
|
||||
'no-useless-escape': 'error',
|
||||
'no-useless-rename': 'error',
|
||||
'no-useless-return': 'error',
|
||||
'no-var': 'error',
|
||||
'no-void': 'error',
|
||||
'no-warning-comments': 'error',
|
||||
'no-with': 'error',
|
||||
'object-curly-spacing': ['error', 'always'],
|
||||
'object-shorthand': 'error',
|
||||
// 'one-var': 'error',
|
||||
'operator-assignment': 'error',
|
||||
'prefer-arrow-callback': 'error',
|
||||
'prefer-const': 'error',
|
||||
'prefer-destructuring': ['error', { 'array': false }],
|
||||
// 'prefer-exponentiation-operator': 'error',
|
||||
// 'prefer-named-capture-group': 'error',
|
||||
'prefer-numeric-literals': 'error',
|
||||
// 'prefer-object-has-own': 'error',
|
||||
'prefer-object-spread': 'error',
|
||||
'prefer-promise-reject-errors': 'error',
|
||||
'prefer-regex-literals': 'error',
|
||||
'prefer-rest-params': 'error',
|
||||
'prefer-spread': 'error',
|
||||
'prefer-template': 'error',
|
||||
'radix': 'error',
|
||||
'require-atomic-updates': 'error',
|
||||
// 'require-await': 'error',
|
||||
// 'require-unicode-regexp': 'error',
|
||||
'require-yield': 'error',
|
||||
'semi': ['error', 'always'],
|
||||
'sort-imports': 'error',
|
||||
// 'sort-keys': 'error',
|
||||
'sort-vars': 'error',
|
||||
'space-before-blocks': 'error',
|
||||
'space-in-parens': 'error',
|
||||
'space-infix-ops': 'error',
|
||||
'strict': 'error',
|
||||
'symbol-description': 'error',
|
||||
'template-curly-spacing': 'error',
|
||||
'unicode-bom': 'error',
|
||||
'use-isnan': 'error',
|
||||
'valid-typeof': 'error',
|
||||
'vars-on-top': 'error',
|
||||
'yoda': 'error'
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['source/index.js'],
|
||||
languageOptions: {
|
||||
sourceType: 'script',
|
||||
ecmaVersion: 2015
|
||||
},
|
||||
rules: {
|
||||
'no-var': 'off',
|
||||
'prefer-arrow-callback': 'off',
|
||||
'prefer-template': 'off',
|
||||
'prefer-destructuring': 'off',
|
||||
'vars-on-top': 'off',
|
||||
'strict': 'off'
|
||||
}
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,774 @@
|
||||
|
||||
import * as child_process from 'child_process';
|
||||
import * as crypto from 'crypto';
|
||||
import * as fs from 'fs/promises';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as url from 'url';
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const read = (match) => {
|
||||
if (args.length > 0 && (!match || args[0] === match)) {
|
||||
return args.shift();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
let configuration = null;
|
||||
|
||||
const dirname = (...args) => {
|
||||
const file = url.fileURLToPath(import.meta.url);
|
||||
const dir = path.dirname(file);
|
||||
return path.join(dir, ...args);
|
||||
};
|
||||
|
||||
const load = async () => {
|
||||
const file = dirname('package.json');
|
||||
const content = await fs.readFile(file, 'utf-8');
|
||||
configuration = JSON.parse(content);
|
||||
};
|
||||
|
||||
const clearLine = () => {
|
||||
if (process.stdout.clearLine) {
|
||||
process.stdout.clearLine();
|
||||
}
|
||||
};
|
||||
|
||||
const write = (message) => {
|
||||
if (process.stdout.write) {
|
||||
process.stdout.write(message);
|
||||
}
|
||||
};
|
||||
|
||||
const writeLine = (message) => {
|
||||
write(message + os.EOL);
|
||||
};
|
||||
|
||||
const access = async (path) => {
|
||||
try {
|
||||
await fs.access(path);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const rm = async (...args) => {
|
||||
const dir = dirname(...args);
|
||||
const exists = await access(dir);
|
||||
if (exists) {
|
||||
const paths = path.join(...args);
|
||||
writeLine(`rm ${paths}`);
|
||||
const options = { recursive: true, force: true };
|
||||
await fs.rm(dir, options);
|
||||
}
|
||||
};
|
||||
|
||||
const mkdir = async (...args) => {
|
||||
const dir = dirname(...args);
|
||||
const exists = await access(dir);
|
||||
if (!exists) {
|
||||
const paths = path.join(...args);
|
||||
writeLine(`mkdir ${paths}`);
|
||||
const options = { recursive: true };
|
||||
await fs.mkdir(dir, options);
|
||||
}
|
||||
return dir;
|
||||
};
|
||||
|
||||
const copy = async (source, target, filter) => {
|
||||
let files = await fs.readdir(source);
|
||||
files = filter ? files.filter((file) => filter(file)) : files;
|
||||
const promises = files.map((file) => fs.copyFile(path.join(source, file), path.join(target, file)));
|
||||
await Promise.all(promises);
|
||||
};
|
||||
|
||||
const unlink = async (dir, filter) => {
|
||||
let files = await fs.readdir(dir);
|
||||
files = filter ? files.filter((file) => filter(file)) : files;
|
||||
const promises = files.map((file) => fs.unlink(path.join(dir, file)));
|
||||
await Promise.all(promises);
|
||||
};
|
||||
|
||||
const exec = async (command, encoding, cwd) => {
|
||||
cwd = cwd || dirname();
|
||||
if (encoding) {
|
||||
return child_process.spawnSync(command, { shell: true, cwd, encoding });
|
||||
}
|
||||
child_process.execSync(command, { cwd, stdio: [0,1,2] });
|
||||
return '';
|
||||
};
|
||||
|
||||
const sleep = (delay) => {
|
||||
return new Promise((resolve) => {
|
||||
global.setTimeout(resolve, delay);
|
||||
});
|
||||
};
|
||||
|
||||
const request = async (url, init, status) => {
|
||||
const response = await global.fetch(url, init);
|
||||
if (status !== false && !response.ok) {
|
||||
throw new Error(`${response.status.toString()} ${response.statusText}`);
|
||||
}
|
||||
if (response.body) {
|
||||
const reader = response.body.getReader();
|
||||
let position = 0;
|
||||
const stream = new global.ReadableStream({
|
||||
start(controller) {
|
||||
const read = async () => {
|
||||
try {
|
||||
const result = await reader.read();
|
||||
if (result.done) {
|
||||
clearLine();
|
||||
controller.close();
|
||||
} else {
|
||||
position += result.value.length;
|
||||
write(` ${position} bytes\r`);
|
||||
controller.enqueue(result.value);
|
||||
read();
|
||||
}
|
||||
} catch (error) {
|
||||
controller.error(error);
|
||||
}
|
||||
};
|
||||
read();
|
||||
}
|
||||
});
|
||||
return new global.Response(stream, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: response.headers
|
||||
});
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
const download = async (url) => {
|
||||
writeLine(`download ${url}`);
|
||||
const response = await request(url);
|
||||
return response.arrayBuffer().then((buffer) => new Uint8Array(buffer));
|
||||
};
|
||||
|
||||
const hash = async (url, algorithm) => {
|
||||
const data = await download(url);
|
||||
const hash = crypto.createHash(algorithm);
|
||||
hash.update(data);
|
||||
return hash.digest('hex');
|
||||
};
|
||||
|
||||
const fork = async (organization, repository) => {
|
||||
const headers = {
|
||||
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`
|
||||
};
|
||||
writeLine(`github delete ${repository}`);
|
||||
await request(`https://api.github.com/repos/${process.env.GITHUB_USER}/${repository}`, {
|
||||
method: 'DELETE',
|
||||
headers
|
||||
}, false);
|
||||
await sleep(4000);
|
||||
writeLine(`github fork ${repository}`);
|
||||
await request(`https://api.github.com/repos/${organization}/${repository}/forks`, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: ''
|
||||
});
|
||||
await sleep(4000);
|
||||
await rm('dist', repository);
|
||||
writeLine(`github clone ${repository}`);
|
||||
await exec(`git clone --depth=2 https://x-access-token:${process.env.GITHUB_TOKEN}@github.com/${process.env.GITHUB_USER}/${repository}.git dist/${repository}`);
|
||||
};
|
||||
|
||||
const pullrequest = async (organization, repository, token, body) => {
|
||||
writeLine(`github push ${repository}`);
|
||||
await exec(`git -C dist/${repository} push`);
|
||||
writeLine(`github pullrequest ${repository}`);
|
||||
const headers = {
|
||||
Authorization: `Bearer ${token}`
|
||||
};
|
||||
await request(`https://api.github.com/repos/${organization}/${repository}/pulls`, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
};
|
||||
|
||||
const clean = async () => {
|
||||
await rm('dist');
|
||||
await rm('node_modules');
|
||||
await rm('package-lock.json');
|
||||
await rm('yarn.lock');
|
||||
if (read('purge')) {
|
||||
await rm('third_party');
|
||||
}
|
||||
};
|
||||
|
||||
const install = async () => {
|
||||
const node_modules = dirname('node_modules');
|
||||
let exists = await access(node_modules);
|
||||
if (exists) {
|
||||
const dependencies = { ...configuration.dependencies, ...configuration.devDependencies };
|
||||
const matches = await Promise.all(Object.entries(dependencies).map(async ([name, version]) => {
|
||||
const file = path.join('node_modules', name, 'package.json');
|
||||
const exists = await access(file);
|
||||
if (exists) {
|
||||
const content = await fs.readFile(file, 'utf8');
|
||||
const obj = JSON.parse(content);
|
||||
return obj.version === version;
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
exists = matches.every((match) => match);
|
||||
if (!exists) {
|
||||
await clean();
|
||||
}
|
||||
}
|
||||
exists = await access(node_modules);
|
||||
if (!exists) {
|
||||
await exec('npm install');
|
||||
}
|
||||
await exec('npx install-electron');
|
||||
try {
|
||||
await exec('python --version', 'utf-8');
|
||||
await exec('python -m pip install --upgrade --quiet setuptools ruff');
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
};
|
||||
|
||||
const start = async () => {
|
||||
await install();
|
||||
await exec('npx electron .');
|
||||
};
|
||||
|
||||
const build = async (target) => {
|
||||
switch (target || read()) {
|
||||
case 'web': {
|
||||
writeLine('build web');
|
||||
await rm('dist', 'web');
|
||||
await mkdir('dist', 'web');
|
||||
writeLine('cp source/dir dist/dir');
|
||||
const source_dir = dirname('source');
|
||||
const dist_dir = dirname('dist', 'web');
|
||||
const extensions = new Set(['html', 'css', 'js', 'json', 'ico', 'png']);
|
||||
await copy(source_dir, dist_dir, (file) => extensions.has(file.split('.').pop()));
|
||||
await rm('dist', 'web', 'app.js');
|
||||
await rm('dist', 'web', 'node.js');
|
||||
await rm('dist', 'web', 'desktop.mjs');
|
||||
const contentFile = dirname('dist', 'web', 'index.html');
|
||||
let content = await fs.readFile(contentFile, 'utf-8');
|
||||
content = content.replace(/(<meta\s*name="version"\s*content=")(.*)(">)/m, (match, p1, p2, p3) => {
|
||||
return p1 + configuration.version + p3;
|
||||
});
|
||||
content = content.replace(/(<meta\s*name="date"\s*content=")(.*)(">)/m, (match, p1, p2, p3) => {
|
||||
return p1 + configuration.date + p3;
|
||||
});
|
||||
await fs.writeFile(contentFile, content, 'utf-8');
|
||||
break;
|
||||
}
|
||||
case 'electron': {
|
||||
const key = read();
|
||||
const target = key ? `electron ${key}` : 'electron';
|
||||
writeLine(`build ${target}`);
|
||||
await install();
|
||||
await exec('npx electron-builder install-app-deps');
|
||||
const table = new Map([
|
||||
['mac', 'npx electron-builder --mac --universal --publish never --config.mac.identity=null'],
|
||||
['windows', 'npx electron-builder --win --x64 --arm64 --publish never --config.win.azureSignOptions='],
|
||||
['linux', 'npx electron-builder --linux --publish never']
|
||||
]);
|
||||
const targets = table.has(key) ? [table.get(key)] : Array.from(table.values());
|
||||
for (const target of targets) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await exec(target);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'python': {
|
||||
writeLine('build python');
|
||||
await exec('python package.py build version');
|
||||
await exec('python -m pip install --user build wheel --quiet');
|
||||
await exec('python -m build --wheel --outdir dist/pypi dist/pypi');
|
||||
if (read('install')) {
|
||||
await exec('python -m pip install --force-reinstall dist/pypi/*.whl');
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
writeLine('build');
|
||||
await rm('dist');
|
||||
await install();
|
||||
await build('web');
|
||||
await build('electron');
|
||||
await build('python');
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const publish = async (target) => {
|
||||
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
|
||||
const GITHUB_USER = process.env.GITHUB_USER;
|
||||
switch (target || read()) {
|
||||
case 'web': {
|
||||
writeLine('publish web');
|
||||
await build('web');
|
||||
await rm('dist', 'gh-pages');
|
||||
const url = `https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_USER}/netron.git`;
|
||||
await exec(`git clone --depth=1 ${url} --branch gh-pages ./dist/gh-pages 2>&1 > /dev/null`);
|
||||
writeLine('cp dist/web dist/gh-pages');
|
||||
const source_dir = dirname('dist', 'web');
|
||||
const target_dir = dirname('dist', 'gh-pages');
|
||||
await unlink(target_dir, (file) => file !== '.git');
|
||||
await copy(source_dir, target_dir);
|
||||
await exec('git -C dist/gh-pages add --all');
|
||||
await exec('git -C dist/gh-pages commit --amend --no-edit');
|
||||
await exec('git -C dist/gh-pages push --force origin gh-pages');
|
||||
break;
|
||||
}
|
||||
case 'electron': {
|
||||
const key = read();
|
||||
const target = key ? ` ${key}` : '';
|
||||
writeLine(`publish electron ${target}`);
|
||||
await install();
|
||||
await exec('npx electron-builder install-app-deps');
|
||||
const table = new Map([
|
||||
['mac', 'npx electron-builder --mac --universal --publish always'],
|
||||
['windows', 'npx electron-builder --win --x64 --arm64 --publish always'],
|
||||
['linux', 'npx electron-builder --linux --publish always']
|
||||
]);
|
||||
const targets = table.has(key) ? [table.get(key)] : Array.from(table.values());
|
||||
for (const target of targets) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await exec(target);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'python': {
|
||||
writeLine('publish python');
|
||||
await build('python');
|
||||
await exec('python -m pip install --user twine');
|
||||
await exec('python -m twine upload --non-interactive --skip-existing --verbose dist/pypi/*.whl');
|
||||
break;
|
||||
}
|
||||
case 'cask': {
|
||||
writeLine('publish cask');
|
||||
await fork('Homebrew', 'homebrew-cask');
|
||||
const repository = `https://github.com/${configuration.repository}`;
|
||||
const url = `${repository}/releases/download/v#{version}/${configuration.productName}-#{version}-mac.zip`;
|
||||
const sha256 = await hash(url.replace(/#{version}/g, configuration.version), 'sha256');
|
||||
writeLine('update manifest');
|
||||
const dir = await mkdir('dist', 'homebrew-cask', 'Casks', 'n');
|
||||
const file = path.join(dir, 'netron.rb');
|
||||
await fs.writeFile(file, [
|
||||
`cask "${configuration.name}" do`,
|
||||
` version "${configuration.version}"`,
|
||||
` sha256 "${sha256.toLowerCase()}"`,
|
||||
'',
|
||||
` url "${url}"`,
|
||||
` name "${configuration.productName}"`,
|
||||
` desc "${configuration.description.replace('Visualizer', 'Visualiser')}"`,
|
||||
` homepage "${repository}"`,
|
||||
'',
|
||||
' auto_updates true',
|
||||
'',
|
||||
` app "${configuration.productName}.app"`,
|
||||
'',
|
||||
' zap trash: [',
|
||||
` "~/Library/Application Support/${configuration.productName}",`,
|
||||
` "~/Library/Preferences/${configuration.build.appId}.plist",`,
|
||||
` "~/Library/Saved Application State/${configuration.build.appId}.savedState",`,
|
||||
' ]',
|
||||
'end',
|
||||
''
|
||||
].join('\n'));
|
||||
writeLine('git push homebrew-cask');
|
||||
await exec('git -C dist/homebrew-cask add --all');
|
||||
await exec(`git -C dist/homebrew-cask commit -m "${configuration.name} ${configuration.version}"`);
|
||||
await pullrequest('Homebrew', 'homebrew-cask', process.env.GITHUB_TOKEN, {
|
||||
title: `${configuration.name} ${configuration.version}`,
|
||||
body: 'Update version and sha256',
|
||||
head: `${process.env.GITHUB_USER}:master`,
|
||||
base: 'master'
|
||||
});
|
||||
await rm('dist', 'homebrew-cask');
|
||||
break;
|
||||
}
|
||||
case 'winget': {
|
||||
writeLine('publish winget');
|
||||
await fork('microsoft', 'winget-pkgs');
|
||||
const name = configuration.name;
|
||||
const version = configuration.version;
|
||||
const product = configuration.productName;
|
||||
const publisher = configuration.author.name;
|
||||
const identifier = `${publisher.replace(' ', '')}.${product}`;
|
||||
const copyright = `Copyright (c) ${publisher}`;
|
||||
const repository = `https://github.com/${configuration.repository}`;
|
||||
const url = `${repository}/releases/download/v${version}/${product}-Setup-${version}.exe`;
|
||||
const content = await fs.readFile(configuration.build.extends, 'utf-8');
|
||||
const builder = JSON.parse(content);
|
||||
const extensions = builder.fileAssociations.map((entry) => `- ${entry.ext}`).sort().join('\n');
|
||||
const sha256 = await hash(url, 'sha256');
|
||||
const paths = ['dist', 'winget-pkgs', 'manifests', publisher[0].toLowerCase(), publisher.replace(' ', ''), product, version];
|
||||
await mkdir(...paths);
|
||||
writeLine('update manifest');
|
||||
const manifestFile = dirname(...paths, identifier);
|
||||
await fs.writeFile(`${manifestFile}.yaml`, [
|
||||
'# yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.12.0.schema.json',
|
||||
`PackageIdentifier: ${identifier}`,
|
||||
`PackageVersion: ${version}`,
|
||||
'DefaultLocale: en-US',
|
||||
'ManifestType: version',
|
||||
'ManifestVersion: 1.12.0',
|
||||
''
|
||||
].join('\n'));
|
||||
await fs.writeFile(`${manifestFile}.installer.yaml`, [
|
||||
'# yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.12.0.schema.json',
|
||||
`PackageIdentifier: ${identifier}`,
|
||||
`PackageVersion: ${version}`,
|
||||
'Platform:',
|
||||
'- Windows.Desktop',
|
||||
'InstallModes:',
|
||||
'- silent',
|
||||
'- silentWithProgress',
|
||||
'Installers:',
|
||||
'- Architecture: x86',
|
||||
' Scope: user',
|
||||
' InstallerType: nullsoft',
|
||||
` InstallerUrl: ${url}`,
|
||||
` InstallerSha256: ${sha256.toUpperCase()}`,
|
||||
' InstallerLocale: en-US',
|
||||
' InstallerSwitches:',
|
||||
' Custom: /NORESTART',
|
||||
' UpgradeBehavior: install',
|
||||
'- Architecture: arm64',
|
||||
' Scope: user',
|
||||
' InstallerType: nullsoft',
|
||||
` InstallerUrl: ${url}`,
|
||||
` InstallerSha256: ${sha256.toUpperCase()}`,
|
||||
' InstallerLocale: en-US',
|
||||
' InstallerSwitches:',
|
||||
' Custom: /NORESTART',
|
||||
' UpgradeBehavior: install',
|
||||
'FileExtensions:',
|
||||
extensions,
|
||||
'ManifestType: installer',
|
||||
'ManifestVersion: 1.12.0',
|
||||
''
|
||||
].join('\n'));
|
||||
await fs.writeFile(`${manifestFile}.locale.en-US.yaml`, [
|
||||
'# yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.12.0.schema.json',
|
||||
`PackageIdentifier: ${identifier}`,
|
||||
`PackageVersion: ${version}`,
|
||||
`PackageName: ${product}`,
|
||||
'PackageLocale: en-US',
|
||||
`PackageUrl: ${repository}`,
|
||||
`Publisher: ${publisher}`,
|
||||
`PublisherUrl: ${repository}`,
|
||||
`PublisherSupportUrl: ${repository}/issues`,
|
||||
`Author: ${publisher}`,
|
||||
`License: ${configuration.license}`,
|
||||
`Copyright: ${copyright}`,
|
||||
`CopyrightUrl: ${repository}/blob/main/LICENSE`,
|
||||
`ShortDescription: ${configuration.description}`,
|
||||
`Description: ${configuration.description}`,
|
||||
`Moniker: ${name}`,
|
||||
'Tags:',
|
||||
'- machine-learning',
|
||||
'- deep-learning',
|
||||
'- neural-network',
|
||||
'ManifestType: defaultLocale',
|
||||
'ManifestVersion: 1.12.0',
|
||||
''
|
||||
].join('\n'));
|
||||
writeLine('git push winget-pkgs');
|
||||
await exec('git -C dist/winget-pkgs add --all');
|
||||
await exec(`git -C dist/winget-pkgs commit -m "Update ${configuration.name} to ${configuration.version}"`);
|
||||
await pullrequest('microsoft', 'winget-pkgs', process.env.WINGET_TOKEN, {
|
||||
title: `Update ${configuration.productName} to ${configuration.version}`,
|
||||
body: '',
|
||||
head: `${process.env.GITHUB_USER}:master`,
|
||||
base: 'master'
|
||||
});
|
||||
await rm('dist', 'winget-pkgs');
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
writeLine('publish');
|
||||
await rm('dist');
|
||||
await install();
|
||||
await publish('web');
|
||||
await publish('electron');
|
||||
await publish('python');
|
||||
await publish('cask');
|
||||
await publish('winget');
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const lint = async () => {
|
||||
await install();
|
||||
writeLine('eslint');
|
||||
await exec('npx eslint --cache --cache-location ./dist/lint/.eslintcache');
|
||||
writeLine('ruff');
|
||||
await exec('python -m ruff check . --quiet');
|
||||
};
|
||||
|
||||
const test = async (target) => {
|
||||
let models = true;
|
||||
while (true) {
|
||||
if (target === 'desktop' || read('desktop')) {
|
||||
target = null;
|
||||
models = false;
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await exec('npx playwright install --with-deps chromium');
|
||||
const host = process.platform === 'linux' && (process.env.GITHUB_ACTIONS || process.env.CI) ? 'xvfb-run -a ' : '';
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await exec(`${host}npx playwright test --config=test/playwright.config.js --project=desktop`);
|
||||
continue;
|
||||
}
|
||||
if (target === 'browser' || read('browser')) {
|
||||
target = null;
|
||||
models = false;
|
||||
if (process.platform !== 'win32') {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await exec('npx playwright install --with-deps chromium');
|
||||
const headed = process.env.GITHUB_ACTIONS || process.env.CI ? '' : ' --headed';
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await exec(`npx playwright test --config=test/playwright.config.js --project=browser${headed}`);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (models) {
|
||||
target = target || args.join(' ');
|
||||
await exec(`node test/models.js ${target}`);
|
||||
}
|
||||
};
|
||||
|
||||
const validate = async () => {
|
||||
writeLine('lint');
|
||||
await lint();
|
||||
writeLine('test');
|
||||
await test('tag:validation');
|
||||
writeLine('test desktop');
|
||||
await test('desktop');
|
||||
writeLine('test browser');
|
||||
await test('browser');
|
||||
};
|
||||
|
||||
const update = async () => {
|
||||
const staged = await exec('git diff --cached --name-only', 'utf-8');
|
||||
if (staged.stdout.trim()) {
|
||||
throw new Error('Staged changes.');
|
||||
}
|
||||
const modified = await exec('git diff --name-only -- package.json package-lock.json', 'utf-8');
|
||||
if (modified.stdout.trim()) {
|
||||
throw new Error('Uncommitted changes.');
|
||||
}
|
||||
const filter = new Set(process.argv.length > 3 ? process.argv.slice(3) : []);
|
||||
if (filter.size === 0) {
|
||||
const output = await exec('npm outdated --json', 'utf-8');
|
||||
const entries = JSON.parse(output.stdout);
|
||||
const compare = (a, b) => {
|
||||
const regex = /^\d+\.\d+\.\d+$/;
|
||||
if (!regex.test(a)) {
|
||||
throw new Error(`Invalid version format '${a}'.`);
|
||||
}
|
||||
if (!regex.test(b)) {
|
||||
throw new Error(`Invalid version format '${b}'.`);
|
||||
}
|
||||
a = a.split('.').map((v) => Number(v));
|
||||
b = b.split('.').map((v) => Number(v));
|
||||
if (a.length !== b.length) {
|
||||
throw new Error(`Invalid version a=${a.join('.')} b=${b.join('.')}`);
|
||||
}
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if ((a[i] || 0) > (b[i] || 0)) {
|
||||
return 1;
|
||||
}
|
||||
if ((a[i] || 0) < (b[i] || 0)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
for (const [name, entry] of Object.entries(entries)) {
|
||||
if (compare(entry.wanted, entry.latest) < 0) {
|
||||
writeLine(`${name} ${entry.latest}`);
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await exec(`npm install --quiet --no-progress --silent --save-exact ${name}@latest`);
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await exec('git add package.json package-lock.json');
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await exec(`git commit --quiet -m "Update to ${name} ${entry.latest}"`);
|
||||
}
|
||||
}
|
||||
await exec('npx install-electron');
|
||||
}
|
||||
let targets = [
|
||||
'armnn',
|
||||
'bigdl',
|
||||
'caffe', 'circle', 'cntk', 'coreml',
|
||||
'dlc', 'dnn',
|
||||
'espdl', 'executorch',
|
||||
'gguf',
|
||||
'jax',
|
||||
'kann', 'keras',
|
||||
'litertlm',
|
||||
'megengine', 'mindspore', 'mlir', 'mnn',
|
||||
'nnabla',
|
||||
'onnx', 'om',
|
||||
'paddle', 'pytorch',
|
||||
'rknn',
|
||||
'sentencepiece', 'sklearn',
|
||||
'tf', 'tosa',
|
||||
'uff',
|
||||
'xmodel'
|
||||
];
|
||||
let commands = [
|
||||
'sync',
|
||||
'install',
|
||||
'schema',
|
||||
'metadata'
|
||||
];
|
||||
if (filter.size > 0 && targets.some((target) => filter.has(target))) {
|
||||
targets = targets.filter((target) => filter.has(target));
|
||||
}
|
||||
if (filter.size > 0 && commands.some((target) => filter.has(target))) {
|
||||
commands = commands.filter((command) => filter.has(command));
|
||||
}
|
||||
commands = commands.join(' ');
|
||||
for (const target of targets) {
|
||||
if (process.platform === 'win32') {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await exec(`bash tools/${target} ${commands}`);
|
||||
} else {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await exec(`tools/${target} ${commands}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const pull = async () => {
|
||||
await exec('git fetch --prune origin "refs/tags/*:refs/tags/*"');
|
||||
let before = await exec('git rev-parse HEAD', 'utf-8');
|
||||
if (before.status !== 0) {
|
||||
throw new Error(before.stderr.trim());
|
||||
}
|
||||
try {
|
||||
await exec('git pull --prune --rebase --autostash');
|
||||
} catch (error) {
|
||||
writeLine(error.message);
|
||||
}
|
||||
let after = await exec('git rev-parse HEAD', 'utf-8');
|
||||
if (after.status !== 0) {
|
||||
throw new Error(after.stderr.trim());
|
||||
}
|
||||
before = before.stdout.trim();
|
||||
after = after.stdout.trim();
|
||||
if (before !== after) {
|
||||
const output = await exec(`git diff --name-only ${before} ${after}`, 'utf-8');
|
||||
const files = new Set(output.stdout.split('\n'));
|
||||
if (files.has('package.json')) {
|
||||
await clean();
|
||||
await install();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const coverage = async () => {
|
||||
switch (read()) {
|
||||
case 'desktop': {
|
||||
await rm('dist', 'nyc');
|
||||
await mkdir('dist', 'nyc');
|
||||
await exec('cp package.json dist/nyc');
|
||||
await exec('cp -R source dist/nyc');
|
||||
await exec('nyc instrument --compact false source dist/nyc/source');
|
||||
const file = dirname('dist', 'nyc', 'source', 'index.html');
|
||||
let content = await fs.readFile(file, 'utf-8');
|
||||
content = content.replace(`"script-src 'self'"`, `"script-src 'self' 'unsafe-eval'"`);
|
||||
await fs.writeFile(file, content, 'utf-8');
|
||||
await exec('nyc --instrument npx electron ./dist/nyc');
|
||||
await exec('nyc report');
|
||||
break;
|
||||
}
|
||||
case 'test': {
|
||||
const target = args.join(' ');
|
||||
await rm('dist', 'c8');
|
||||
await exec(`npx c8 --reporter=html --report-dir=dist/c8/report node test/models.js ${target}`);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new Error('Unsupported coverage target.');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const analyze = async () => {
|
||||
const exists = await access('third_party/tools/codeql');
|
||||
if (!exists) {
|
||||
await exec('git clone --depth=1 https://github.com/github/codeql.git third_party/tools/codeql');
|
||||
}
|
||||
await rm('dist', 'codeql');
|
||||
await mkdir('dist', 'codeql', 'netron');
|
||||
await exec('cp -r publish source test tools dist/codeql/netron/');
|
||||
await exec('codeql database create dist/codeql/database --source-root dist/codeql/netron --language=javascript --threads=3');
|
||||
await exec('codeql database analyze dist/codeql/database ./third_party/tools/codeql/javascript/ql/src/codeql-suites/javascript-security-and-quality.qls --format=csv --output=dist/codeql/results.csv --threads=3');
|
||||
await exec('cat dist/codeql/results.csv');
|
||||
};
|
||||
|
||||
const release = async () => {
|
||||
await pull();
|
||||
const file = dirname('package.json');
|
||||
let content = await fs.readFile(file, 'utf-8');
|
||||
content = content.replace(/(\s*"version":\s")(\d\.\d\.\d)(",)/m, (match, p1, p2, p3) => {
|
||||
const version = Array.from((parseInt(p2.split('.').join(''), 10) + 1).toString()).join('.');
|
||||
return p1 + version + p3;
|
||||
});
|
||||
content = content.replace(/(\s*"date":\s")(.*)(",)/m, (match, p1, p2, p3) => {
|
||||
const date = new Date().toISOString().split('.').shift().split('T').join(' ');
|
||||
return p1 + date + p3;
|
||||
});
|
||||
await fs.writeFile(file, content, 'utf-8');
|
||||
await exec('npm install --package-lock-only');
|
||||
await load();
|
||||
await exec('git add package.json');
|
||||
await exec('git add package-lock.json');
|
||||
await exec(`git commit -m "Update to ${configuration.version}"`);
|
||||
await exec(`git tag v${configuration.version}`);
|
||||
await exec('git push');
|
||||
await exec('git push --tags');
|
||||
};
|
||||
|
||||
const main = async () => {
|
||||
await load();
|
||||
try {
|
||||
const task = read();
|
||||
switch (task) {
|
||||
case 'start': await start(); break;
|
||||
case 'clean': await clean(); break;
|
||||
case 'install': await install(); break;
|
||||
case 'build': await build(); break;
|
||||
case 'publish': await publish(); break;
|
||||
case 'release': await release(); break;
|
||||
case 'lint': await lint(); break;
|
||||
case 'test': await test(); break;
|
||||
case 'validate': await validate(); break;
|
||||
case 'update': await update(); break;
|
||||
case 'pull': await pull(); break;
|
||||
case 'analyze': await analyze(); break;
|
||||
case 'coverage': await coverage(); break;
|
||||
default: throw new Error(`Unsupported task '${task}'.`);
|
||||
}
|
||||
} catch (error) {
|
||||
if (process.stdout.write) {
|
||||
process.stdout.write(error.message + os.EOL);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
await main();
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "netron",
|
||||
"productName": "Netron",
|
||||
"author": {
|
||||
"name": "Lutz Roeder",
|
||||
"email": "lutzroeder@users.noreply.github.com",
|
||||
"url": "https://www.lutzroeder.com"
|
||||
},
|
||||
"version": "9.1.5",
|
||||
"date": "2026-07-10 23:12:08",
|
||||
"description": "Visualizer for neural network, deep learning, and machine learning models",
|
||||
"license": "MIT",
|
||||
"repository": "lutzroeder/netron",
|
||||
"type": "module",
|
||||
"main": "source/app.js",
|
||||
"scripts": {
|
||||
"start": "node package.js start",
|
||||
"server": "python package.py build start",
|
||||
"clean": "node package.js clean",
|
||||
"install": "node package.js install",
|
||||
"lint": "node package.js lint",
|
||||
"build": "node package.js build",
|
||||
"publish": "node package.js publish",
|
||||
"release": "node package.js release",
|
||||
"test": "node package.js test",
|
||||
"validate": "node package.js validate",
|
||||
"coverage": "node package.js coverage",
|
||||
"analyze": "node package.js analyze",
|
||||
"update": "node package.js update",
|
||||
"pull": "node package.js pull"
|
||||
},
|
||||
"dependencies": {
|
||||
"electron-updater": "6.8.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron/notarize": "3.1.1",
|
||||
"@playwright/test": "1.61.1",
|
||||
"electron": "43.1.0",
|
||||
"electron-builder": "26.15.3",
|
||||
"eslint": "10.7.0"
|
||||
},
|
||||
"build": {
|
||||
"extends": "publish/electron-builder.json"
|
||||
},
|
||||
"nyc": {
|
||||
"reporter": [
|
||||
"lcov",
|
||||
"html"
|
||||
],
|
||||
"report-dir": "dist/nyc/report",
|
||||
"temp-dir": "dist/nyc/.nyc_output"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
argv = sys.argv[1:]
|
||||
|
||||
root_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
dist_dir = os.path.join(root_dir, "dist")
|
||||
dist_pypi_dir = os.path.join(dist_dir, "pypi")
|
||||
|
||||
def _build():
|
||||
import shutil
|
||||
source_dir = os.path.join(root_dir, "source")
|
||||
shutil.rmtree(os.path.join(source_dir, "__pycache__"), ignore_errors=True)
|
||||
shutil.rmtree(dist_pypi_dir, ignore_errors=True)
|
||||
shutil.copytree(source_dir, os.path.join(dist_pypi_dir, "netron"))
|
||||
shutil.copyfile(
|
||||
os.path.join(root_dir, "pyproject.toml"),
|
||||
os.path.join(dist_pypi_dir, "pyproject.toml"))
|
||||
os.remove(os.path.join(dist_pypi_dir, "netron", "desktop.mjs"))
|
||||
os.remove(os.path.join(dist_pypi_dir, "netron", "app.js"))
|
||||
|
||||
def _install():
|
||||
import pip._internal.cli.main
|
||||
pip._internal.cli.main.main(["install", dist_pypi_dir])
|
||||
|
||||
def _version():
|
||||
import json
|
||||
import re
|
||||
path = os.path.join(root_dir, "package.json")
|
||||
with open(path, encoding="utf-8") as file:
|
||||
package = json.load(file)
|
||||
version = package["version"]
|
||||
date = package["date"]
|
||||
entries = [
|
||||
("pyproject.toml", '(version\\s*=\\s*")(.*)(")', version),
|
||||
("netron/server.py", '(__version__\\s=\\s")(.*)(")', version),
|
||||
("netron/index.html", '(<meta name="version" content=")(.*)(">)', version),
|
||||
("netron/index.html", '(<meta name="date" content=")(.*)(">)', date)
|
||||
]
|
||||
for path, regex, value in entries:
|
||||
path = os.path.join(dist_pypi_dir, path)
|
||||
with open(path, encoding="utf-8") as file:
|
||||
content = file.read()
|
||||
content, count = re.subn(regex, rf"\g<1>{value}\g<3>", content)
|
||||
if count == 0:
|
||||
raise ValueError(f"Failed to update '{path}' with '{value}'.")
|
||||
with open(path, "w", encoding="utf-8") as file:
|
||||
file.write(content)
|
||||
|
||||
def _start():
|
||||
""" Start server """
|
||||
sys.path.insert(0, os.path.join(root_dir, "dist", "pypi"))
|
||||
__import__("netron").main()
|
||||
argv.clear()
|
||||
|
||||
def main():
|
||||
table = {
|
||||
"build": _build,
|
||||
"install": _install,
|
||||
"version": _version,
|
||||
"start": _start
|
||||
}
|
||||
while len(argv) > 0:
|
||||
command = argv.pop(0)
|
||||
del sys.argv[1]
|
||||
table[command]()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
After Width: | Height: | Size: 8.2 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns='http://www.w3.org/2000/svg' width='1320' height='800' viewBox='0 0 1320 800'>
|
||||
<rect x='0' y='0' width='1320' height='800' fill='#f8f8f8'/>
|
||||
<path d='M 645 325 L 677 360 L 645 395' fill='none' stroke-width='14' stroke='#aaa' stroke-linecap='square' />
|
||||
<path d='M 645 325 L 677 360 L 645 395' fill='none' stroke-width='12' stroke='#bbb' stroke-linecap='square' />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 391 B |
|
After Width: | Height: | Size: 8.0 KiB |
@@ -0,0 +1,120 @@
|
||||
{
|
||||
"appId": "com.lutzroeder.netron",
|
||||
"productName": "Netron",
|
||||
"files": [ "source/**/*" ],
|
||||
"directories": { "buildResources": "./publish" },
|
||||
"fileAssociations": [
|
||||
{ "ext": "armnn", "name": "Arm NN Model" },
|
||||
{ "ext": "caffemodel", "name": "Caffe Model" },
|
||||
{ "ext": "circle", "name": "Circle Model" },
|
||||
{ "ext": "ckpt", "name": "Model Checkpoint" },
|
||||
{ "ext": "cmf", "name": "CNTK Model" },
|
||||
{ "ext": "dlc", "name": "DLC Model" },
|
||||
{ "ext": "dnn", "name": "CNTK Model" },
|
||||
{ "ext": "espdl", "name": "ESP-DL Model" },
|
||||
{ "ext": "gguf", "name": "GGUF Model" },
|
||||
{ "ext": "h5", "name": "Keras Model" },
|
||||
{ "ext": "hd5", "name": "Keras Model" },
|
||||
{ "ext": "hdf5", "name": "Keras Model" },
|
||||
{ "ext": "hn", "name": "Hailo Model" },
|
||||
{ "ext": "jax_export", "name": "JAX Export Model" },
|
||||
{ "ext": "jax_exported", "name": "JAX Export Model" },
|
||||
{ "ext": "kann", "name": "KaNN Model" },
|
||||
{ "ext": "kgraph", "name": "KaNN Model" },
|
||||
{ "ext": "keras", "name": "Keras Model" },
|
||||
{ "ext": "kmodel", "name": "Kendryte Model" },
|
||||
{ "ext": "lite", "name": "TensorFlow Lite Model" },
|
||||
{ "ext": "litertlm", "name": "LiteRT-LM Model" },
|
||||
{ "ext": "mar", "name": "MXNet Model" },
|
||||
{ "ext": "maxviz", "name": "Modular Model" },
|
||||
{ "ext": "meta", "name": "TensorFlow Meta Graph" },
|
||||
{ "ext": "mge", "name": "MegEngine Mge Model" },
|
||||
{ "ext": "mindir", "name": "MindIR Model" },
|
||||
{ "ext": "mlmodel", "name": "Core ML Model" },
|
||||
{ "ext": "mlir", "name": "MLIR Model" },
|
||||
{ "ext": "mlirbc", "name": "MLIR Bytecode Model" },
|
||||
{ "ext": "mlnet", "name": "ML.NET Model" },
|
||||
{ "ext": "mlpackage", "name": "Core ML Model Package", "isPackage": true },
|
||||
{ "ext": "mnn", "name": "MNN Model" },
|
||||
{ "ext": "model", "name": "Model" },
|
||||
{ "ext": "nb", "name": "Paddle Lite Model" },
|
||||
{ "ext": "ngf", "name": "ONNX Model" },
|
||||
{ "ext": "nn", "name": "Barracuda Model" },
|
||||
{ "ext": "nnp", "name": "Neural Network Libraries" },
|
||||
{ "ext": "npy", "name": "NumPy Array" },
|
||||
{ "ext": "npz", "name": "NumPy Archive" },
|
||||
{ "ext": "om", "name": "DaVinci OM Model" },
|
||||
{ "ext": "onnx", "name": "ONNX Model" },
|
||||
{ "ext": "ort", "name": "ONNX Runtime Model" },
|
||||
{ "ext": "paddle", "name": "PaddlePaddle Model" },
|
||||
{ "ext": "param", "name": "NCNN Model" },
|
||||
{ "ext": "pb", "name": "Protocol Buffer" },
|
||||
{ "ext": "pbtxt", "name": "Text Protocol Buffer" },
|
||||
{ "ext": "pdiparams", "name": "PaddlePaddle Model" },
|
||||
{ "ext": "pdmodel", "name": "PaddlePaddle Model" },
|
||||
{ "ext": "pdopt", "name": "PaddlePaddle Model" },
|
||||
{ "ext": "pdparams", "name": "PaddlePaddle Model" },
|
||||
{ "ext": "pickle", "name": "Python Pickle File" },
|
||||
{ "ext": "pkl", "name": "Python Pickle File" },
|
||||
{ "ext": "prototxt", "name": "Text Protocol Buffer" },
|
||||
{ "ext": "pt", "name": "PyTorch Model" },
|
||||
{ "ext": "pt2", "name": "PyTorch Model" },
|
||||
{ "ext": "pte", "name": "ExecuTorch Model" },
|
||||
{ "ext": "pth", "name": "PyTorch Model" },
|
||||
{ "ext": "ptl", "name": "PyTorch Model" },
|
||||
{ "ext": "rknn", "name": "RKNN Model" },
|
||||
{ "ext": "safetensors", "name": "Safetensors Checkpoint" },
|
||||
{ "ext": "t7", "name": "Torch Model" },
|
||||
{ "ext": "tfl", "name": "TensorFlow Lite Model" },
|
||||
{ "ext": "tflite", "name": "TensorFlow Lite Model" },
|
||||
{ "ext": "tmfile", "name": "Tengine Model" },
|
||||
{ "ext": "tm", "name": "MegEngine Traced Model" },
|
||||
{ "ext": "tnnproto", "name": "TNN Model" },
|
||||
{ "ext": "tosa", "name": "TOSA Model" },
|
||||
{ "ext": "torchscript", "name": "PyTorch Model" },
|
||||
{ "ext": "uff", "name": "UFF Model" },
|
||||
{ "ext": "xmodel", "name": "Vitis AI Model" }
|
||||
],
|
||||
"publish": [
|
||||
{ "provider": "github", "releaseType": "release" }
|
||||
],
|
||||
"linux": {
|
||||
"artifactName":"${productName}-${version}-${arch}.${ext}",
|
||||
"executableName": "netron",
|
||||
"target": [
|
||||
{ "target": "deb", "arch": ["x64"] },
|
||||
{ "target": "rpm", "arch": ["x64"] }
|
||||
]
|
||||
},
|
||||
"mac": {
|
||||
"artifactName": "${productName}-${version}-mac.${ext}",
|
||||
"category": "public.app-category.developer-tools",
|
||||
"darkModeSupport": true,
|
||||
"gatekeeperAssess": false,
|
||||
"hardenedRuntime": true,
|
||||
"notarize": true,
|
||||
"target": [ "dmg", "zip" ]
|
||||
},
|
||||
"win": {
|
||||
"target": [ "nsis" ],
|
||||
"azureSignOptions" : {
|
||||
"publisherName": "Lutz Roeder",
|
||||
"endpoint": "https://eus.codesigning.azure.net/",
|
||||
"codeSigningAccountName": "lutzroeder",
|
||||
"certificateProfileName": "lutzroeder"
|
||||
}
|
||||
},
|
||||
"dmg": {
|
||||
"artifactName": "${productName}-${version}.${ext}",
|
||||
"title": "${productName} ${version}",
|
||||
"writeUpdateInfo": false,
|
||||
"iconSize": 160,
|
||||
"contents": [
|
||||
{ "x": 180, "y": 170 },
|
||||
{ "x": 480, "y": 170, "type": "link", "path": "/Applications" }
|
||||
]
|
||||
},
|
||||
"nsis": {
|
||||
"differentialPackage": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<div style="height: 400px;">
|
||||
<div style="background: #f6f6f6; width: 400px; height: 400px; float: left;">
|
||||
<img src="icon.svg" width="300" style="position: relative; top: 50px; left: 50px;">
|
||||
</div>
|
||||
<div style="background: #d8d8d8; width: 400px; height: 400px; float: left;">
|
||||
<img src="icon.svg" width="300" style="position: relative; top: 50px; left: 50px;">
|
||||
</div>
|
||||
<div style="background: #1e1e1e; width: 400px; height: 400px; float: left;">
|
||||
<img src="icon.svg" width="300" style="position: relative; top: 50px; left: 50px;">
|
||||
</div>
|
||||
</div>
|
||||
<div style="height: 400px;">
|
||||
<div style="background: #f6f6f6; width: 400px; height: 400px; float: left;">
|
||||
<img src="icon.icns.svg" width="300" style="position: relative; top: 50px; left: 50px;">
|
||||
</div>
|
||||
<div style="background: #d8d8d8; width: 400px; height: 400px; float: left;">
|
||||
<img src="icon.icns.svg" width="300" style="position: relative; top: 50px; left: 50px;">
|
||||
</div>
|
||||
<div style="background: #1e1e1e; width: 400px; height: 400px; float: left;">
|
||||
<img src="icon.icns.svg" width="300" style="position: relative; top: 50px; left: 50px;">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,175 @@
|
||||
<svg xmlns='http://www.w3.org/2000/svg' width='1024' height='1024' viewBox='0 0 1024 1024'>
|
||||
|
||||
<style>
|
||||
.background-shadow {
|
||||
filter: url(#background-dropshadow);
|
||||
}
|
||||
.node-border {
|
||||
fill: #eeeeee;
|
||||
opacity: 1.0;
|
||||
}
|
||||
.node {
|
||||
fill: #307295;
|
||||
opacity: 1;
|
||||
}
|
||||
.node-shadow {
|
||||
filter: url(#node-dropshadow);
|
||||
opacity: 0.3;
|
||||
}
|
||||
.line-border {
|
||||
stroke: #000000;
|
||||
opacity: 0.2;
|
||||
}
|
||||
.line {
|
||||
stroke: #eeeeee;
|
||||
opacity: 1.0;
|
||||
}
|
||||
.line-shadow {
|
||||
stroke: #000000;
|
||||
filter: url(#node-dropshadow);
|
||||
opacity: 0.2;
|
||||
}
|
||||
</style>
|
||||
|
||||
<filter id='background-dropshadow'>
|
||||
<feGaussianBlur in='SourceAlpha' stdDeviation='20' />
|
||||
</filter>
|
||||
|
||||
<linearGradient id='background-fill' gradientTransform='rotate(90)'>
|
||||
<stop offset='0' stop-color='#ffffff'/>
|
||||
<stop offset='1' stop-color='#d2d2d2'/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id='circle-stroke' gradientUnits='userSpaceOnUse' x1='0' y1='62' x2='0' y2='962'>
|
||||
<stop offset='0' stop-color='#aaaaaa'/>
|
||||
<stop offset='1' stop-color='#ffffff'/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id='circle-fill' gradientUnits='userSpaceOnUse' x1='0' y1='100' x2='0' y2='924'>
|
||||
<stop offset='0' stop-color='#4baac5' stop-opacity="1" />
|
||||
<stop offset='1' stop-color='#307295' stop-opacity="1" />
|
||||
</linearGradient>
|
||||
|
||||
<filter id='node-dropshadow' x="-30%" y="-30%" width="160%" height="160%">
|
||||
<feGaussianBlur in='SourceGraphic' stdDeviation='15' />
|
||||
</filter>
|
||||
|
||||
<rect x='117' y='124' width='790' height='790' rx='160' class='background-shadow' />
|
||||
<rect x='100' y='100' width='824' height='824' rx='160' fill='url(#background-fill)' />
|
||||
<circle cx='512' cy='512' r='356' stroke-width='4' stroke='url(#circle-stroke)' fill='url(#circle-fill)' />
|
||||
|
||||
<g transform='translate(512,512) scale(1.1)'>
|
||||
|
||||
<g transform='translate(6,12)' opacity='1.0'>
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='-180' stroke-width='24' class='line-shadow' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='-180' stroke-width='24' class='line-shadow' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='-60' stroke-width='24' class='line-shadow' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='-60' stroke-width='24' class='line-shadow' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='60' stroke-width='24' class='line-shadow' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='60' stroke-width='24' class='line-shadow' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='180' stroke-width='24' class='line-shadow' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='180' stroke-width='24' class='line-shadow' />
|
||||
|
||||
<line x1='20' y1='-180' x2='208' y2='0' stroke-width='24' class='line-shadow' />
|
||||
<line x1='20' y1='-60' x2='208' y2='0' stroke-width='24' class='line-shadow' />
|
||||
<line x1='20' y1='60' x2='208' y2='0' stroke-width='24' class='line-shadow' />
|
||||
<line x1='20' y1='180' x2='208' y2='0' stroke-width='24' class='line-shadow' />
|
||||
|
||||
<g transform='translate(-168,-92)'>
|
||||
<circle cx='0' cy='0' r='40' class='node-shadow' />
|
||||
</g>
|
||||
<g transform='translate(-168,92)'>
|
||||
<circle cx='0' cy='0' r='40' class='node-shadow' />
|
||||
</g>
|
||||
|
||||
<g transform='translate(20,-180)'>
|
||||
<circle cx='0' cy='0' r='40' class='node-shadow' />
|
||||
</g>
|
||||
<g transform='translate(20,-60)'>
|
||||
<circle cx='0' cy='0' r='40' class='node-shadow' />
|
||||
</g>
|
||||
<g transform='translate(20,60)'>
|
||||
<circle cx='0' cy='0' r='40' class='node-shadow' />
|
||||
</g>
|
||||
<g transform='translate(20,180)'>
|
||||
<circle cx='0' cy='0' r='40' class='node-shadow' />
|
||||
</g>
|
||||
|
||||
<g transform='translate(208,0)'>
|
||||
<circle cx='0' cy='0' r='40' class='node-shadow' />
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='-180' stroke-width='24' class='line-border' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='-180' stroke-width='24' class='line-border' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='-60' stroke-width='24' class='line-border' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='-60' stroke-width='24' class='line-border' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='60' stroke-width='24' class='line-border' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='60' stroke-width='24' class='line-border' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='180' stroke-width='24' class='line-border' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='180' stroke-width='24' class='line-border' />
|
||||
|
||||
<line x1='20' y1='-180' x2='208' y2='0' stroke-width='24' class='line-border' />
|
||||
<line x1='20' y1='-60' x2='208' y2='0' stroke-width='24' class='line-border' />
|
||||
<line x1='20' y1='+60' x2='208' y2='0' stroke-width='24' class='line-border' />
|
||||
<line x1='20' y1='+180' x2='208' y2='0' stroke-width='24' class='line-border' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='-180' stroke-width='10' class='line' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='-180' stroke-width='10' class='line' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='-60' stroke-width='10' class='line' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='-60' stroke-width='10' class='line' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='60' stroke-width='10' class='line' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='60' stroke-width='10' class='line' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='180' stroke-width='10' class='line' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='180' stroke-width='10' class='line' />
|
||||
|
||||
<line x1='20' y1='-180' x2='208' y2=' 0' stroke-width='10' class='line' />
|
||||
<line x1='20' y1='-60' x2='208' y2=' 0' stroke-width='10' class='line' />
|
||||
<line x1='20' y1='60' x2='208' y2=' 0' stroke-width='10' class='line' />
|
||||
<line x1='20' y1='180' x2='208' y2=' 0' stroke-width='10' class='line' />
|
||||
|
||||
<g transform='translate(-168,-92)'>
|
||||
<circle cx='0' cy='0' r='50' class='node-border' />
|
||||
<circle cx='0' cy='0' r='38' class='node' />
|
||||
</g>
|
||||
<g transform='translate(-168,92)'>
|
||||
<circle cx='0' cy='0' r='50' class='node-border' />
|
||||
<circle cx='0' cy='0' r='38' class='node' />
|
||||
</g>
|
||||
|
||||
<g transform='translate(20,-180)'>
|
||||
<circle cx='0' cy='0' r='50' class='node-border' />
|
||||
<circle cx='0' cy='0' r='38' class='node' />
|
||||
</g>
|
||||
<g transform='translate(20,-60)'>
|
||||
<circle cx='0' cy='0' r='50' class='node-border' />
|
||||
<circle cx='0' cy='0' r='38' class='node' />
|
||||
</g>
|
||||
<g transform='translate(20,60)'>
|
||||
<circle cx='0' cy='0' r='50' class='node-border' />
|
||||
<circle cx='0' cy='0' r='38' class='node' />
|
||||
</g>
|
||||
<g transform='translate(20,180)'>
|
||||
<circle cx='0' cy='0' r='50' class='node-border' />
|
||||
<circle cx='0' cy='0' r='38' class='node' />
|
||||
</g>
|
||||
|
||||
<g transform='translate(208,0)'>
|
||||
<circle cx='0' cy='0' r='50' class='node-border' />
|
||||
<circle cx='0' cy='0' r='38' class='node' />
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 151 KiB |
|
After Width: | Height: | Size: 159 KiB |
@@ -0,0 +1,179 @@
|
||||
<svg xmlns='http://www.w3.org/2000/svg' width='1024' height='1024' viewBox='0 0 1024 1024'>
|
||||
|
||||
<style>
|
||||
.background-shadow {
|
||||
filter: url(#background-dropshadow);
|
||||
}
|
||||
.node-border {
|
||||
fill: #eeeeee;
|
||||
opacity: 1.0;
|
||||
}
|
||||
.node {
|
||||
fill: #307295;
|
||||
opacity: 1;
|
||||
}
|
||||
.node-shadow {
|
||||
filter: url(#node-dropshadow);
|
||||
opacity: 0.3;
|
||||
}
|
||||
.line-border {
|
||||
stroke: #000000;
|
||||
opacity: 0.2;
|
||||
}
|
||||
.line {
|
||||
stroke: #eeeeee;
|
||||
opacity: 1.0;
|
||||
}
|
||||
.line-shadow {
|
||||
stroke: #000000;
|
||||
filter: url(#node-dropshadow);
|
||||
opacity: 0.2;
|
||||
}
|
||||
</style>
|
||||
|
||||
<filter id='background-dropshadow'>
|
||||
<feGaussianBlur in='SourceAlpha' stdDeviation='20' />
|
||||
</filter>
|
||||
|
||||
<linearGradient id='background-fill' gradientUnits='userSpaceOnUse' x1='0' y1='62' x2='0' y2='962'>
|
||||
<stop offset='0' stop-color='#fefefe'/>
|
||||
<stop offset='1' stop-color='#cacaca'/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id='circle-stroke' gradientUnits='userSpaceOnUse' x1='0' y1='62' x2='0' y2='962'>
|
||||
<stop offset='0' stop-color='#aaaaaa'/>
|
||||
<stop offset='1' stop-color='#ffffff'/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id='circle-fill' gradientUnits='userSpaceOnUse' x1='-512' y1='-412' x2='-512' y2='412'>
|
||||
<stop offset='0' stop-color='#4baac5' stop-opacity="1" />
|
||||
<stop offset='1' stop-color='#307295' stop-opacity="1" />
|
||||
</linearGradient>
|
||||
|
||||
<filter id='node-dropshadow' x="-30%" y="-30%" width="160%" height="160%">
|
||||
<feGaussianBlur in='SourceGraphic' stdDeviation='15' />
|
||||
</filter>
|
||||
|
||||
<g transform='translate(512,512) scale(1.06)'>
|
||||
|
||||
<circle cx='0' cy='0' r='438' class='background-shadow' />
|
||||
<circle cx='0' cy='0' r='456' fill='url(#background-fill)'/>
|
||||
<circle cx='0' cy='0' r='417' stroke-width='4' stroke='url(#circle-stroke)' fill='url(#circle-fill)' />
|
||||
|
||||
<g transform='scale(1.2)'>
|
||||
|
||||
<g transform='translate(6,12)' opacity='1.0'>
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='-180' stroke-width='24' class='line-shadow' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='-180' stroke-width='24' class='line-shadow' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='-60' stroke-width='24' class='line-shadow' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='-60' stroke-width='24' class='line-shadow' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='60' stroke-width='24' class='line-shadow' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='60' stroke-width='24' class='line-shadow' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='180' stroke-width='24' class='line-shadow' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='180' stroke-width='24' class='line-shadow' />
|
||||
|
||||
<line x1='20' y1='-180' x2='208' y2='0' stroke-width='24' class='line-shadow' />
|
||||
<line x1='20' y1='-60' x2='208' y2='0' stroke-width='24' class='line-shadow' />
|
||||
<line x1='20' y1='60' x2='208' y2='0' stroke-width='24' class='line-shadow' />
|
||||
<line x1='20' y1='180' x2='208' y2='0' stroke-width='24' class='line-shadow' />
|
||||
|
||||
<g transform='translate(-168,-92)'>
|
||||
<circle cx='0' cy='0' r='40' class='node-shadow' />
|
||||
</g>
|
||||
<g transform='translate(-168,92)'>
|
||||
<circle cx='0' cy='0' r='40' class='node-shadow' />
|
||||
</g>
|
||||
|
||||
<g transform='translate(20,-180)'>
|
||||
<circle cx='0' cy='0' r='40' class='node-shadow' />
|
||||
</g>
|
||||
<g transform='translate(20,-60)'>
|
||||
<circle cx='0' cy='0' r='40' class='node-shadow' />
|
||||
</g>
|
||||
<g transform='translate(20,60)'>
|
||||
<circle cx='0' cy='0' r='40' class='node-shadow' />
|
||||
</g>
|
||||
<g transform='translate(20,180)'>
|
||||
<circle cx='0' cy='0' r='40' class='node-shadow' />
|
||||
</g>
|
||||
|
||||
<g transform='translate(208,0)'>
|
||||
<circle cx='0' cy='0' r='40' class='node-shadow' />
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='-180' stroke-width='24' class='line-border' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='-180' stroke-width='24' class='line-border' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='-60' stroke-width='24' class='line-border' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='-60' stroke-width='24' class='line-border' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='60' stroke-width='24' class='line-border' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='60' stroke-width='24' class='line-border' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='180' stroke-width='24' class='line-border' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='180' stroke-width='24' class='line-border' />
|
||||
|
||||
<line x1='20' y1='-180' x2='208' y2='0' stroke-width='24' class='line-border' />
|
||||
<line x1='20' y1='-60' x2='208' y2='0' stroke-width='24' class='line-border' />
|
||||
<line x1='20' y1='+60' x2='208' y2='0' stroke-width='24' class='line-border' />
|
||||
<line x1='20' y1='+180' x2='208' y2='0' stroke-width='24' class='line-border' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='-180' stroke-width='10' class='line' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='-180' stroke-width='10' class='line' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='-60' stroke-width='10' class='line' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='-60' stroke-width='10' class='line' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='60' stroke-width='10' class='line' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='60' stroke-width='10' class='line' />
|
||||
|
||||
<line x1='-168' y1='-92' x2='20' y2='180' stroke-width='10' class='line' />
|
||||
<line x1='-168' y1='+92' x2='20' y2='180' stroke-width='10' class='line' />
|
||||
|
||||
<line x1='20' y1='-180' x2='208' y2=' 0' stroke-width='10' class='line' />
|
||||
<line x1='20' y1='-60' x2='208' y2=' 0' stroke-width='10' class='line' />
|
||||
<line x1='20' y1='60' x2='208' y2=' 0' stroke-width='10' class='line' />
|
||||
<line x1='20' y1='180' x2='208' y2=' 0' stroke-width='10' class='line' />
|
||||
|
||||
<g transform='translate(-168,-92)'>
|
||||
<circle cx='0' cy='0' r='50' class='node-border' />
|
||||
<circle cx='0' cy='0' r='38' class='node' />
|
||||
</g>
|
||||
<g transform='translate(-168,92)'>
|
||||
<circle cx='0' cy='0' r='50' class='node-border' />
|
||||
<circle cx='0' cy='0' r='38' class='node' />
|
||||
</g>
|
||||
|
||||
<g transform='translate(20,-180)'>
|
||||
<circle cx='0' cy='0' r='50' class='node-border' />
|
||||
<circle cx='0' cy='0' r='38' class='node' />
|
||||
</g>
|
||||
<g transform='translate(20,-60)'>
|
||||
<circle cx='0' cy='0' r='50' class='node-border' />
|
||||
<circle cx='0' cy='0' r='38' class='node' />
|
||||
</g>
|
||||
<g transform='translate(20,60)'>
|
||||
<circle cx='0' cy='0' r='50' class='node-border' />
|
||||
<circle cx='0' cy='0' r='38' class='node' />
|
||||
</g>
|
||||
<g transform='translate(20,180)'>
|
||||
<circle cx='0' cy='0' r='50' class='node-border' />
|
||||
<circle cx='0' cy='0' r='38' class='node' />
|
||||
</g>
|
||||
|
||||
<g transform='translate(208,0)'>
|
||||
<circle cx='0' cy='0' r='50' class='node-border' />
|
||||
<circle cx='0' cy='0' r='38' class='node' />
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.7 KiB |
@@ -0,0 +1,67 @@
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=42", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "netron"
|
||||
version = "0.0.0"
|
||||
description = "Viewer for neural network, deep learning and machine learning models."
|
||||
authors = [
|
||||
{ name = "Lutz Roeder", email = "lutzroeder@users.noreply.github.com" }
|
||||
]
|
||||
keywords = [
|
||||
"onnx", "keras", "tensorflow", "tflite", "coreml", "mxnet", "caffe", "caffe2",
|
||||
"torchscript", "pytorch", "ncnn", "mnn", "openvino", "darknet", "paddlepaddle", "chainer",
|
||||
"artificial intelligence", "machine learning", "deep learning", "neural network",
|
||||
"visualizer", "viewer"
|
||||
]
|
||||
classifiers = [
|
||||
"Intended Audience :: Developers",
|
||||
"Intended Audience :: Education",
|
||||
"Intended Audience :: Science/Research",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Topic :: Software Development",
|
||||
"Topic :: Software Development :: Libraries",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"Topic :: Scientific/Engineering",
|
||||
"Topic :: Scientific/Engineering :: Mathematics",
|
||||
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
||||
"Topic :: Scientific/Engineering :: Visualization"
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
homepage = "https://github.com/lutzroeder/netron"
|
||||
|
||||
[project.readme]
|
||||
text = """
|
||||
Netron is a viewer for neural network, deep learning and machine learning models.
|
||||
|
||||
Netron supports ONNX, TensorFlow Lite, PyTorch, torch.export, ExecuTorch, TorchScript, TensorFlow, Core ML, OpenVINO, Keras, Caffe, Darknet, Safetensors and NumPy.
|
||||
|
||||
Netron has experimental support for MLIR, JAX, GGUF, RKNN, ncnn, MNN, PaddlePaddle and scikit-learn.
|
||||
"""
|
||||
content-type = "text/markdown"
|
||||
|
||||
[project.license]
|
||||
text = "MIT"
|
||||
|
||||
[project.scripts]
|
||||
netron = "netron:main"
|
||||
|
||||
[tool.setuptools]
|
||||
package-dir = { "netron" = "netron" }
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
namespaces = false
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
netron = ["*.*"]
|
||||
|
||||
[tool.setuptools.exclude-package-data]
|
||||
netron = ["app.js", "electron.*"]
|
||||
|
||||
[tool.ruff]
|
||||
lint.select = ["B", "E", "F", "I", "UP", "W", "Q"]
|
||||
cache-dir = "./dist/lint/.ruff_cache"
|
||||
@@ -0,0 +1,47 @@
|
||||
""" Python Server entry point """
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from .server import __version__, serve, start, status, stop, wait, widget
|
||||
|
||||
__all__ = ["start", "stop", "status", "wait", "serve", "widget", "__version__"]
|
||||
|
||||
def main():
|
||||
""" main entry point """
|
||||
parser = argparse.ArgumentParser(description=
|
||||
"Viewer for neural network, deep learning and machine learning models.")
|
||||
parser.add_argument("file",
|
||||
metavar="MODEL_FILE", help="model file to serve", nargs="?", default=None)
|
||||
parser.add_argument("-b", "--browse",
|
||||
help="launch web browser", action="store_true")
|
||||
parser.add_argument("-p", "--port", help="port to serve", type=int)
|
||||
parser.add_argument("--host",
|
||||
metavar="ADDR", help="host to serve", default="localhost")
|
||||
parser.add_argument("--verbosity",
|
||||
metavar="LEVEL", help="log verbosity (quiet, default, debug)",
|
||||
choices=[ "quiet", "debug", "default" ], default="default")
|
||||
parser.add_argument("--version", help="print version", action="store_true")
|
||||
args = parser.parse_args()
|
||||
levels = {
|
||||
"quiet": logging.CRITICAL,
|
||||
"default": logging.INFO,
|
||||
"debug": logging.DEBUG,
|
||||
}
|
||||
logging.basicConfig(level=levels[args.verbosity], format="%(message)s")
|
||||
logger = logging.getLogger(__name__)
|
||||
if args.file and not os.path.exists(args.file):
|
||||
logger.error(f"Model file '{args.file}' does not exist.")
|
||||
sys.exit(2)
|
||||
if args.version:
|
||||
logger.info(__version__)
|
||||
sys.exit(0)
|
||||
address = (args.host, args.port) if args.host else args.port if args.port else None
|
||||
start(args.file, address=address, browse=args.browse)
|
||||
wait()
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,699 @@
|
||||
|
||||
const acuity = {};
|
||||
|
||||
acuity.ModelFactory = class {
|
||||
|
||||
async match(context) {
|
||||
const obj = await context.peek('json');
|
||||
if (obj && obj.MetaData && obj.Layers && Object.keys(obj).length < 256) {
|
||||
return context.set('acuity', obj);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async open(context) {
|
||||
const metadata = await context.metadata('acuity-metadata.json');
|
||||
return new acuity.Model(metadata, context.value);
|
||||
}
|
||||
};
|
||||
|
||||
acuity.Model = class {
|
||||
|
||||
constructor(metadata, model, data, quantization) {
|
||||
this.name = model.MetaData.Name;
|
||||
this.format = `Acuity${model.MetaData && model.MetaData.AcuityVersion ? ` v${model.MetaData.AcuityVersion}` : ''}`;
|
||||
this.runtime = model.MetaData.Platform;
|
||||
this.modules = [new acuity.Graph(metadata, model, data, quantization)];
|
||||
}
|
||||
};
|
||||
|
||||
acuity.Graph = class {
|
||||
|
||||
constructor(metadata, model) {
|
||||
this.nodes = [];
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.metrics = [];
|
||||
const values = new Map();
|
||||
const value = (name) => {
|
||||
if (!values.has(name)) {
|
||||
values.set(name, { name, shape: null });
|
||||
}
|
||||
return values.get(name);
|
||||
};
|
||||
let totalFlops = 0;
|
||||
for (const [name, layer] of Object.entries(model.Layers)) {
|
||||
layer.inputs = layer.inputs.map((input) => {
|
||||
return value(input);
|
||||
});
|
||||
layer.outputs = layer.outputs.map((port) => {
|
||||
const output = value(`@${name}:${port}`);
|
||||
let shape = null;
|
||||
if (layer.op.toLowerCase() === 'input' ||
|
||||
layer.op.toLowerCase() === 'variable') {
|
||||
if (Object.prototype.hasOwnProperty.call(layer.parameters, 'shape') && layer.parameters.shape.length > 0) {
|
||||
shape = layer.parameters.shape;
|
||||
} else if (Object.prototype.hasOwnProperty.call(layer.parameters, 'size') && Object.prototype.hasOwnProperty.call(layer.parameters, 'channels')) {
|
||||
const sizes = layer.parameters.size.split(' ');
|
||||
shape = [0, parseInt(sizes[0], 10), parseInt(sizes[1], 10), layer.parameters.channels];
|
||||
} else if (Object.prototype.hasOwnProperty.call(layer.parameters, 'is_scalar')) {
|
||||
shape = [1];
|
||||
}
|
||||
if (shape && shape.length === 4 && shape[0] === 0) {
|
||||
shape[0] = 1;
|
||||
}
|
||||
}
|
||||
output.shape = shape;
|
||||
return output;
|
||||
});
|
||||
// Add other layer types (e.g., pooling, batch norm, etc.) as needed.
|
||||
if (layer.type === 'Conv2D') {
|
||||
const { kernelShape, inputShape, outputShape } = layer;
|
||||
const [kH, kW] = kernelShape;
|
||||
const [inC] = inputShape;
|
||||
const [outC, oH, oW] = outputShape;
|
||||
totalFlops += kH * kW * inC * oH * oW * outC;
|
||||
} else if (layer.type === 'Dense') {
|
||||
const { inputSize, outputSize } = layer;
|
||||
totalFlops += inputSize * outputSize;
|
||||
}
|
||||
}
|
||||
this.metrics.push(new acuity.Argument('flops', totalFlops));
|
||||
acuity.Inference.infer(model.Layers);
|
||||
for (const [name, obj] of values) {
|
||||
const type = new acuity.TensorType(null, new acuity.TensorShape(obj.shape));
|
||||
const value = new acuity.Value(name, type, null, null);
|
||||
values.set(name, value);
|
||||
}
|
||||
for (const [name, layer] of Object.entries(model.Layers)) {
|
||||
switch (layer.op.toLowerCase()) {
|
||||
case 'input': {
|
||||
const value = values.get(layer.outputs[0].name);
|
||||
const argument = new acuity.Argument(name, [value]);
|
||||
this.inputs.push(argument);
|
||||
break;
|
||||
}
|
||||
case 'output': {
|
||||
const value = values.get(layer.inputs[0].name);
|
||||
const argument = new acuity.Argument(name, [value]);
|
||||
this.outputs.push(argument);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const node = new acuity.Node(metadata, name, layer, values);
|
||||
this.nodes.push(node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
acuity.Node = class {
|
||||
|
||||
constructor(metadata, name, layer, values) {
|
||||
const op = layer.op;
|
||||
this.name = name;
|
||||
this.type = metadata.type(op) || { name: op };
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.attributes = [];
|
||||
if (this.type) {
|
||||
if (layer.parameters) {
|
||||
for (const [name, value] of Object.entries(layer.parameters)) {
|
||||
const meta = metadata.attribute(op, name);
|
||||
const type = meta && meta.type ? meta.type : null;
|
||||
const visible = meta && meta.default !== undefined && meta.default === value ? false : true;
|
||||
const attribute = new acuity.Argument(name, value, type, visible);
|
||||
this.attributes.push(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < layer.inputs.length; i++) {
|
||||
const input = layer.inputs[i];
|
||||
const value = values.get(input.name);
|
||||
const name = this.type && this.type.inputs && i < this.type.inputs.length ? this.type.inputs[i].name : `input${i}`;
|
||||
const argument = new acuity.Argument(name, [value]);
|
||||
this.inputs.push(argument);
|
||||
}
|
||||
|
||||
if (this.type && this.type.constants) {
|
||||
for (const constant of this.type.constants) {
|
||||
// const name = "@" + this.name + ":" + constant.name;
|
||||
const type = new acuity.TensorType(null, new acuity.TensorShape(null));
|
||||
const value = new acuity.Value('', type, null, new acuity.Tensor(type));
|
||||
const argument = new acuity.Argument(constant.name, [value]);
|
||||
this.inputs.push(argument);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < layer.outputs.length; i++) {
|
||||
const output = layer.outputs[i];
|
||||
const value = values.get(output.name);
|
||||
const name = this.type && this.type.outputs && i < this.type.outputs.length ? this.type.outputs[i].name : `output${i}`;
|
||||
const argument = new acuity.Argument(name, [value]);
|
||||
this.outputs.push(argument);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
acuity.Argument = class {
|
||||
|
||||
constructor(name, value, type = null, visible = true) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
this.visible = visible;
|
||||
}
|
||||
};
|
||||
|
||||
acuity.Value = class {
|
||||
|
||||
constructor(name, type = null, quantization = null, initializer = null) {
|
||||
if (typeof name !== 'string') {
|
||||
throw new acuity.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
|
||||
}
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.quantization = quantization;
|
||||
this.initializer = initializer;
|
||||
}
|
||||
};
|
||||
|
||||
acuity.TensorType = class {
|
||||
|
||||
constructor(dataType, shape) {
|
||||
this.dataType = dataType || '?';
|
||||
this.shape = shape;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return (this.dataType || '?') + this.shape.toString();
|
||||
}
|
||||
};
|
||||
|
||||
acuity.TensorShape = class {
|
||||
|
||||
constructor(dimensions) {
|
||||
this.dimensions = Array.isArray(dimensions) && dimensions.length === 1 && dimensions[0] === 0 ? [] : dimensions;
|
||||
}
|
||||
|
||||
toString() {
|
||||
if (!Array.isArray(this.dimensions) || this.dimensions.length === 0 || (this.dimensions.length === 1 && this.dimensions[0] === 0)) {
|
||||
return '';
|
||||
}
|
||||
return `[${this.dimensions.map((dimension) => dimension ? dimension.toString() : '?').join(',')}]`;
|
||||
}
|
||||
};
|
||||
|
||||
acuity.Tensor = class {
|
||||
|
||||
constructor(type) {
|
||||
this.type = type;
|
||||
this.Category = 'Constant';
|
||||
}
|
||||
};
|
||||
|
||||
acuity.Inference = class {
|
||||
|
||||
static infer(layers) {
|
||||
const outputs = new Map();
|
||||
const outputLayers = [];
|
||||
for (const [, layer] of Object.entries(layers)) {
|
||||
if (layer.op.toLowerCase() === 'output') {
|
||||
outputLayers.push(layer);
|
||||
}
|
||||
for (const output of layer.outputs) {
|
||||
outputs.set(output.name, layer);
|
||||
}
|
||||
}
|
||||
const broadcasts = new Set([
|
||||
'add', 'equal', 'fllor_mod', 'floor_div', 'greater', 'greater_equal', 'less', 'less_equal',
|
||||
'logical_and', 'logical_or', 'minimum', 'multiply', 'not_equal', 'pow', 'real_div',
|
||||
'squared_difference', 'subtract', 'divide', 'addn', 'Divide', 'bitwise_and', 'bitwise_or',
|
||||
'bitwise_xor', 'average', 'logical_not', 'logical_xor'
|
||||
]);
|
||||
const passthroughs = new Set([
|
||||
'LocalResponseNormalization', 'a_times_b_plus_c', 'abs', 'batchnorm_single', 'batchnormalize',
|
||||
'cast', 'cast', 'clipbyvalue', 'dequantize', 'dtype_converter', 'elu', 'exp', 'floor',
|
||||
'groupnormalize', 'hard_sigmoid', 'hard_swish', 'instancenormalize', 'l2normalize', 'l2normalizescale',
|
||||
'layernormalize', 'leakyrelu', 'log', 'log_softmax', 'mish', 'neg', 'norm_with_channel_mean',
|
||||
'norm_with_min_max', 'norm_with_scale', 'pow', 'prelu', 'quantize', 'relu', 'relu_keras',
|
||||
'relun', 'reverse', 'round', 'rsqrt', 'sigmoid', 'sin', 'softmax', 'softrelu', 'sqrt', 'square', 'tanh',
|
||||
'swish', 'gelu', 'dropout', 'eltwise', 'cos', 'l1_layernormalize', 'inverse_sigmoid', 'selu', 'mod',
|
||||
'mish', 'minimum_with_clip', 'celu', 'cumsum', 'dft', 'dropout2', 'erf', 'noop', 'squashing', 'tan', 'ceil',
|
||||
'atan', 'atan2', 'atanh', 'alpha_dropout', 'acosh', 'rmsnormalize', 'sign'
|
||||
]);
|
||||
const reduces = new Set([
|
||||
'reduceany', 'reducemax', 'reducemean', 'reducemin', 'reduceprod', 'reducesum'
|
||||
]);
|
||||
const poolings = new Set([
|
||||
'pooling', 'l2pooling'
|
||||
]);
|
||||
const operators = new Map();
|
||||
operators.set('broadcast', ([a, b]) => {
|
||||
const longer = a.length >= b.length ? a.slice() : b.slice();
|
||||
const shorter = a.length < b.length ? a.slice() : b.slice();
|
||||
const remain = longer.length - shorter.length;
|
||||
for (let i = 0; i < remain; i++) {
|
||||
shorter.splice(0, 0, 1);
|
||||
}
|
||||
for (let i = 0; i < longer.length; i++) {
|
||||
longer[i] = longer[i] > shorter[i] ? longer[i] : shorter[i];
|
||||
}
|
||||
return [longer];
|
||||
});
|
||||
operators.set('concat', (inputs, params) => {
|
||||
const outputShape = inputs[0].slice();
|
||||
outputShape[params.dim] = 0;
|
||||
for (const shape of inputs) {
|
||||
outputShape[params.dim] += shape[params.dim];
|
||||
}
|
||||
return [outputShape];
|
||||
});
|
||||
operators.set('conv1d', (inputs, params) => {
|
||||
if (params.padding === 'VALID') {
|
||||
const out_h = ~~((inputs[0][1] + params.stride - params.ksize) / params.stride);
|
||||
return [[inputs[0][0], out_h, params.weights]];
|
||||
} else if (params.padding === 'SAME') {
|
||||
const out_h = ~~((inputs[0][1] + params.stride - 1) / params.stride);
|
||||
return [[inputs[0][0], out_h, params.weights]];
|
||||
}
|
||||
return null;
|
||||
});
|
||||
operators.set('convolution', (inputs, params) => {
|
||||
if (params.padding === 'VALID') {
|
||||
const out_h = Math.floor((inputs[0][1] + params.stride_h + 2 * params.pad_h - params.ksize_h) / params.stride_h);
|
||||
const out_w = Math.floor((inputs[0][2] + params.stride_w + 2 * params.pad_w - params.ksize_w) / params.stride_w);
|
||||
return [[inputs[0][0], out_h, out_w, params.weights]];
|
||||
} else if (params.padding === 'SAME') {
|
||||
const out_h = Math.floor((inputs[0][1] + params.stride_h - 1) / params.stride_h);
|
||||
const out_w = Math.floor((inputs[0][2] + params.stride_w - 1) / params.stride_w);
|
||||
return [[inputs[0][0], out_h, out_w, params.weights]];
|
||||
}
|
||||
return null;
|
||||
});
|
||||
operators.set('depthwise_conv1d', (inputs, params) => {
|
||||
if (params.padding === 'VALID') {
|
||||
const out_h = ~~((inputs[0][1] + params.stride + params.pad[0] + params.pad[1] - params.ksize) / params.stride);
|
||||
return [[inputs[0][0], out_h, inputs[0][2] * params.multiplier]];
|
||||
} else if (params.padding === 'SAME') {
|
||||
const out_h = ~~((inputs[0][1] + params.stride - 1) / params.stride);
|
||||
return [[inputs[0][0], out_h, inputs[0][2] * params.multiplier]];
|
||||
}
|
||||
return null;
|
||||
});
|
||||
operators.set('depthwise_convolution', (inputs, params) => {
|
||||
if (params.padding === 'VALID') {
|
||||
const out_h = ~~((inputs[0][1] + params.stride_h + params.pad[0] + params.pad[1] - params.ksize_h) / params.stride_h);
|
||||
const out_w = ~~((inputs[0][2] + params.stride_w + params.pad[2] + params.pad[3] - params.ksize_w) / params.stride_w);
|
||||
return [[inputs[0][0], out_h, out_w, inputs[0][3] * params.multiplier]];
|
||||
} else if (params.padding === 'SAME') {
|
||||
const out_h = ~~((inputs[0][1] + params.stride_h - 1) / params.stride_h);
|
||||
const out_w = ~~((inputs[0][2] + params.stride_w - 1) / params.stride_w);
|
||||
return [[inputs[0][0], out_h, out_w, inputs[0][3] * params.multiplier]];
|
||||
}
|
||||
return null;
|
||||
});
|
||||
operators.set('deconvolution', (inputs, params) => {
|
||||
return [params.output_shape.map((item, index) => item === 0 ? inputs[0][index] : item)];
|
||||
});
|
||||
operators.set('deconvolution1d', (inputs, params) => {
|
||||
return [params.output_shape.map((item, index) => item === 0 ? inputs[0][index] : item)];
|
||||
});
|
||||
operators.set('fullconnect', (inputs, params) => {
|
||||
return [inputs[0].slice(0, params.axis).concat([params.weights])];
|
||||
});
|
||||
operators.set('gather', (inputs, params) => {
|
||||
const prefix = inputs[1].slice();
|
||||
const suffix = inputs[0].slice(params.axis + 1);
|
||||
return [prefix.concat(suffix)];
|
||||
});
|
||||
operators.set('lstm', (inputs, params) => {
|
||||
const [input] = inputs;
|
||||
const [a, b] = input;
|
||||
let batch = a;
|
||||
const output = params.num_proj === null ? params.weights : params.num_proj;
|
||||
if (params.time_major) {
|
||||
batch = b;
|
||||
}
|
||||
const newShape = params.return_sequences ? [a, b, output] : [batch, output];
|
||||
return [newShape, [batch, output], [batch, params.weights]];
|
||||
});
|
||||
operators.set('matmul', ([a, b], params) => {
|
||||
let newShape = a.slice(0, -2);
|
||||
if (params.transpose_a) {
|
||||
newShape = newShape.concat(a.slice(-1));
|
||||
} else {
|
||||
newShape = newShape.concat(a.slice(-2, -1));
|
||||
}
|
||||
if (params.transpose_b) {
|
||||
newShape = newShape.concat(b.slice(-2, -1));
|
||||
} else {
|
||||
newShape = newShape.concat(b.slice(-1));
|
||||
}
|
||||
return [newShape];
|
||||
});
|
||||
operators.set('pad', (inputs, params) => {
|
||||
return [inputs[0].map((item, index) => item + params.padding_value[index][0] + params.padding_value[index][1])];
|
||||
});
|
||||
operators.set('permute', (inputs, params) => {
|
||||
return [inputs[0].map((item, index) => inputs[0][params.perm[index]])];
|
||||
});
|
||||
operators.set('pooling', (inputs, params) => {
|
||||
if (params.padding === 'VALID') {
|
||||
const out_h = ~~((inputs[0][1] + params.stride_h - params.ksize_h) / params.stride_h);
|
||||
const out_w = ~~((inputs[0][2] + params.stride_w - params.ksize_w) / params.stride_w);
|
||||
return [[inputs[0][0], out_h, out_w, inputs[0][3]]];
|
||||
} else if (params.padding === 'SAME') {
|
||||
const out_h = ~~((inputs[0][1] + params.stride_h - 1) / params.stride_h);
|
||||
const out_w = ~~((inputs[0][2] + params.stride_w - 1) / params.stride_w);
|
||||
return [[inputs[0][0], out_h, out_w, inputs[0][3]]];
|
||||
}
|
||||
return null;
|
||||
});
|
||||
operators.set('reduce', (inputs, params) => {
|
||||
const newShape = inputs[0].slice();
|
||||
const axis_list = params.axis_list.map((item) => {
|
||||
return item < 0 ? newShape.length + item : item;
|
||||
});
|
||||
axis_list.sort((a, b) => {
|
||||
return b - a;
|
||||
});
|
||||
axis_list.forEach((i) => {
|
||||
newShape[i] = 1;
|
||||
});
|
||||
if (!params.keep_dims) {
|
||||
axis_list.forEach((i) => {
|
||||
newShape.splice(i, 1);
|
||||
});
|
||||
if (!newShape.length) {
|
||||
newShape.splice(0, 0, 0);
|
||||
}
|
||||
}
|
||||
return [newShape];
|
||||
});
|
||||
operators.set('repeat', (inputs, params) => {
|
||||
const newShape = inputs[0].slice();
|
||||
newShape[params.axis] = params.maxlen;
|
||||
return [newShape];
|
||||
});
|
||||
operators.set('reshape', (inputs, params) => {
|
||||
const negativeIndexs = [];
|
||||
let shape = params.shape;
|
||||
if (typeof params.shape === 'string') {
|
||||
shape = params.shape.split(/\s+/).map((item) => {
|
||||
return parseInt(item, 10);
|
||||
});
|
||||
}
|
||||
const newShape = shape.map((item, index) => {
|
||||
if (item === 0) {
|
||||
return inputs[0][index];
|
||||
}
|
||||
if (item === -1) {
|
||||
negativeIndexs.push(index);
|
||||
return 1;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
if (negativeIndexs.length > 0) {
|
||||
newShape[negativeIndexs[0]] = inputs[0].reduce((a, c) => a * c) / newShape.reduce((a, c) => a * c);
|
||||
}
|
||||
return [newShape];
|
||||
});
|
||||
operators.set('sequence_mask', (inputs, params) => {
|
||||
return [inputs[0].slice().concat([params.maxlen])];
|
||||
});
|
||||
operators.set('slice', (inputs, params) => {
|
||||
return [params.size.map((item, index) => item === -1 ? inputs[0][index] : item)];
|
||||
});
|
||||
operators.set('squeeze', (inputs, params) => {
|
||||
const newShape = inputs[0].slice();
|
||||
const axis_list = [...new Set(params.axis_list)].sort((a, b) => b - a);
|
||||
for (const item of axis_list) {
|
||||
newShape.splice(item, 1);
|
||||
}
|
||||
return [newShape];
|
||||
});
|
||||
operators.set('space2depth', (inputs, params) => {
|
||||
const h = inputs[0][1] / params.block_size[0];
|
||||
const w = inputs[0][2] / params.block_size[1];
|
||||
const c = inputs[0][3] * params.block_size[1] * params.block_size[1];
|
||||
return [[inputs[0][0], h, w, c]];
|
||||
});
|
||||
operators.set('depth2space', (inputs, params) => {
|
||||
const h = inputs[0][1] * params.block_size;
|
||||
const w = inputs[0][2] * params.block_size;
|
||||
const c = inputs[0][3] / (params.block_size * params.block_size);
|
||||
return [[inputs[0][0], h, w, c]];
|
||||
});
|
||||
operators.set('upsampling', (inputs, params) => {
|
||||
const h = inputs[0][1] * params.factor;
|
||||
const w = inputs[0][2] * params.factor;
|
||||
return [[inputs[0][0], h, w, inputs[0][3]]];
|
||||
});
|
||||
operators.set('crop_image', (inputs, params) => {
|
||||
return [[inputs[0][0], params.crop_size[0], params.crop_size[1], inputs[0][3]]];
|
||||
});
|
||||
operators.set('split', (inputs, params) => {
|
||||
const sizes = [];
|
||||
const slices = params.slices.slice();
|
||||
slices.splice(0, 0, 0);
|
||||
slices.push(inputs[0][params.dim]);
|
||||
slices.reduce((a, b) => {
|
||||
sizes.push(b - a);
|
||||
return b;
|
||||
});
|
||||
return sizes.map((item) => {
|
||||
const shape = inputs[0].slice();
|
||||
shape[params.dim] = item;
|
||||
return shape;
|
||||
});
|
||||
});
|
||||
operators.set('stack', (inputs, params) => {
|
||||
const newShape = inputs[0].slice();
|
||||
if (newShape.length === 1 && newShape[0] === 0) {
|
||||
newShape[0] = 1;
|
||||
} else {
|
||||
newShape.splice(params.axis, 0, inputs.length);
|
||||
}
|
||||
return [newShape];
|
||||
});
|
||||
operators.set('stridedslice', (inputs, params) => {
|
||||
const input_shape = inputs[0].slice();
|
||||
const begin = params.slice_begin.slice();
|
||||
const end = params.slice_end.slice();
|
||||
if (params.slice_begin_mask > 0) {
|
||||
for (let i = 0; i < begin.length; i++) {
|
||||
if ((params.slice_begin_mask >>> i) & 0x1) {
|
||||
begin[i] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (params.slice_end_mask > 0) {
|
||||
for (let i = 0; i < end.length; i++) {
|
||||
if ((params.slice_end_mask >>> i) & 0x1) {
|
||||
end[i] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < begin.length; i++) {
|
||||
if (begin[i] === -1) {
|
||||
begin[i] = 0;
|
||||
}
|
||||
}
|
||||
if (inputs[0].length === end.length) {
|
||||
for (let i = 0; i < end.length; i++) {
|
||||
if (end[i] === -1 || end[i] > input_shape[i]) {
|
||||
end[i] = input_shape[i];
|
||||
}
|
||||
}
|
||||
} else if (inputs[0].length < end.length) {
|
||||
if (params.slice_new_axis_mask) {
|
||||
const len = (params.slice_new_axis_mask >>> 0).toString(2).length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
if ((params.slice_new_axis_mask >>> i) & 0x1) {
|
||||
input_shape.splice(i, 0, 1);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < end.length; i++) {
|
||||
if (end[i] === -1) {
|
||||
end[i] = input_shape[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let newShape = [];
|
||||
for (let i = 0; i < begin.length; i++) {
|
||||
newShape = newShape.concat([(end[i] - begin[i]) / params.slice_strides[i]]);
|
||||
}
|
||||
if (params.slice_shrink_axis_mask) {
|
||||
const len = (params.slice_shrink_axis_mask >>> 0).toString(2).length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
if ((params.slice_shrink_axis_mask >>> i) & 0x1) {
|
||||
newShape.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (params.slice_new_axis_mask) {
|
||||
const len = (params.slice_new_axis_mask >>> 0).toString(2).length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
if ((params.slice_new_axis_mask >>> i) & 0x1) {
|
||||
if (inputs[0].length === begin.length) {
|
||||
newShape.splice(i, 0, 1);
|
||||
} else if (inputs[0].length < begin.length) {
|
||||
newShape[i] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return [newShape];
|
||||
});
|
||||
operators.set('image_resize', (inputs, params) => {
|
||||
const newShape = inputs[0].slice();
|
||||
newShape[1] = params.new_size[0];
|
||||
newShape[2] = params.new_size[1];
|
||||
return [newShape];
|
||||
});
|
||||
operators.set('argmax', (inputs, params) => {
|
||||
const newShape = inputs[0].slice();
|
||||
if (params.keepdims) {
|
||||
newShape[params.axis] = 1;
|
||||
} else {
|
||||
newShape.splice(params.axis, 1);
|
||||
if (!newShape.length) {
|
||||
newShape.splice(0, 0, 0);
|
||||
}
|
||||
}
|
||||
return [newShape];
|
||||
});
|
||||
operators.set('argmin', operators.get('argmax'));
|
||||
/* eslint-disable no-unused-vars */
|
||||
operators.set('shapelayer', (inputs, params) => {
|
||||
return [[inputs[0].length]];
|
||||
});
|
||||
operators.set('capsule_norm', (inputs, params) => {
|
||||
return [[inputs[0][0], inputs[0][inputs[0].length - 1]]];
|
||||
});
|
||||
operators.set('size', (inputs, params) => {
|
||||
return [[1]];
|
||||
});
|
||||
/* eslint-enable no-unused-vars */
|
||||
operators.set('einsum', ((operators, inputs, params) => {
|
||||
const identifyOperation = (inputs, equation) => {
|
||||
const identifyFuncs = new Map();
|
||||
identifyFuncs.set('matmul', (inputs, equation) => {
|
||||
if (inputs.length !== 2) {
|
||||
return { found: false };
|
||||
}
|
||||
|
||||
const parts = equation.replace(/\s+/g, '').split(/,|->/);
|
||||
if (parts.length !== 3) {
|
||||
return { found: false };
|
||||
}
|
||||
|
||||
const [first, second, output] = parts.map((p) => p.split(''));
|
||||
if (!(first.length === output.length || second.length === output.length)) {
|
||||
return { found: false };
|
||||
}
|
||||
|
||||
let a = first.slice(-2);
|
||||
const b = second.slice(-2);
|
||||
const c = output.slice(-2);
|
||||
let transpose_a = false;
|
||||
let transpose_b = false;
|
||||
if (a[0] === c[0]) {
|
||||
transpose_a = false;
|
||||
} else if (a[1] === c[0]) {
|
||||
transpose_a = true;
|
||||
a = [].concat(a.reverse());
|
||||
} else {
|
||||
return { found: false };
|
||||
}
|
||||
|
||||
if (a[1] === b[0]) {
|
||||
transpose_b = false;
|
||||
} else if (a[1] === b[1]) {
|
||||
transpose_b = true;
|
||||
} else {
|
||||
return { found: false };
|
||||
}
|
||||
return { found: true, op: 'matmul', params: { transpose_a, transpose_b } };
|
||||
});
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
for (const [name, func] of identifyFuncs.entries()) {
|
||||
const result = func(inputs, equation);
|
||||
if (result.found) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
/* eslint-enable no-unused-vars */
|
||||
return { found: false };
|
||||
};
|
||||
|
||||
const result = identifyOperation(inputs, params.equation);
|
||||
if (result.found) {
|
||||
if (operators.has(result.op)) {
|
||||
return operators.get(result.op)(inputs, result.params);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}).bind(undefined, operators));
|
||||
const infer = (output) => {
|
||||
if (outputs.has(output.name)) {
|
||||
let ready = true;
|
||||
const layer = outputs.get(output.name);
|
||||
for (const input of layer.inputs) {
|
||||
if (input.shape === null) {
|
||||
infer(input);
|
||||
if (input.shape === null) {
|
||||
ready = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ready) {
|
||||
let callback = null;
|
||||
if (operators.has(layer.op)) {
|
||||
callback = operators.get(layer.op);
|
||||
} else if (passthroughs.has(layer.op)) {
|
||||
callback = (inputs) => [inputs[0].slice()];
|
||||
} else if (broadcasts.has(layer.op)) {
|
||||
callback = operators.get('broadcast');
|
||||
} else if (reduces.has(layer.op)) {
|
||||
callback = operators.get('reduce');
|
||||
} else if (poolings.has(layer.op)) {
|
||||
callback = operators.get('pooling');
|
||||
}
|
||||
if (!callback) {
|
||||
callback = () => [];
|
||||
}
|
||||
const parameters = layer.parameters;
|
||||
const inputs = layer.inputs.map((input) => input.shape);
|
||||
const outputs = callback(inputs, parameters);
|
||||
for (let i = 0; i < outputs.length; i++) {
|
||||
if (i < layer.outputs.length) {
|
||||
layer.outputs[i].shape = outputs[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
for (const layer of outputLayers) {
|
||||
for (const output of layer.outputs) {
|
||||
infer(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
acuity.Error = class extends Error {
|
||||
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'Error loading Acuity model.';
|
||||
}
|
||||
};
|
||||
|
||||
export const ModelFactory = acuity.ModelFactory;
|
||||
@@ -0,0 +1,422 @@
|
||||
[
|
||||
{
|
||||
"name": "ActivationLayer",
|
||||
"category": "Activation",
|
||||
"attributes": [
|
||||
{ "name": "activationFunction", "type": "ActivationFunction" },
|
||||
{ "name": "a", "type": "float32" },
|
||||
{ "name": "b", "type": "float32" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "AdditionLayer",
|
||||
"inputs": [
|
||||
{ "name": "A" },
|
||||
{ "name": "B" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "C" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "BatchNormalizationLayer",
|
||||
"category": "Normalization",
|
||||
"attributes": [
|
||||
{ "name": "eps", "type": "float32" },
|
||||
{ "name": "dataLayout", "type": "DataLayout" }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "mean" },
|
||||
{ "name": "variance" },
|
||||
{ "name": "beta" },
|
||||
{ "name": "gamma" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "BatchToSpaceNdLayer",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "blockShape", "type": "string" },
|
||||
{ "name": "crops", "type": "string" },
|
||||
{ "name": "dataLayout", "type": "DataLayout" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ConcatLayer",
|
||||
"category": "Tensor",
|
||||
"attributes": [
|
||||
{ "name": "concatAxis", "type": "uint32" },
|
||||
{ "name": "numViews", "type": "uint32" },
|
||||
{ "name": "numDimensions", "type": "uint32" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ConstantLayer",
|
||||
"category": "Constant",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Convolution2dLayer",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "padTop", "type": "uint32" },
|
||||
{ "name": "padRight", "type": "uint32" },
|
||||
{ "name": "padBottom", "type": "uint32" },
|
||||
{ "name": "padLeft", "type": "uint32" },
|
||||
{ "name": "strideX", "type": "uint32" },
|
||||
{ "name": "strideY", "type": "uint32" },
|
||||
{ "name": "dilationX", "type": "uint32" },
|
||||
{ "name": "dilationY", "type": "uint32" },
|
||||
{ "name": "dataLayout", "type": "DataLayout" }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "weights" },
|
||||
{ "name": "biases" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "DepthwiseConvolution2dLayer",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "padTop", "type": "uint32" },
|
||||
{ "name": "padRight", "type": "uint32" },
|
||||
{ "name": "padBottom", "type": "uint32" },
|
||||
{ "name": "padLeft", "type": "uint32" },
|
||||
{ "name": "strideX", "type": "uint32" },
|
||||
{ "name": "strideY", "type": "uint32" },
|
||||
{ "name": "dilationX", "type": "uint32" },
|
||||
{ "name": "dilationY", "type": "uint32" },
|
||||
{ "name": "dataLayout", "type": "DataLayout" }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "weights" },
|
||||
{ "name": "biases" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "DequantizeLayer",
|
||||
"category": "Quantization"
|
||||
},
|
||||
{
|
||||
"name": "DetectionPostProcessLayer",
|
||||
"attributes": [
|
||||
{ "name": "maxDetections", "type": "uint32" },
|
||||
{ "name": "maxClassesPerDetection", "type": "uint32" },
|
||||
{ "name": "detectionsPerClass", "type": "uint32" },
|
||||
{ "name": "nmsScoreThreshold", "type": "float32" },
|
||||
{ "name": "numIouThreshold", "type": "float32" },
|
||||
{ "name": "numClasses", "type": "uint32" },
|
||||
{ "name": "useRegularNms", "type": "boolean" },
|
||||
{ "name": "scaleX", "type": "float32" },
|
||||
{ "name": "scaleY", "type": "float32" },
|
||||
{ "name": "scaleW", "type": "float32" },
|
||||
{ "name": "scaleH", "type": "float32" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "DivisionLayer",
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "EqualLayer",
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "FloorLayer",
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "FullyConnectedLayer",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "transposeWeightsMatrix", "type": "boolean" }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "weights" },
|
||||
{ "name": "biases" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "GatherLayer",
|
||||
"category": "Tensor"
|
||||
},
|
||||
{
|
||||
"name": "GreaterLayer",
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "InputLayer"
|
||||
},
|
||||
{
|
||||
"name": "L2NormalizationLayer",
|
||||
"category": "Normalization",
|
||||
"attributes": [
|
||||
{ "name": "eps", "type": "float32" },
|
||||
{ "name": "dataLayout", "type": "DataLayout" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "LstmLayer",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "activationFunc", "type": "uint32" },
|
||||
{ "name": "clippingThresCell", "type": "float32" },
|
||||
{ "name": "clippingThresProj", "type": "float32" },
|
||||
{ "name": "cifgEnabled", "type": "boolean" },
|
||||
{ "name": "peepholeEnabled", "type": "boolean" },
|
||||
{ "name": "projectionEnabled", "type": "boolean" },
|
||||
{ "name": "layerNormEnabled", "type": "boolean" }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "inputToForgetWeights1" },
|
||||
{ "name": "inputToCellWeights1" },
|
||||
{ "name": "inputToOutputWeights1" },
|
||||
{ "name": "recurrentToForgetWeights1" },
|
||||
{ "name": "recurrentToCellWeights1" },
|
||||
{ "name": "recurrentToOutputWeights1" },
|
||||
{ "name": "forgetGateBias1" },
|
||||
{ "name": "cellBias1" },
|
||||
{ "name": "outputGateBias1" },
|
||||
{ "name": "inputToInputWeights1" },
|
||||
{ "name": "recurrentToInputWeights1" },
|
||||
{ "name": "cellToInputWeights1" },
|
||||
{ "name": "inputGateBias1" },
|
||||
{ "name": "projectionWeights1" },
|
||||
{ "name": "projectionBias1" },
|
||||
{ "name": "cellToForgetWeights1" },
|
||||
{ "name": "cellToOutputWeights1" },
|
||||
{ "name": "inputLayerNormWeights1" },
|
||||
{ "name": "forgetLayerNormWeights1" },
|
||||
{ "name": "cellLayerNormWeights1" },
|
||||
{ "name": "outputLayerNormWeights1" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MaximumLayer",
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "MeanLayer",
|
||||
"attributes": [
|
||||
{ "name": "axis", "type": "uint32" },
|
||||
{ "name": "keepDims", "type": "boolean" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MergeLayer",
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "MergerLayer",
|
||||
"category": "Tensor"
|
||||
},
|
||||
{
|
||||
"name": "MinimumLayer",
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "MultiplicationLayer",
|
||||
"inputs": [
|
||||
{ "name": "A" },
|
||||
{ "name": "B" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "C" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "NormalizationLayer",
|
||||
"category": "Normalization",
|
||||
"attributes": [
|
||||
{ "name": "normChannelType", "type": "NormalizationAlgorithmChannel" },
|
||||
{ "name": "normMethodType", "type": "NormalizationAlgorithmMethod" },
|
||||
{ "name": "normSize", "type": "uint32" },
|
||||
{ "name": "alpha", "type": "float32" },
|
||||
{ "name": "beta", "type": "float32" },
|
||||
{ "name": "dataLayout", "type": "DataLayout" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OutputLayer",
|
||||
"category": "Tensor"
|
||||
},
|
||||
{
|
||||
"name": "PadLayer",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "padList", "type": "uint32" },
|
||||
{ "name": "padValue", "type": "float32" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "PermuteLayer",
|
||||
"category": "Shape",
|
||||
"attributes": [
|
||||
{ "name": "dimMappings", "type": "string" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Pooling2dLayer",
|
||||
"category": "Pool",
|
||||
"attributes": [
|
||||
{ "name": "poolType", "type": "PoolingAlgorithm" },
|
||||
{ "name": "padTop", "type": "uint32" },
|
||||
{ "name": "padRight", "type": "uint32" },
|
||||
{ "name": "padBottom", "type": "uint32" },
|
||||
{ "name": "padLeft", "type": "uint32" },
|
||||
{ "name": "poolWidth", "type": "uint32" },
|
||||
{ "name": "poolHeight", "type": "uint32" },
|
||||
{ "name": "strideX", "type": "uint32" },
|
||||
{ "name": "strideY", "type": "uint32" },
|
||||
{ "name": "outputShapeRounding", "type": "OutputShapeRounding" },
|
||||
{ "name": "paddingMethod", "type": "PaddingMethod" },
|
||||
{ "name": "dataLayout", "type": "DataLayout" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "PreluLayer",
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "QuantizedLstmLayer",
|
||||
"category": "Layer",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "inputToInputWeights1" },
|
||||
{ "name": "inputToForgetWeights1" },
|
||||
{ "name": "inputToCellWeights1" },
|
||||
{ "name": "inputToOutputWeights1" },
|
||||
{ "name": "recurrentToInputWeights1" },
|
||||
{ "name": "recurrentToForgetWeights1" },
|
||||
{ "name": "recurrentToCellWeights1" },
|
||||
{ "name": "recurrentToOutputWeights1" },
|
||||
{ "name": "inputGateBias1" },
|
||||
{ "name": "forgetGateBias1" },
|
||||
{ "name": "cellBias1" },
|
||||
{ "name": "outputGateBias1" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "QuantizeLayer",
|
||||
"category": "Quantization"
|
||||
},
|
||||
{
|
||||
"name": "ReshapeLayer",
|
||||
"category": "Shape",
|
||||
"attributes": [
|
||||
{ "name": "targetShape", "type": "uint32[]" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ResizeBilinearLayer",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "targetWidth", "type": "uint32" },
|
||||
{ "name": "targetHeight", "type": "uint32" },
|
||||
{ "name": "dataLayout", "type": "DataLayout" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ResizeLayer",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "targetWidth", "type": "uint32" },
|
||||
{ "name": "targetHeight", "type": "uint32" },
|
||||
{ "name": "method", "type": "ResizeMethod" },
|
||||
{ "name": "dataLayout", "type": "DataLayout" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "RsqrtLayer",
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "SoftmaxLayer",
|
||||
"category": "Activation",
|
||||
"attributes": [
|
||||
{ "name": "beta", "type": "float32" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SpaceToBatchNdLayer",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "blockShape", "type": "string" },
|
||||
{ "name": "padList", "type": "string" },
|
||||
{ "name": "dataLayout", "type": "DataLayout" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SpaceToDepthLayer",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "blockSize", "type": "uint32" },
|
||||
{ "name": "dataLayout", "type": "DataLayout" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SplitterLayer",
|
||||
"category": "Tensor",
|
||||
"attributes": [
|
||||
{ "name": "concatAxis", "type": "uint32" },
|
||||
{ "name": "numViews", "type": "uint32" },
|
||||
{ "name": "numDimensions", "type": "uint32" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "StackLayer",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "axis", "type": "uint32" },
|
||||
{ "name": "numInputs", "type": "uint32" },
|
||||
{ "name": "inputShape", "type": "uint32" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "StridedSliceLayer",
|
||||
"category": "Tensor",
|
||||
"attributes": [
|
||||
{ "name": "begin", "type": "int32" },
|
||||
{ "name": "end", "type": "int32" },
|
||||
{ "name": "stride", "type": "int32" },
|
||||
{ "name": "beginMask", "type": "int32" },
|
||||
{ "name": "endMask", "type": "int32" },
|
||||
{ "name": "shrinkAxisMask", "type": "int32" },
|
||||
{ "name": "ellipsisMask", "type": "int32" },
|
||||
{ "name": "newAxisMask", "type": "int32" },
|
||||
{ "name": "dataLayout", "type": "DataLayout" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SubtractionLayer"
|
||||
},
|
||||
{
|
||||
"name": "SwitchLayer",
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "TransposeConvolution2dLayer",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "padTop", "type": "uint32" },
|
||||
{ "name": "padRight", "type": "uint32" },
|
||||
{ "name": "padBottom", "type": "uint32" },
|
||||
{ "name": "padLeft", "type": "uint32" },
|
||||
{ "name": "strideX", "type": "uint32" },
|
||||
{ "name": "strideY", "type": "uint32" },
|
||||
{ "name": "dataLayout", "type": "DataLayout" }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "weights" },
|
||||
{ "name": "biases" }
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,294 @@
|
||||
|
||||
const armnn = {};
|
||||
|
||||
armnn.ModelFactory = class {
|
||||
|
||||
async match(context) {
|
||||
const identifier = context.identifier;
|
||||
const extension = identifier.lastIndexOf('.') > 0 ? identifier.split('.').pop().toLowerCase() : '';
|
||||
if (extension === 'armnn') {
|
||||
const reader = await context.peek('flatbuffers.binary');
|
||||
if (reader) {
|
||||
return context.set('armnn.flatbuffers', reader);
|
||||
}
|
||||
}
|
||||
if (extension === 'json') {
|
||||
const obj = await context.peek('json');
|
||||
if (obj && obj.layers && obj.inputIds && obj.outputIds) {
|
||||
return context.set('armnn.flatbuffers.json', obj);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async open(context) {
|
||||
armnn.schema = await context.require('./armnn-schema');
|
||||
armnn.schema = armnn.schema.armnnSerializer;
|
||||
let model = null;
|
||||
switch (context.type) {
|
||||
case 'armnn.flatbuffers': {
|
||||
try {
|
||||
const reader = await context.read('flatbuffers.binary');
|
||||
model = armnn.schema.SerializedGraph.create(reader);
|
||||
} catch (error) {
|
||||
const message = error && error.message ? error.message : error.toString();
|
||||
throw new armnn.Error(`File format is not armnn.SerializedGraph (${message.replace(/\.$/, '')}).`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'armnn.flatbuffers.json': {
|
||||
try {
|
||||
const reader = await context.read('flatbuffers.text');
|
||||
model = armnn.schema.SerializedGraph.createText(reader);
|
||||
} catch (error) {
|
||||
const message = error && error.message ? error.message : error.toString();
|
||||
throw new armnn.Error(`File text format is not armnn.SerializedGraph (${message.replace(/\.$/, '')}).`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new armnn.Error(`Unsupported Arm NN format '${context.type}'.`);
|
||||
}
|
||||
}
|
||||
const metadata = await context.metadata('armnn-metadata.json');
|
||||
return new armnn.Model(metadata, model);
|
||||
}
|
||||
};
|
||||
|
||||
armnn.Model = class {
|
||||
|
||||
constructor(metadata, model) {
|
||||
this.format = 'Arm NN';
|
||||
this.modules = [new armnn.Graph(metadata, model)];
|
||||
}
|
||||
};
|
||||
|
||||
armnn.Graph = class {
|
||||
|
||||
constructor(metadata, graph) {
|
||||
this.name = '';
|
||||
this.nodes = [];
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
const counts = new Map();
|
||||
for (const layer of graph.layers) {
|
||||
const base = armnn.Node.getBase(layer);
|
||||
for (const slot of base.inputSlots) {
|
||||
const name = `${slot.connection.sourceLayerIndex}:${slot.connection.outputSlotIndex}`;
|
||||
counts.set(name, counts.has(name) ? counts.get(name) + 1 : 1);
|
||||
}
|
||||
}
|
||||
const values = new Map();
|
||||
const value = (layerIndex, slotIndex, tensor) => {
|
||||
const name = `${layerIndex}:${slotIndex}`;
|
||||
if (!values.has(name)) {
|
||||
const layer = graph.layers[layerIndex];
|
||||
const base = layerIndex < graph.layers.length ? armnn.Node.getBase(layer) : null;
|
||||
const tensorInfo = base && slotIndex < base.outputSlots.length ? base.outputSlots[slotIndex].tensorInfo : null;
|
||||
values.set(name, new armnn.Value(name, tensorInfo, tensor));
|
||||
}
|
||||
return values.get(name);
|
||||
};
|
||||
const layers = graph.layers.filter((layer) => {
|
||||
const base = armnn.Node.getBase(layer);
|
||||
if (base.layerType === armnn.schema.LayerType.Constant && base.outputSlots.length === 1 && layer.layer.input) {
|
||||
const [slot] = base.outputSlots;
|
||||
const name = `${base.index}:${slot.index}`;
|
||||
if (counts.get(name) === 1) {
|
||||
const tensor = new armnn.Tensor(layer.layer.input, 'Constant');
|
||||
value(base.index, slot.index, tensor);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
for (const layer of layers) {
|
||||
const base = armnn.Node.getBase(layer);
|
||||
for (const slot of base.inputSlots) {
|
||||
value(slot.connection.sourceLayerIndex, slot.connection.outputSlotIndex);
|
||||
}
|
||||
}
|
||||
for (const layer of layers) {
|
||||
const base = armnn.Node.getBase(layer);
|
||||
switch (base.layerType) {
|
||||
case armnn.schema.LayerType.Input: {
|
||||
const name = base ? base.layerName : '';
|
||||
for (const slot of base.outputSlots) {
|
||||
const argument = new armnn.Argument(name, [value(base.index, slot.index)]);
|
||||
this.inputs.push(argument);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case armnn.schema.LayerType.Output: {
|
||||
const base = armnn.Node.getBase(layer);
|
||||
const name = base ? base.layerName : '';
|
||||
for (const slot of base.inputSlots) {
|
||||
const argument = new armnn.Argument(name, [value(slot.connection.sourceLayerIndex, slot.connection.outputSlotIndex)]);
|
||||
this.outputs.push(argument);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
this.nodes.push(new armnn.Node(metadata, layer, value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
armnn.Node = class {
|
||||
|
||||
constructor(metadata, layer, value) {
|
||||
const name = layer.layer.constructor.name;
|
||||
const type = metadata.type(name);
|
||||
this.type = type ? { ...type } : { name };
|
||||
this.type.name = this.type.name.replace(/Layer$/, '');
|
||||
this.name = '';
|
||||
this.outputs = [];
|
||||
this.inputs = [];
|
||||
this.attributes = [];
|
||||
const inputSchemas = (this.type && this.type.inputs) ? [...this.type.inputs] : [{ name: 'input' }];
|
||||
const outputSchemas = (this.type && this.type.outputs) ? [...this.type.outputs] : [{ name: 'output' }];
|
||||
const base = armnn.Node.getBase(layer);
|
||||
if (base) {
|
||||
this.name = base.layerName;
|
||||
const inputs = [...base.inputSlots];
|
||||
while (inputs.length > 0) {
|
||||
const schema = inputSchemas.length > 0 ? inputSchemas.shift() : { name: '?' };
|
||||
const count = schema.list ? inputs.length : 1;
|
||||
const argument = new armnn.Argument(schema.name, inputs.splice(0, count).map((inputSlot) => {
|
||||
return value(inputSlot.connection.sourceLayerIndex, inputSlot.connection.outputSlotIndex);
|
||||
}));
|
||||
this.inputs.push(argument);
|
||||
}
|
||||
const outputs = [...base.outputSlots];
|
||||
while (outputs.length > 0) {
|
||||
const schema = outputSchemas.length > 0 ? outputSchemas.shift() : { name: '?' };
|
||||
const count = schema.list ? outputs.length : 1;
|
||||
this.outputs.push(new armnn.Argument(schema.name, outputs.splice(0, count).map((outputSlot) => {
|
||||
return value(base.index, outputSlot.index);
|
||||
})));
|
||||
}
|
||||
}
|
||||
if (layer.layer) {
|
||||
if (layer.layer.descriptor && this.type.attributes) {
|
||||
for (const [key, obj] of Object.entries(layer.layer.descriptor)) {
|
||||
const schema = metadata.attribute(name, key);
|
||||
const type = schema ? schema.type : null;
|
||||
let value = ArrayBuffer.isView(obj) ? Array.from(obj) : obj;
|
||||
const enumType = armnn.schema[type];
|
||||
if (enumType) {
|
||||
value = enumType[value] || value;
|
||||
}
|
||||
const attribute = new armnn.Argument(key, value, type);
|
||||
this.attributes.push(attribute);
|
||||
}
|
||||
}
|
||||
for (const [name, tensor] of Object.entries(layer.layer).filter(([, value]) => value instanceof armnn.schema.ConstTensor)) {
|
||||
const value = new armnn.Value('', tensor.info, new armnn.Tensor(tensor));
|
||||
const argument = new armnn.Argument(name, [value]);
|
||||
this.inputs.push(argument);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static getBase(layer) {
|
||||
return layer.layer.base.base ? layer.layer.base.base : layer.layer.base;
|
||||
}
|
||||
|
||||
static makeKey(layer_id, index) {
|
||||
return `${layer_id}_${index}`;
|
||||
}
|
||||
};
|
||||
|
||||
armnn.Argument = class {
|
||||
|
||||
constructor(name, value, type = null) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
}
|
||||
};
|
||||
|
||||
armnn.Value = class {
|
||||
|
||||
constructor(name, tensorInfo, initializer) {
|
||||
if (typeof name !== 'string') {
|
||||
throw new armnn.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
|
||||
}
|
||||
this.name = name;
|
||||
this.type = new armnn.TensorType(tensorInfo);
|
||||
this.initializer = initializer;
|
||||
if (tensorInfo.quantizationScale !== 0 ||
|
||||
tensorInfo.quantizationOffset !== 0 ||
|
||||
tensorInfo.quantizationScales.length > 0 ||
|
||||
tensorInfo.quantizationDim !== 0) {
|
||||
this.quantization = {
|
||||
type: 'linear',
|
||||
dimension: tensorInfo.quantizationDim,
|
||||
scale: [tensorInfo.quantizationScale],
|
||||
offset: [tensorInfo.quantizationOffset]
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
armnn.Tensor = class {
|
||||
|
||||
constructor(tensor, category = '') {
|
||||
this.type = new armnn.TensorType(tensor.info);
|
||||
this.category = category;
|
||||
const data = tensor.data.data.slice(0);
|
||||
this.values = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
||||
}
|
||||
};
|
||||
|
||||
armnn.TensorType = class {
|
||||
|
||||
constructor(tensorInfo) {
|
||||
const dataType = tensorInfo.dataType;
|
||||
switch (dataType) {
|
||||
case 0: this.dataType = 'float16'; break;
|
||||
case 1: this.dataType = 'float32'; break;
|
||||
case 2: this.dataType = 'quint8'; break; // QuantisedAsymm8
|
||||
case 3: this.dataType = 'int32'; break;
|
||||
case 4: this.dataType = 'boolean'; break;
|
||||
case 5: this.dataType = 'qint16'; break; // QuantisedSymm16
|
||||
case 6: this.dataType = 'quint8'; break; // QAsymmU8
|
||||
case 7: this.dataType = 'qint16'; break; // QSymmS16
|
||||
case 8: this.dataType = 'qint8'; break; // QAsymmS8
|
||||
case 9: this.dataType = 'qint8'; break; // QSymmS8
|
||||
default:
|
||||
throw new armnn.Error(`Unsupported data type '${JSON.stringify(dataType)}'.`);
|
||||
}
|
||||
this.shape = new armnn.TensorShape(tensorInfo.dimensions);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.dataType + this.shape.toString();
|
||||
}
|
||||
};
|
||||
|
||||
armnn.TensorShape = class {
|
||||
|
||||
constructor(dimensions) {
|
||||
this.dimensions = Array.from(dimensions);
|
||||
}
|
||||
|
||||
toString() {
|
||||
if (!this.dimensions || this.dimensions.length === 0) {
|
||||
return '';
|
||||
}
|
||||
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
|
||||
}
|
||||
};
|
||||
|
||||
armnn.Error = class extends Error {
|
||||
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'Error loading Arm NN model.';
|
||||
}
|
||||
};
|
||||
|
||||
export const ModelFactory = armnn.ModelFactory;
|
||||
@@ -0,0 +1,448 @@
|
||||
|
||||
// Experimental
|
||||
|
||||
const barracuda = {};
|
||||
|
||||
barracuda.ModelFactory = class {
|
||||
|
||||
async match(context) {
|
||||
const stream = context.stream;
|
||||
if (stream && stream.length > 12) {
|
||||
const buffer = stream.peek(12);
|
||||
if (buffer[0] <= 0x20 && buffer.subarray(1, 8).every((value) => value === 0x00)) {
|
||||
return context.set('barracuda');
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async open(context) {
|
||||
const metadata = barracuda.Metadata.open();
|
||||
const reader = await context.read('binary');
|
||||
const model = new barracuda.NNModel(reader);
|
||||
return new barracuda.Model(metadata, model);
|
||||
}
|
||||
};
|
||||
|
||||
barracuda.Model = class {
|
||||
|
||||
constructor(metadata, model) {
|
||||
const version = model.version.toString();
|
||||
this.format = `Barracuda v${version}`;
|
||||
this.modules = [new barracuda.Graph(metadata, model)];
|
||||
}
|
||||
};
|
||||
|
||||
barracuda.Graph = class {
|
||||
|
||||
constructor(metadata, model) {
|
||||
this.name = '';
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.nodes = [];
|
||||
const values = new Map();
|
||||
values.map = (name, type, tensor) => {
|
||||
if (!values.has(name)) {
|
||||
type = tensor ? tensor.type : type;
|
||||
values.set(name, new barracuda.Value(name, type, tensor));
|
||||
} else if (type || tensor) {
|
||||
throw new barracuda.Error(`Duplicate value '${name}'.`);
|
||||
}
|
||||
return values.get(name);
|
||||
};
|
||||
const layers = [];
|
||||
for (const layer of model.layers) {
|
||||
if (layer.type !== 255 || layer.inputs.length > 0) {
|
||||
layers.push(layer);
|
||||
} else {
|
||||
for (const tensor of layer.tensors) {
|
||||
values.map(tensor.name, null, new barracuda.Tensor(tensor));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const input of model.inputs) {
|
||||
const shape = new barracuda.TensorShape(input.shape);
|
||||
const type = new barracuda.TensorType(4, shape);
|
||||
const argument = new barracuda.Argument(input.name, [values.map(input.name, type)]);
|
||||
this.inputs.push(argument);
|
||||
}
|
||||
for (const output of model.outputs) {
|
||||
const argument = new barracuda.Argument(output, [values.map(output)]);
|
||||
this.outputs.push(argument);
|
||||
}
|
||||
for (const layer of layers) {
|
||||
const node = new barracuda.Node(metadata, layer, null, values);
|
||||
this.nodes.push(node);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
barracuda.Argument = class {
|
||||
|
||||
constructor(name, value, type = null) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
}
|
||||
};
|
||||
|
||||
barracuda.Value = class {
|
||||
|
||||
constructor(name, type = null, initializer = null) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.initializer = initializer;
|
||||
}
|
||||
};
|
||||
|
||||
barracuda.Node = class {
|
||||
|
||||
constructor(metadata, layer, type, values) {
|
||||
this.name = layer.name || '';
|
||||
this.type = type ? type : metadata.type(layer.type);
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.attributes = [];
|
||||
const inputs = Array.prototype.slice.call(this.type.inputs || ['input']);
|
||||
if (this.type.inputs && this.type.inputs.length === 1 && this.type.inputs[0].name === 'inputs') {
|
||||
const argument = new barracuda.Argument('inputs', layer.inputs.map((input) => values.map(input)));
|
||||
this.inputs.push(argument);
|
||||
} else if (layer.inputs) {
|
||||
for (let i = 0; i < layer.inputs.length; i++) {
|
||||
const input = layer.inputs[i];
|
||||
const name = inputs.length > 0 && inputs[0] ? inputs.shift().name : i.toString();
|
||||
const argument = new barracuda.Argument(name, [values.map(input)]);
|
||||
this.inputs.push(argument);
|
||||
}
|
||||
}
|
||||
if (layer.tensors) {
|
||||
for (let i = 0; i < layer.tensors.length; i++) {
|
||||
const tensor = layer.tensors[i];
|
||||
const initializer = new barracuda.Tensor(tensor);
|
||||
const name = inputs.length > 0 && inputs[0] ? inputs.shift().name : i.toString();
|
||||
const argument = new barracuda.Argument(name, [values.map(tensor.name, initializer.type, initializer)]);
|
||||
this.inputs.push(argument);
|
||||
}
|
||||
}
|
||||
if (layer.inputs !== undefined) {
|
||||
const argument = new barracuda.Argument('output', [values.map(this.name)]);
|
||||
this.outputs.push(argument);
|
||||
}
|
||||
if (layer.activation !== undefined && (layer.type === 50 || layer.activation !== 0)) {
|
||||
const type = barracuda.Activation[layer.activation];
|
||||
if (!type) {
|
||||
throw new barracuda.Error(`Unsupported activation '${layer.activation}'.`);
|
||||
}
|
||||
const node = new barracuda.Node(metadata, {}, { name: type, category: 'Activation' }, values);
|
||||
this.chain = [node];
|
||||
}
|
||||
const attributes = [
|
||||
['strides', 'int32[]', []],
|
||||
['pads', 'int32[]', (value) => Array.isArray(value) && (value.every((v) => v === 0) || value.every((v) => v === -1))],
|
||||
['pool_size', 'int32[]', []],
|
||||
['alpha', 'float32', 1],
|
||||
['beta', 'float32', 0],
|
||||
['axis', 'int32', -1]
|
||||
];
|
||||
for (const [name, type, defaultValue] of attributes) {
|
||||
const value = layer[name];
|
||||
if ((value === undefined) ||
|
||||
(Array.isArray(defaultValue) && Array.isArray(value) && value.length === defaultValue.length && value.every((v, i) => v === defaultValue[i])) ||
|
||||
(typeof defaultValue === 'function' && defaultValue(value)) ||
|
||||
(defaultValue === value)) {
|
||||
continue;
|
||||
}
|
||||
const attribute = new barracuda.Argument(name, value, type);
|
||||
this.attributes.push(attribute);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
barracuda.Tensor = class {
|
||||
|
||||
constructor(tensor) {
|
||||
this.type = new barracuda.TensorType(tensor.itemsize, new barracuda.TensorShape(tensor.shape));
|
||||
this.values = tensor.data;
|
||||
}
|
||||
};
|
||||
|
||||
barracuda.TensorType = class {
|
||||
|
||||
constructor(itemsize, shape) {
|
||||
switch (itemsize) {
|
||||
case 4: this.dataType = 'float32'; break;
|
||||
default: throw new barracuda.Error(`Unsupported data type size '${itemsize}'.`);
|
||||
}
|
||||
this.shape = shape;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.dataType + this.shape.toString();
|
||||
}
|
||||
};
|
||||
|
||||
barracuda.TensorShape = class {
|
||||
|
||||
constructor(dimensions) {
|
||||
this.dimensions = dimensions;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.dimensions ? (`[${this.dimensions.map((dimension) => dimension ? dimension.toString() : '?').join(',')}]`) : '';
|
||||
}
|
||||
};
|
||||
|
||||
barracuda.NNModel = class {
|
||||
|
||||
constructor(reader) {
|
||||
// https://github.com/Unity-Technologies/barracuda-release/blob/release/1.3.2/Barracuda/Runtime/Core/Model.cs
|
||||
reader = new barracuda.BinaryReader(reader);
|
||||
this.version = reader.int32();
|
||||
reader.int32();
|
||||
this.inputs = new Array(reader.int32());
|
||||
for (let i = 0; i < this.inputs.length; i++) {
|
||||
this.inputs[i] = {
|
||||
name: reader.string(),
|
||||
shape: reader.shape()
|
||||
};
|
||||
}
|
||||
this.outputs = reader.strings();
|
||||
this.memories = new Array(reader.int32());
|
||||
for (let i = 0; i < this.memories.length; i++) {
|
||||
this.memories[i] = {
|
||||
shape: reader.shape(),
|
||||
in: reader.string(),
|
||||
out: reader.string()
|
||||
};
|
||||
}
|
||||
this.layers = new Array(reader.int32());
|
||||
for (let i = 0; i < this.layers.length; i++) {
|
||||
const layer = {};
|
||||
layer.name = reader.string();
|
||||
layer.type = reader.int32();
|
||||
layer.activation = reader.int32();
|
||||
reader.int32();
|
||||
reader.int32();
|
||||
layer.pads = reader.int32s();
|
||||
layer.strides = reader.int32s();
|
||||
layer.pool_size = reader.int32s();
|
||||
layer.axis = reader.int32();
|
||||
layer.alpha = reader.float32();
|
||||
layer.beta = reader.float32();
|
||||
reader.int32();
|
||||
layer.inputs = reader.strings();
|
||||
layer.tensors = [];
|
||||
const tensorsLength = reader.int32();
|
||||
for (let j = 0; j < tensorsLength; j++) {
|
||||
layer.tensors.push({
|
||||
name: reader.string(),
|
||||
shape: reader.shape(),
|
||||
offset: reader.int64().toNumber(),
|
||||
itemsize: reader.int32(),
|
||||
length: reader.int32()
|
||||
});
|
||||
}
|
||||
this.layers[i] = layer;
|
||||
}
|
||||
const position = reader.position;
|
||||
for (const layer of this.layers) {
|
||||
for (const tensor of layer.tensors) {
|
||||
const offset = tensor.offset;
|
||||
reader.seek(position + (offset * tensor.itemsize));
|
||||
tensor.data = reader.read(tensor.length * tensor.itemsize);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
barracuda.Activation = {
|
||||
0: "Linear", 1: "Relu", 2: "Softmax", 3: "Tanh", 4: "Sigmoid", 5: "Elu", 6: "Relu6", 7: "LeakyRelu", 8: "Selu", 9: "Swish",
|
||||
10: "LogSoftmax", 11: "Softplus", 12: "Softsign", 13: "PRelu",
|
||||
20: "Hardmax", 21: "HardSigmoid",
|
||||
100: "Abs", 101: "Neg", 102: "Ceil", 103: "Clip", 104: "Floor", 105: "Round",
|
||||
110: "Reciprocal", 111: "Sqrt", 113: "Exp", 114: "Log",
|
||||
200: "Acos", 201: "Acosh", 202: "Asin", 203: "Asinh", 204: "Atan", 205: "Atanh", 206: "Cos", 207: "Cosh", 208: "Sin", 209: "Sinh", 210: "Tan"
|
||||
};
|
||||
|
||||
barracuda.BinaryReader = class {
|
||||
|
||||
constructor(reader) {
|
||||
this._reader = reader;
|
||||
}
|
||||
|
||||
get position() {
|
||||
return this._reader.position;
|
||||
}
|
||||
|
||||
seek(position) {
|
||||
this._reader.seek(position);
|
||||
}
|
||||
|
||||
skip(offset) {
|
||||
this._reader.skip(offset);
|
||||
}
|
||||
|
||||
read(length) {
|
||||
return this._reader.read(length);
|
||||
}
|
||||
|
||||
byte() {
|
||||
return this._reader.byte();
|
||||
}
|
||||
|
||||
int32() {
|
||||
return this._reader.int32();
|
||||
}
|
||||
|
||||
int32s() {
|
||||
const values = new Array(this.int32());
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
values[i] = this.int32();
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
int64() {
|
||||
return this._reader.int64();
|
||||
}
|
||||
|
||||
float32() {
|
||||
return this._reader.float32();
|
||||
}
|
||||
|
||||
string() {
|
||||
let content = '';
|
||||
const size = this.int32();
|
||||
for (let i = 0; i < size; i++) {
|
||||
const c = this.byte();
|
||||
content += String.fromCharCode(c);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
strings() {
|
||||
const values = [];
|
||||
const length = this.int32();
|
||||
for (let i = 0; i < length; i++) {
|
||||
values.push(this.string());
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
shape() {
|
||||
return this.int32s();
|
||||
}
|
||||
};
|
||||
|
||||
barracuda.Metadata = class {
|
||||
|
||||
static open() {
|
||||
barracuda.Metadata._metadata = barracuda.Metadata._metadata || new barracuda.Metadata();
|
||||
return barracuda.Metadata._metadata;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this._types = new Map();
|
||||
const register = (id, name, category, inputs) => {
|
||||
this._types.set(id, { name, category, inputs: (inputs || []).map((input) => {
|
||||
return { name: input };
|
||||
}) });
|
||||
};
|
||||
register(0, 'Nop', '');
|
||||
register(1, 'Dense', 'Layer', ['input', 'kernel', 'bias']);
|
||||
register(2, 'MatMul', '', ['input', 'kernel', 'bias']);
|
||||
register(20, 'Conv2D', 'Layer', ['input', 'kernel', 'bias']);
|
||||
register(21, 'DepthwiseConv2D', 'Layer', ['input', 'kernel', 'bias']);
|
||||
register(22, 'Conv2DTrans', 'Layer', ['input', 'kernel', 'bias']);
|
||||
register(23, 'Upsample2D', 'Data');
|
||||
register(25, 'MaxPool2D', 'Pool');
|
||||
register(26, 'AvgPool2D', 'Pool');
|
||||
register(27, 'GlobalMaxPool2D', 'Pool');
|
||||
register(28, 'GlobalAvgPool2D', 'Pool');
|
||||
register(29, 'Border2D', '');
|
||||
register(30, 'Conv3D', 'Layer');
|
||||
register(32, 'Conv3DTrans', 'Layer');
|
||||
register(33, 'Upsample3D', 'Data');
|
||||
register(35, 'MaxPool3D', 'Pool');
|
||||
register(36, 'AvgPool3D', 'Pool');
|
||||
register(37, 'GlobalMaxPool3D', 'Pool');
|
||||
register(38, 'GlobalAvgPool3D', 'Pool');
|
||||
register(39, 'Border3D', '');
|
||||
register(50, 'Activation', '', ['input']);
|
||||
register(51, 'ScaleBias', 'Normalization', ['input', 'scale', 'bias']);
|
||||
register(52, 'Normalization', 'Normalization');
|
||||
register(53, 'LRN', 'Normalization');
|
||||
register(60, 'Dropout', 'Dropout');
|
||||
register(64, 'RandomNormal', '');
|
||||
register(65, 'RandomUniform', '');
|
||||
register(66, 'Multinomial', '');
|
||||
register(67, 'OneHot', '');
|
||||
register(68, 'TopKIndices', '');
|
||||
register(69, 'TopKValues', '');
|
||||
register(100, 'Add', '', ['inputs']);
|
||||
register(101, 'Sub', '', ['inputs']);
|
||||
register(102, 'Mul', '', ['inputs']);
|
||||
register(103, 'RealDiv', '', ['inputs']);
|
||||
register(104, 'Pow', '', ['inputs']);
|
||||
register(110, 'Minimum', '', ['inputs']);
|
||||
register(111, 'Maximum', '', ['inputs']);
|
||||
register(112, 'Mean', '', ['inputs']);
|
||||
register(120, 'ReduceL1', '', ['inputs']);
|
||||
register(121, 'ReduceL2', '', ['inputs']);
|
||||
register(122, 'ReduceLogSum', '', ['inputs']);
|
||||
register(123, 'ReduceLogSumExp', '', ['inputs']);
|
||||
register(124, 'ReduceMax', '', ['inputs']);
|
||||
register(125, 'ReduceMean', '', ['inputs']);
|
||||
register(126, 'ReduceMin', '', ['inputs']);
|
||||
register(127, 'ReduceProd', '', ['inputs']);
|
||||
register(128, 'ReduceSum', '', ['inputs']);
|
||||
register(129, 'ReduceSumSquare', '', ['inputs']);
|
||||
register(140, 'Greater', '');
|
||||
register(141, 'GreaterEqual', '');
|
||||
register(142, 'Less', '');
|
||||
register(143, 'LessEqual', '');
|
||||
register(144, 'Equal', '');
|
||||
register(145, 'LogicalOr', '');
|
||||
register(146, 'LogicalAnd', '');
|
||||
register(147, 'LogicalNot', '');
|
||||
register(148, 'LogicalXor', '');
|
||||
register(160, 'Pad2DReflect', '');
|
||||
register(161, 'Pad2DSymmetric', '');
|
||||
register(162, 'Pad2DEdge', '');
|
||||
register(200, 'Flatten', 'Shape');
|
||||
register(201, 'Reshape', 'Shape');
|
||||
register(202, 'Transpose', '');
|
||||
register(203, 'Squeeze', '');
|
||||
register(204, 'Unsqueeze', '');
|
||||
register(205, 'Gather', '');
|
||||
register(206, 'DepthToSpace', '');
|
||||
register(207, 'SpaceToDepth', '');
|
||||
register(208, 'Expand', '');
|
||||
register(209, 'Resample2D', '');
|
||||
register(210, 'Concat', 'Tensor', ['inputs']);
|
||||
register(211, 'StridedSlice', 'Shape');
|
||||
register(212, 'Tile', '');
|
||||
register(213, 'Shape', '');
|
||||
register(214, 'NonMaxSuppression', '');
|
||||
register(215, 'LSTM', '');
|
||||
register(255, 'Load', '');
|
||||
}
|
||||
|
||||
type(name) {
|
||||
if (!this._types.has(name)) {
|
||||
this._types.set(name, { name: name.toString() });
|
||||
}
|
||||
return this._types.get(name);
|
||||
}
|
||||
};
|
||||
|
||||
barracuda.Error = class extends Error {
|
||||
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'Error loading Barracuda model.';
|
||||
}
|
||||
};
|
||||
|
||||
export const ModelFactory = barracuda.ModelFactory;
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
[
|
||||
{
|
||||
"name": "com.intel.analytics.bigdl.nn.Dropout",
|
||||
"category": "Dropout"
|
||||
},
|
||||
{
|
||||
"name": "com.intel.analytics.bigdl.nn.InferReshape",
|
||||
"category": "Shape"
|
||||
},
|
||||
{
|
||||
"name": "com.intel.analytics.bigdl.nn.JoinTable",
|
||||
"category": "Tensor",
|
||||
"inputs": [
|
||||
{ "name": "inputs", "list": true }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "com.intel.analytics.bigdl.nn.Linear",
|
||||
"category": "Layer",
|
||||
"inputs": [
|
||||
{ "name": "inputs" },
|
||||
{ "name": "weight" },
|
||||
{ "name": "bias" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "com.intel.analytics.bigdl.nn.NormalizeScale",
|
||||
"category": "Normalization",
|
||||
"inputs": [
|
||||
{ "name": "inputs" },
|
||||
{ "name": "w" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "com.intel.analytics.bigdl.nn.ReLU",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "Scale",
|
||||
"category": "Layer",
|
||||
"inputs": [
|
||||
{ "name": "inputs" },
|
||||
{ "name": "weight" },
|
||||
{ "name": "bias" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SoftMax",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "com.intel.analytics.bigdl.nn.SpatialAveragePooling",
|
||||
"category": "Pool"
|
||||
},
|
||||
{
|
||||
"name": "com.intel.analytics.bigdl.nn.SpatialBatchNormalization",
|
||||
"category": "Normalization"
|
||||
},
|
||||
{
|
||||
"name": "com.intel.analytics.bigdl.nn.quantized.SpatialConvolution",
|
||||
"category": "Layer",
|
||||
"inputs": [
|
||||
{ "name": "inputs" },
|
||||
{ "name": "weight" },
|
||||
{ "name": "bias" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "com.intel.analytics.bigdl.nn.SpatialCrossMapLRN",
|
||||
"category": "Normalization"
|
||||
},
|
||||
{
|
||||
"name": "com.intel.analytics.bigdl.nn.SpatialDilatedConvolution",
|
||||
"category": "Layer",
|
||||
"inputs": [
|
||||
{ "name": "inputs" },
|
||||
{ "name": "weight" },
|
||||
{ "name": "bias" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "com.intel.analytics.bigdl.nn.SpatialMaxPooling",
|
||||
"category": "Pool"
|
||||
},
|
||||
{
|
||||
"name": "com.intel.analytics.bigdl.nn.Transpose",
|
||||
"category": "Shape"
|
||||
},
|
||||
{
|
||||
"name": "com.intel.analytics.bigdl.nn.View"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,630 @@
|
||||
|
||||
export const com = {};
|
||||
export const google = {};
|
||||
|
||||
com.intel = {};
|
||||
|
||||
com.intel.analytics = {};
|
||||
|
||||
com.intel.analytics.bigdl = {};
|
||||
|
||||
com.intel.analytics.bigdl.serialization = {};
|
||||
|
||||
com.intel.analytics.bigdl.serialization.BigDLModule = class BigDLModule {
|
||||
|
||||
constructor() {
|
||||
this.subModules = [];
|
||||
this.preModules = [];
|
||||
this.nextModules = [];
|
||||
this.attr = {};
|
||||
this.parameters = [];
|
||||
this.inputScales = [];
|
||||
this.outputScales = [];
|
||||
this.weightScales = [];
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new com.intel.analytics.bigdl.serialization.BigDLModule();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.name = reader.string();
|
||||
break;
|
||||
case 2:
|
||||
message.subModules.push(com.intel.analytics.bigdl.serialization.BigDLModule.decode(reader, reader.uint32()));
|
||||
break;
|
||||
case 3:
|
||||
message.weight = com.intel.analytics.bigdl.serialization.BigDLTensor.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 4:
|
||||
message.bias = com.intel.analytics.bigdl.serialization.BigDLTensor.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 5:
|
||||
message.preModules.push(reader.string());
|
||||
break;
|
||||
case 6:
|
||||
message.nextModules.push(reader.string());
|
||||
break;
|
||||
case 7:
|
||||
message.moduleType = reader.string();
|
||||
break;
|
||||
case 8:
|
||||
reader.entry(message.attr, () => reader.string(), () => com.intel.analytics.bigdl.serialization.AttrValue.decode(reader, reader.uint32()));
|
||||
break;
|
||||
case 9:
|
||||
message.version = reader.string();
|
||||
break;
|
||||
case 10:
|
||||
message.train = reader.bool();
|
||||
break;
|
||||
case 11:
|
||||
message.namePostfix = reader.string();
|
||||
break;
|
||||
case 12:
|
||||
message.id = reader.int32();
|
||||
break;
|
||||
case 13:
|
||||
message.inputShape = com.intel.analytics.bigdl.serialization.Shape.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 14:
|
||||
message.outputShape = com.intel.analytics.bigdl.serialization.Shape.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 15:
|
||||
message.hasParameters = reader.bool();
|
||||
break;
|
||||
case 16:
|
||||
message.parameters.push(com.intel.analytics.bigdl.serialization.BigDLTensor.decode(reader, reader.uint32()));
|
||||
break;
|
||||
case 17:
|
||||
message.isMklInt8Enabled = reader.bool();
|
||||
break;
|
||||
case 18:
|
||||
message.inputDimMasks = reader.int32();
|
||||
break;
|
||||
case 19:
|
||||
message.inputScales.push(com.intel.analytics.bigdl.serialization.AttrValue.decode(reader, reader.uint32()));
|
||||
break;
|
||||
case 20:
|
||||
message.outputDimMasks = reader.int32();
|
||||
break;
|
||||
case 21:
|
||||
message.outputScales.push(com.intel.analytics.bigdl.serialization.AttrValue.decode(reader, reader.uint32()));
|
||||
break;
|
||||
case 22:
|
||||
message.weightDimMasks = reader.int32();
|
||||
break;
|
||||
case 23:
|
||||
message.weightScales.push(com.intel.analytics.bigdl.serialization.AttrValue.decode(reader, reader.uint32()));
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.name = "";
|
||||
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.weight = null;
|
||||
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.bias = null;
|
||||
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.moduleType = "";
|
||||
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.version = "";
|
||||
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.train = false;
|
||||
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.namePostfix = "";
|
||||
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.id = 0;
|
||||
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.inputShape = null;
|
||||
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.outputShape = null;
|
||||
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.hasParameters = false;
|
||||
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.isMklInt8Enabled = false;
|
||||
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.inputDimMasks = 0;
|
||||
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.outputDimMasks = 0;
|
||||
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.weightDimMasks = 0;
|
||||
|
||||
com.intel.analytics.bigdl.serialization.VarFormat = {
|
||||
"EMPTY_FORMAT": 0,
|
||||
"DEFAULT": 1,
|
||||
"ONE_D": 2,
|
||||
"IN_OUT": 3,
|
||||
"OUT_IN": 4,
|
||||
"IN_OUT_KW_KH": 5,
|
||||
"OUT_IN_KW_KH": 6,
|
||||
"GP_OUT_IN_KW_KH": 7,
|
||||
"GP_IN_OUT_KW_KH": 8,
|
||||
"OUT_IN_KT_KH_KW": 9
|
||||
};
|
||||
|
||||
com.intel.analytics.bigdl.serialization.InitMethodType = {
|
||||
"EMPTY_INITIALIZATION": 0,
|
||||
"RANDOM_UNIFORM": 1,
|
||||
"RANDOM_UNIFORM_PARAM": 2,
|
||||
"RANDOM_NORMAL": 3,
|
||||
"ZEROS": 4,
|
||||
"ONES": 5,
|
||||
"CONST": 6,
|
||||
"XAVIER": 7,
|
||||
"BILINEARFILLER": 8
|
||||
};
|
||||
|
||||
com.intel.analytics.bigdl.serialization.RegularizerType = {
|
||||
"L1L2Regularizer": 0,
|
||||
"L1Regularizer": 1,
|
||||
"L2Regularizer": 2
|
||||
};
|
||||
|
||||
com.intel.analytics.bigdl.serialization.InputDataFormat = {
|
||||
"NCHW": 0,
|
||||
"NHWC": 1
|
||||
};
|
||||
|
||||
com.intel.analytics.bigdl.serialization.TensorType = {
|
||||
"DENSE": 0,
|
||||
"QUANT": 1
|
||||
};
|
||||
|
||||
com.intel.analytics.bigdl.serialization.InitMethod = class InitMethod {
|
||||
|
||||
constructor() {
|
||||
this.data = [];
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new com.intel.analytics.bigdl.serialization.InitMethod();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.methodType = reader.int32();
|
||||
break;
|
||||
case 2:
|
||||
message.data = reader.doubles(message.data, tag);
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
com.intel.analytics.bigdl.serialization.InitMethod.prototype.methodType = 0;
|
||||
|
||||
com.intel.analytics.bigdl.serialization.BigDLTensor = class BigDLTensor {
|
||||
|
||||
constructor() {
|
||||
this.size = [];
|
||||
this.stride = [];
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new com.intel.analytics.bigdl.serialization.BigDLTensor();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.datatype = reader.int32();
|
||||
break;
|
||||
case 2:
|
||||
message.size = reader.array(message.size, () => reader.int32(), tag);
|
||||
break;
|
||||
case 3:
|
||||
message.stride = reader.array(message.stride, () => reader.int32(), tag);
|
||||
break;
|
||||
case 4:
|
||||
message.offset = reader.int32();
|
||||
break;
|
||||
case 5:
|
||||
message.dimension = reader.int32();
|
||||
break;
|
||||
case 6:
|
||||
message.nElements = reader.int32();
|
||||
break;
|
||||
case 7:
|
||||
message.isScalar = reader.bool();
|
||||
break;
|
||||
case 8:
|
||||
message.storage = com.intel.analytics.bigdl.serialization.TensorStorage.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 9:
|
||||
message.id = reader.int32();
|
||||
break;
|
||||
case 10:
|
||||
message.tensorType = reader.int32();
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
com.intel.analytics.bigdl.serialization.BigDLTensor.prototype.datatype = 0;
|
||||
com.intel.analytics.bigdl.serialization.BigDLTensor.prototype.offset = 0;
|
||||
com.intel.analytics.bigdl.serialization.BigDLTensor.prototype.dimension = 0;
|
||||
com.intel.analytics.bigdl.serialization.BigDLTensor.prototype.nElements = 0;
|
||||
com.intel.analytics.bigdl.serialization.BigDLTensor.prototype.isScalar = false;
|
||||
com.intel.analytics.bigdl.serialization.BigDLTensor.prototype.storage = null;
|
||||
com.intel.analytics.bigdl.serialization.BigDLTensor.prototype.id = 0;
|
||||
com.intel.analytics.bigdl.serialization.BigDLTensor.prototype.tensorType = 0;
|
||||
|
||||
com.intel.analytics.bigdl.serialization.TensorStorage = class TensorStorage {
|
||||
|
||||
constructor() {
|
||||
this.float_data = [];
|
||||
this.double_data = [];
|
||||
this.bool_data = [];
|
||||
this.string_data = [];
|
||||
this.int_data = [];
|
||||
this.long_data = [];
|
||||
this.bytes_data = [];
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new com.intel.analytics.bigdl.serialization.TensorStorage();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.datatype = reader.int32();
|
||||
break;
|
||||
case 2:
|
||||
message.float_data = reader.floats(message.float_data, tag);
|
||||
break;
|
||||
case 3:
|
||||
message.double_data = reader.doubles(message.double_data, tag);
|
||||
break;
|
||||
case 4:
|
||||
message.bool_data = reader.array(message.bool_data, () => reader.bool(), tag);
|
||||
break;
|
||||
case 5:
|
||||
message.string_data.push(reader.string());
|
||||
break;
|
||||
case 6:
|
||||
message.int_data = reader.array(message.int_data, () => reader.int32(), tag);
|
||||
break;
|
||||
case 7:
|
||||
message.long_data = reader.array(message.long_data, () => reader.int64(), tag);
|
||||
break;
|
||||
case 8:
|
||||
message.bytes_data.push(reader.bytes());
|
||||
break;
|
||||
case 9:
|
||||
message.id = reader.int32();
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
com.intel.analytics.bigdl.serialization.TensorStorage.prototype.datatype = 0;
|
||||
com.intel.analytics.bigdl.serialization.TensorStorage.prototype.id = 0;
|
||||
|
||||
com.intel.analytics.bigdl.serialization.Regularizer = class Regularizer {
|
||||
|
||||
constructor() {
|
||||
this.regularData = [];
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new com.intel.analytics.bigdl.serialization.Regularizer();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.regularizerType = reader.int32();
|
||||
break;
|
||||
case 2:
|
||||
message.regularData = reader.doubles(message.regularData, tag);
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
com.intel.analytics.bigdl.serialization.Regularizer.prototype.regularizerType = 0;
|
||||
|
||||
com.intel.analytics.bigdl.serialization.DataType = {
|
||||
"INT32": 0,
|
||||
"INT64": 1,
|
||||
"FLOAT": 2,
|
||||
"DOUBLE": 3,
|
||||
"STRING": 4,
|
||||
"BOOL": 5,
|
||||
"CHAR": 6,
|
||||
"SHORT": 7,
|
||||
"BYTES": 8,
|
||||
"REGULARIZER": 9,
|
||||
"TENSOR": 10,
|
||||
"VARIABLE_FORMAT": 11,
|
||||
"INITMETHOD": 12,
|
||||
"MODULE": 13,
|
||||
"NAME_ATTR_LIST": 14,
|
||||
"ARRAY_VALUE": 15,
|
||||
"DATA_FORMAT": 16,
|
||||
"CUSTOM": 17,
|
||||
"SHAPE": 18
|
||||
};
|
||||
|
||||
com.intel.analytics.bigdl.serialization.AttrValue = class AttrValue {
|
||||
|
||||
get value() {
|
||||
com.intel.analytics.bigdl.serialization.AttrValue.valueSet = com.intel.analytics.bigdl.serialization.AttrValue.valueSet || new Set(["int32Value", "int64Value", "floatValue", "doubleValue", "stringValue", "boolValue", "regularizerValue", "tensorValue", "variableFormatValue", "initMethodValue", "bigDLModuleValue", "nameAttrListValue", "arrayValue", "dataFormatValue", "customValue", "shape"]);
|
||||
return Object.keys(this).find((key) => com.intel.analytics.bigdl.serialization.AttrValue.valueSet.has(key) && this[key] !== null);
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new com.intel.analytics.bigdl.serialization.AttrValue();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.dataType = reader.int32();
|
||||
break;
|
||||
case 2:
|
||||
message.subType = reader.string();
|
||||
break;
|
||||
case 3:
|
||||
message.int32Value = reader.int32();
|
||||
break;
|
||||
case 4:
|
||||
message.int64Value = reader.int64();
|
||||
break;
|
||||
case 5:
|
||||
message.floatValue = reader.float();
|
||||
break;
|
||||
case 6:
|
||||
message.doubleValue = reader.double();
|
||||
break;
|
||||
case 7:
|
||||
message.stringValue = reader.string();
|
||||
break;
|
||||
case 8:
|
||||
message.boolValue = reader.bool();
|
||||
break;
|
||||
case 9:
|
||||
message.regularizerValue = com.intel.analytics.bigdl.serialization.Regularizer.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 10:
|
||||
message.tensorValue = com.intel.analytics.bigdl.serialization.BigDLTensor.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 11:
|
||||
message.variableFormatValue = reader.int32();
|
||||
break;
|
||||
case 12:
|
||||
message.initMethodValue = com.intel.analytics.bigdl.serialization.InitMethod.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 13:
|
||||
message.bigDLModuleValue = com.intel.analytics.bigdl.serialization.BigDLModule.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 14:
|
||||
message.nameAttrListValue = com.intel.analytics.bigdl.serialization.NameAttrList.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 15:
|
||||
message.arrayValue = com.intel.analytics.bigdl.serialization.AttrValue.ArrayValue.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 16:
|
||||
message.dataFormatValue = reader.int32();
|
||||
break;
|
||||
case 17:
|
||||
message.customValue = google.protobuf.Any.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 18:
|
||||
message.shape = com.intel.analytics.bigdl.serialization.Shape.decode(reader, reader.uint32());
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
com.intel.analytics.bigdl.serialization.AttrValue.prototype.dataType = 0;
|
||||
com.intel.analytics.bigdl.serialization.AttrValue.prototype.subType = "";
|
||||
|
||||
com.intel.analytics.bigdl.serialization.AttrValue.ArrayValue = class ArrayValue {
|
||||
|
||||
constructor() {
|
||||
this.i32 = [];
|
||||
this.i64 = [];
|
||||
this.flt = [];
|
||||
this.dbl = [];
|
||||
this.str = [];
|
||||
this.boolean = [];
|
||||
this.Regularizer = [];
|
||||
this.tensor = [];
|
||||
this.variableFormat = [];
|
||||
this.initMethod = [];
|
||||
this.bigDLModule = [];
|
||||
this.nameAttrList = [];
|
||||
this.dataFormat = [];
|
||||
this.custom = [];
|
||||
this.shape = [];
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new com.intel.analytics.bigdl.serialization.AttrValue.ArrayValue();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.size = reader.int32();
|
||||
break;
|
||||
case 2:
|
||||
message.datatype = reader.int32();
|
||||
break;
|
||||
case 3:
|
||||
message.i32 = reader.array(message.i32, () => reader.int32(), tag);
|
||||
break;
|
||||
case 4:
|
||||
message.i64 = reader.array(message.i64, () => reader.int64(), tag);
|
||||
break;
|
||||
case 5:
|
||||
message.flt = reader.floats(message.flt, tag);
|
||||
break;
|
||||
case 6:
|
||||
message.dbl = reader.doubles(message.dbl, tag);
|
||||
break;
|
||||
case 7:
|
||||
message.str.push(reader.string());
|
||||
break;
|
||||
case 8:
|
||||
message.boolean = reader.array(message.boolean, () => reader.bool(), tag);
|
||||
break;
|
||||
case 9:
|
||||
message.Regularizer.push(com.intel.analytics.bigdl.serialization.Regularizer.decode(reader, reader.uint32()));
|
||||
break;
|
||||
case 10:
|
||||
message.tensor.push(com.intel.analytics.bigdl.serialization.BigDLTensor.decode(reader, reader.uint32()));
|
||||
break;
|
||||
case 11:
|
||||
message.variableFormat = reader.array(message.variableFormat, () => reader.int32(), tag);
|
||||
break;
|
||||
case 12:
|
||||
message.initMethod.push(com.intel.analytics.bigdl.serialization.InitMethod.decode(reader, reader.uint32()));
|
||||
break;
|
||||
case 13:
|
||||
message.bigDLModule.push(com.intel.analytics.bigdl.serialization.BigDLModule.decode(reader, reader.uint32()));
|
||||
break;
|
||||
case 14:
|
||||
message.nameAttrList.push(com.intel.analytics.bigdl.serialization.NameAttrList.decode(reader, reader.uint32()));
|
||||
break;
|
||||
case 15:
|
||||
message.dataFormat = reader.array(message.dataFormat, () => reader.int32(), tag);
|
||||
break;
|
||||
case 16:
|
||||
message.custom.push(google.protobuf.Any.decode(reader, reader.uint32()));
|
||||
break;
|
||||
case 17:
|
||||
message.shape.push(com.intel.analytics.bigdl.serialization.Shape.decode(reader, reader.uint32()));
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
com.intel.analytics.bigdl.serialization.AttrValue.ArrayValue.prototype.size = 0;
|
||||
com.intel.analytics.bigdl.serialization.AttrValue.ArrayValue.prototype.datatype = 0;
|
||||
|
||||
com.intel.analytics.bigdl.serialization.NameAttrList = class NameAttrList {
|
||||
|
||||
constructor() {
|
||||
this.attr = {};
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new com.intel.analytics.bigdl.serialization.NameAttrList();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.name = reader.string();
|
||||
break;
|
||||
case 2:
|
||||
reader.entry(message.attr, () => reader.string(), () => com.intel.analytics.bigdl.serialization.AttrValue.decode(reader, reader.uint32()));
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
com.intel.analytics.bigdl.serialization.NameAttrList.prototype.name = "";
|
||||
|
||||
com.intel.analytics.bigdl.serialization.Shape = class Shape {
|
||||
|
||||
constructor() {
|
||||
this.shapeValue = [];
|
||||
this.shape = [];
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new com.intel.analytics.bigdl.serialization.Shape();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.shapeType = reader.int32();
|
||||
break;
|
||||
case 2:
|
||||
message.ssize = reader.int32();
|
||||
break;
|
||||
case 3:
|
||||
message.shapeValue = reader.array(message.shapeValue, () => reader.int32(), tag);
|
||||
break;
|
||||
case 4:
|
||||
message.shape.push(com.intel.analytics.bigdl.serialization.Shape.decode(reader, reader.uint32()));
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
com.intel.analytics.bigdl.serialization.Shape.prototype.shapeType = 0;
|
||||
com.intel.analytics.bigdl.serialization.Shape.prototype.ssize = 0;
|
||||
|
||||
com.intel.analytics.bigdl.serialization.Shape.ShapeType = {
|
||||
"SINGLE": 0,
|
||||
"MULTI": 1
|
||||
};
|
||||
|
||||
google.protobuf = {};
|
||||
|
||||
google.protobuf.Any = class Any {
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new google.protobuf.Any();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.type_url = reader.string();
|
||||
break;
|
||||
case 2:
|
||||
message.value = reader.bytes();
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
google.protobuf.Any.prototype.type_url = "";
|
||||
google.protobuf.Any.prototype.value = new Uint8Array([]);
|
||||
@@ -0,0 +1,302 @@
|
||||
|
||||
// Experimental
|
||||
|
||||
const bigdl = {};
|
||||
|
||||
bigdl.ModelFactory = class {
|
||||
|
||||
async match(context) {
|
||||
const tags = await context.tags('pb');
|
||||
if (tags.has(2) && tags.has(7) && tags.has(8) &&
|
||||
tags.has(9) && tags.has(10) && tags.has(11) && tags.has(12)) {
|
||||
return context.set('bigdl');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async open(context) {
|
||||
bigdl.proto = await context.require('./bigdl-proto');
|
||||
bigdl.proto = bigdl.proto.com.intel.analytics.bigdl.serialization;
|
||||
let module = null;
|
||||
try {
|
||||
// https://github.com/intel-analytics/BigDL/blob/master/spark/dl/src/main/resources/serialization/bigdl.proto
|
||||
const reader = await context.read('protobuf.binary');
|
||||
module = bigdl.proto.BigDLModule.decode(reader);
|
||||
} catch (error) {
|
||||
const message = error && error.message ? error.message : error.toString();
|
||||
throw new bigdl.Error(`File format is not bigdl.BigDLModule (${message.replace(/\.$/, '')}).`);
|
||||
}
|
||||
const metadata = await context.metadata('bigdl-metadata.json');
|
||||
return new bigdl.Model(metadata, module);
|
||||
}
|
||||
};
|
||||
|
||||
bigdl.Model = class {
|
||||
|
||||
constructor(metadata, module) {
|
||||
const version = module && module.version ? module.version : '';
|
||||
this.format = `BigDL${version ? ` v${version}` : ''}`;
|
||||
this.modules = [new bigdl.Graph(metadata, module)];
|
||||
}
|
||||
};
|
||||
|
||||
bigdl.Graph = class {
|
||||
|
||||
constructor(metadata, module) {
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.nodes = [];
|
||||
this.description = module.moduleType;
|
||||
const tensors = module.attr && module.attr.global_storage && module.attr.global_storage.nameAttrListValue && module.attr.global_storage.nameAttrListValue.attr ? module.attr.global_storage.nameAttrListValue.attr : {};
|
||||
const values = new Map();
|
||||
values.map = (name) => {
|
||||
if (!values.has(name)) {
|
||||
values.set(name, new bigdl.Value(name));
|
||||
}
|
||||
return values.get(name);
|
||||
};
|
||||
const loadModule = (metadata, module, tensors) => {
|
||||
switch (module.moduleType) {
|
||||
case 'com.intel.analytics.bigdl.nn.StaticGraph':
|
||||
case 'com.intel.analytics.bigdl.nn.Sequential': {
|
||||
for (const submodule of module.subModules) {
|
||||
loadModule(metadata, submodule, tensors);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'com.intel.analytics.bigdl.nn.Input': {
|
||||
const argument = new bigdl.Argument(module.name, [values.map(module.name)]);
|
||||
this.inputs.push(argument);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const node = new bigdl.Node(metadata, module, tensors, values);
|
||||
this.nodes.push(node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
loadModule(metadata, module, tensors);
|
||||
}
|
||||
};
|
||||
|
||||
bigdl.Argument = class {
|
||||
|
||||
constructor(name, value, type = null) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
}
|
||||
};
|
||||
|
||||
bigdl.Value = class {
|
||||
|
||||
constructor(name, type, initializer) {
|
||||
if (typeof name !== 'string') {
|
||||
throw new bigdl.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
|
||||
}
|
||||
this.name = name;
|
||||
this.type = !type && initializer ? initializer.type : type;
|
||||
this.initializer = initializer;
|
||||
}
|
||||
};
|
||||
|
||||
bigdl.Node = class {
|
||||
|
||||
constructor(metadata, module, tensors, values) {
|
||||
const type = module.moduleType;
|
||||
this.name = module.name;
|
||||
this.attributes = [];
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.inputs.push(new bigdl.Argument('input', module.preModules.map((id) => values.map(id))));
|
||||
this.type = metadata.type(type) || { name: type };
|
||||
const inputs = this.type && this.type.inputs ? this.type.inputs.slice() : [];
|
||||
inputs.shift();
|
||||
if (module.weight) {
|
||||
inputs.shift();
|
||||
this.inputs.push(new bigdl.Argument('weight', [
|
||||
new bigdl.Value('', null, new bigdl.Tensor(module.weight, tensors))
|
||||
]));
|
||||
}
|
||||
if (module.bias) {
|
||||
inputs.shift();
|
||||
this.inputs.push(new bigdl.Argument('bias', [
|
||||
new bigdl.Value('', null, new bigdl.Tensor(module.bias, tensors))
|
||||
]));
|
||||
}
|
||||
if (module.parameters && module.parameters.length > 0) {
|
||||
for (const parameter of module.parameters) {
|
||||
const input = inputs.shift();
|
||||
const inputName = input ? input.name : this.inputs.length.toString();
|
||||
this.inputs.push(new bigdl.Argument(inputName, [
|
||||
new bigdl.Value('', null, new bigdl.Tensor(parameter, tensors))
|
||||
]));
|
||||
}
|
||||
}
|
||||
for (const [key, obj] of Object.entries(module.attr)) {
|
||||
if (key === 'module_numerics' || key === 'module_tags') {
|
||||
continue;
|
||||
}
|
||||
if (obj.dataType === bigdl.proto.DataType.TENSOR) {
|
||||
if (obj.value) {
|
||||
this.inputs.push(new bigdl.Argument(key, [new bigdl.Value('', null, new bigdl.Tensor(obj.tensorValue, tensors))]));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (obj.dataType === bigdl.proto.DataType.REGULARIZER && obj.value === undefined) {
|
||||
continue;
|
||||
}
|
||||
if (obj.dataType === bigdl.proto.DataType.ARRAY_VALUE && obj.arrayValue.datatype === bigdl.proto.DataType.TENSOR) {
|
||||
this.inputs.push(new bigdl.Argument(key, obj.arrayValue.tensor.map((tensor) => new bigdl.Value('', null, new bigdl.Tensor(tensor, tensors)))));
|
||||
continue;
|
||||
}
|
||||
let type = null;
|
||||
let value = null;
|
||||
switch (obj.dataType) {
|
||||
case bigdl.proto.DataType.INT32: {
|
||||
type = 'int32';
|
||||
value = obj.int32Value;
|
||||
break;
|
||||
}
|
||||
case bigdl.proto.DataType.FLOAT: {
|
||||
type = 'float32';
|
||||
value = obj.floatValue;
|
||||
break;
|
||||
}
|
||||
case bigdl.proto.DataType.DOUBLE: {
|
||||
type = 'float64';
|
||||
value = obj.doubleValue;
|
||||
break;
|
||||
}
|
||||
case bigdl.proto.DataType.BOOL: {
|
||||
type = 'boolean';
|
||||
value = obj.boolValue;
|
||||
break;
|
||||
}
|
||||
case bigdl.proto.DataType.REGULARIZER: {
|
||||
value = obj.value;
|
||||
break;
|
||||
}
|
||||
case bigdl.proto.DataType.MODULE: {
|
||||
value = obj.bigDLModule;
|
||||
break;
|
||||
}
|
||||
case bigdl.proto.DataType.NAME_ATTR_LIST: {
|
||||
value = value.nameAttrListValue;
|
||||
break;
|
||||
}
|
||||
case bigdl.proto.DataType.ARRAY_VALUE: {
|
||||
switch (obj.arrayValue.datatype) {
|
||||
case bigdl.proto.DataType.INT32: {
|
||||
type = 'int32[]';
|
||||
value = obj.arrayValue.i32;
|
||||
break;
|
||||
}
|
||||
case bigdl.proto.DataType.FLOAT: {
|
||||
type = 'float32[]';
|
||||
value = obj.arrayValue.flt;
|
||||
break;
|
||||
}
|
||||
case bigdl.proto.DataType.STRING: {
|
||||
type = 'string[]';
|
||||
value = obj.arrayValue.str;
|
||||
break;
|
||||
}
|
||||
case bigdl.proto.DataType.TENSOR: {
|
||||
type = 'tensor[]';
|
||||
value = obj.arrayValue.tensor;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new bigdl.Error(`Unsupported attribute array data type '${obj.arrayValue.datatype}'.`);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case bigdl.proto.DataType.DATA_FORMAT: {
|
||||
switch (obj.dataFormatValue) {
|
||||
case 0: value = 'NCHW'; break;
|
||||
case 1: value = 'NHWC'; break;
|
||||
default: throw new bigdl.Error(`Unsupported data format '${obj.dataFormatValue}'.`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new bigdl.Error(`Unsupported attribute data type '${obj.dataType}'.`);
|
||||
}
|
||||
}
|
||||
const argument = new bigdl.Argument(key, value, type);
|
||||
this.attributes.push(argument);
|
||||
}
|
||||
const output = this.name || this.type + module.namePostfix;
|
||||
this.outputs.push(new bigdl.Argument('output', [values.map(output)]));
|
||||
}
|
||||
};
|
||||
|
||||
bigdl.Tensor = class {
|
||||
|
||||
constructor(tensor /*, tensors */) {
|
||||
this.type = new bigdl.TensorType(tensor.datatype, new bigdl.TensorShape(tensor.size));
|
||||
/*
|
||||
if (tensor && tensor.id && tensors && tensors[tensor.id] && tensors[tensor.id].tensorValue && tensors[tensor.id].tensorValue.storage) {
|
||||
const storage = tensors[tensor.id].tensorValue.storage;
|
||||
switch (this.type.dataType) {
|
||||
case 'float32':
|
||||
if (storage.bytes_data && storage.bytes_data.length > 0) {
|
||||
this.values = storage.bytes_data[0];
|
||||
this.encoding = '<';
|
||||
}
|
||||
else if (storage.float_data && storage.float_data.length > 0) {
|
||||
this.values = storage.float_data;
|
||||
this.encoding = '|';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
||||
bigdl.TensorType = class {
|
||||
|
||||
constructor(dataType, shape) {
|
||||
switch (dataType) {
|
||||
case bigdl.proto.DataType.FLOAT: this.dataType = 'float32'; break;
|
||||
case bigdl.proto.DataType.DOUBLE: this.dataType = 'float64'; break;
|
||||
default: throw new bigdl.Error(`Unsupported tensor type '${dataType}'.`);
|
||||
}
|
||||
this.shape = shape;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return (this.dataType || '?') + this.shape.toString();
|
||||
}
|
||||
};
|
||||
|
||||
bigdl.TensorShape = class {
|
||||
|
||||
constructor(dimensions) {
|
||||
this.dimensions = dimensions;
|
||||
if (!dimensions.every((dimension) => Number.isInteger(dimension))) {
|
||||
throw new bigdl.Error(`Invalid tensor shape '${JSON.stringify(dimensions)}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.dimensions ? (`[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`) : '';
|
||||
}
|
||||
};
|
||||
|
||||
bigdl.Error = class extends Error {
|
||||
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'Error loading BigDL model.';
|
||||
}
|
||||
};
|
||||
|
||||
export const ModelFactory = bigdl.ModelFactory;
|
||||
|
||||
@@ -0,0 +1,916 @@
|
||||
|
||||
import * as base from './base.js';
|
||||
|
||||
const browser = {};
|
||||
|
||||
browser.Host = class {
|
||||
|
||||
constructor() {
|
||||
this._window = window;
|
||||
this._navigator = window.navigator;
|
||||
this._document = window.document;
|
||||
this._telemetry = new base.Telemetry(this._window);
|
||||
this._window.eval = () => {
|
||||
throw new Error('window.eval() not supported.');
|
||||
};
|
||||
this._meta = {};
|
||||
for (const element of Array.from(this._document.getElementsByTagName('meta'))) {
|
||||
if (element.name !== undefined && element.name !== '' && element.content !== undefined) {
|
||||
this._meta[element.name] = this._meta[element.name] || [];
|
||||
this._meta[element.name].push(element.content);
|
||||
}
|
||||
}
|
||||
this._environment = {
|
||||
name: this._document.title,
|
||||
type: this._meta.type ? this._meta.type[0] : 'Browser',
|
||||
version: this._meta.version ? this._meta.version[0] : null,
|
||||
date: Array.isArray(this._meta.date) && this._meta.date.length > 0 && this._meta.date[0] ? new Date(`${this._meta.date[0].split(' ').join('T')}Z`) : new Date(),
|
||||
packaged: this._meta.version && this._meta.version[0] !== '0.0.0',
|
||||
platform: /(Mac|iPhone|iPod|iPad)/i.test(this._navigator.platform) ? 'darwin' : undefined,
|
||||
agent: this._navigator.userAgent.toLowerCase().indexOf('safari') !== -1 && this._navigator.userAgent.toLowerCase().indexOf('chrome') === -1 ? 'safari' : '',
|
||||
repository: this._element('logo-github').getAttribute('href'),
|
||||
menu: true
|
||||
};
|
||||
if (this.version && !/^\d+\.\d+\.\d+$/.test(this.version)) {
|
||||
throw new Error('Invalid version.');
|
||||
}
|
||||
}
|
||||
|
||||
get window() {
|
||||
return this._window;
|
||||
}
|
||||
|
||||
get document() {
|
||||
return this._document;
|
||||
}
|
||||
|
||||
get version() {
|
||||
return this._environment.version;
|
||||
}
|
||||
|
||||
get type() {
|
||||
return this._environment.type;
|
||||
}
|
||||
|
||||
async view(view) {
|
||||
const window = this.window;
|
||||
const document = this.document;
|
||||
this._view = view;
|
||||
const age = async () => {
|
||||
const days = (new Date() - new Date(this._environment.date)) / (24 * 60 * 60 * 1000);
|
||||
if (days > 180) {
|
||||
const link = this._element('logo-github').href;
|
||||
document.body.classList.remove('spinner');
|
||||
for (;;) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await this.message('Please update to the newest version.', null, 'Update');
|
||||
this.openURL(link);
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
const consent = async () => {
|
||||
if (this._getCookie('consent') || this._getCookie('_ga')) {
|
||||
return;
|
||||
}
|
||||
let consent = true;
|
||||
try {
|
||||
const text = await this._fetch('https://ipinfo.io/json', { 'Content-Type': 'application/json' }, 'utf-8', null, 2000);
|
||||
const json = JSON.parse(text);
|
||||
const countries = ['AT', 'BE', 'BG', 'HR', 'CZ', 'CY', 'DK', 'EE', 'FI', 'FR', 'DE', 'EL', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'NO', 'PL', 'PT', 'SK', 'ES', 'SE', 'GB', 'UK', 'GR', 'EU', 'RO'];
|
||||
if (json && json.country && countries.indexOf(json.country) === -1) {
|
||||
consent = false;
|
||||
}
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
if (consent) {
|
||||
document.body.classList.remove('spinner');
|
||||
await this.message('This app uses cookies to report errors and anonymous usage information.', null, 'Accept');
|
||||
}
|
||||
this._setCookie('consent', Date.now().toString(), 30);
|
||||
};
|
||||
const telemetry = async () => {
|
||||
if (this._environment.packaged) {
|
||||
window.addEventListener('error', (event) => {
|
||||
if (event instanceof window.ErrorEvent && event.error && event.error instanceof Error) {
|
||||
this.exception(event.error, true);
|
||||
} else {
|
||||
const message = event && event.message ? event.message : JSON.stringify(event);
|
||||
const error = new Error(message);
|
||||
this.exception(error, true);
|
||||
}
|
||||
});
|
||||
const measurement_id = '848W2NVWVH';
|
||||
const user = this._getCookie('_ga').replace(/^(GA1\.\d\.)*/, '');
|
||||
const session = this._getCookie(`_ga${measurement_id}`);
|
||||
await this._telemetry.start(`G-${measurement_id}`, user, session);
|
||||
this._telemetry.set('page_location', document.location && document.location.href ? document.location.href : null);
|
||||
this._telemetry.set('page_title', document.title ? document.title : null);
|
||||
this._telemetry.set('page_referrer', document.referrer ? document.referrer : null);
|
||||
this._telemetry.send('page_view', {
|
||||
app_name: this.type,
|
||||
app_version: this.version,
|
||||
});
|
||||
this._telemetry.send('scroll', {
|
||||
percent_scrolled: 90,
|
||||
app_name: this.type,
|
||||
app_version: this.version
|
||||
});
|
||||
this._setCookie('_ga', `GA1.2.${this._telemetry.get('client_id')}`, 1200);
|
||||
this._setCookie(`_ga${measurement_id}`, `GS1.1.${this._telemetry.session}`, 1200);
|
||||
}
|
||||
};
|
||||
const capabilities = async () => {
|
||||
const filter = (list) => {
|
||||
return list.filter((capability) => {
|
||||
const path = capability.split('.').reverse();
|
||||
let obj = window[path.pop()];
|
||||
while (obj && path.length > 0) {
|
||||
obj = obj[path.pop()];
|
||||
}
|
||||
return obj;
|
||||
});
|
||||
};
|
||||
const capabilities = filter(['fetch', 'DataView.prototype.getBigInt64', 'Worker', 'Array.prototype.flat']);
|
||||
this.event('browser_open', {
|
||||
browser_capabilities: capabilities.map((capability) => capability.split('.').pop()).join(',')
|
||||
});
|
||||
return Promise.resolve();
|
||||
};
|
||||
await age();
|
||||
await consent();
|
||||
await telemetry();
|
||||
await capabilities();
|
||||
}
|
||||
|
||||
async start() {
|
||||
if (this._meta.file) {
|
||||
const [url] = this._meta.file;
|
||||
if (this._view.accept(url)) {
|
||||
const identifier = Array.isArray(this._meta.identifier) && this._meta.identifier.length === 1 ? this._meta.identifier[0] : null;
|
||||
const name = this._meta.name || null;
|
||||
const status = await this._openModel(this._url(url), identifier || null, name);
|
||||
if (status === '') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
const window = this.window;
|
||||
const document = this.document;
|
||||
const search = window.location.search;
|
||||
const params = new Map(search ? new window.URLSearchParams(window.location.search) : []);
|
||||
const hash = window.location.hash ? window.location.hash.replace(/^#/, '') : '';
|
||||
const url = hash ? hash : params.get('url');
|
||||
if (url) {
|
||||
const identifier = params.get('identifier') || null;
|
||||
const location = url
|
||||
.replace(/^https:\/\/github\.com\/([\w-]*\/[\w-]*)\/blob\/([\w/\-_.]*)(\?raw=true)?$/, 'https://raw.githubusercontent.com/$1/$2')
|
||||
.replace(/^https:\/\/github\.com\/([\w-]*\/[\w-]*)\/raw\/([\w/\-_.]*)$/, 'https://raw.githubusercontent.com/$1/$2')
|
||||
.replace(/^https:\/\/huggingface.co\/(.*)\/blob\/(.*)$/, 'https://huggingface.co/$1/resolve/$2');
|
||||
if (this._view.accept(identifier || location) && location.indexOf('*') === -1) {
|
||||
const status = await this._openModel(location, identifier);
|
||||
if (status === '') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
const gist = params.get('gist');
|
||||
if (gist) {
|
||||
this._openGist(gist);
|
||||
return;
|
||||
}
|
||||
const openFileButton = this._element('open-file-button');
|
||||
const openFileDialog = this._element('open-file-dialog');
|
||||
if (openFileButton && openFileDialog) {
|
||||
openFileButton.addEventListener('click', () => {
|
||||
this.execute('open');
|
||||
});
|
||||
const mobileSafari = this.environment('platform') === 'darwin' && window.navigator.maxTouchPoints && window.navigator.maxTouchPoints > 1;
|
||||
if (!mobileSafari) {
|
||||
const extensions = new base.Metadata().extensions.map((extension) => `.${extension}`);
|
||||
openFileDialog.setAttribute('accept', extensions.join(', '));
|
||||
}
|
||||
openFileDialog.addEventListener('change', (e) => {
|
||||
if (e.target && e.target.files && e.target.files.length > 0) {
|
||||
const files = Array.from(e.target.files);
|
||||
const file = files.find((file) => this._view.accept(file.name, file.size));
|
||||
if (file) {
|
||||
this._open(file, files);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
document.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
document.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
document.body.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length > 0) {
|
||||
const files = Array.from(e.dataTransfer.files);
|
||||
const file = files.find((file) => this._view.accept(file.name, file.size));
|
||||
if (file) {
|
||||
this._open(file, files);
|
||||
}
|
||||
}
|
||||
});
|
||||
this._view.show('welcome');
|
||||
}
|
||||
|
||||
environment(name) {
|
||||
return this._environment[name];
|
||||
}
|
||||
|
||||
async require(id) {
|
||||
return import(`${id}.js`);
|
||||
}
|
||||
|
||||
worker(id) {
|
||||
const window = this.window;
|
||||
return new window.Worker(`${id}.js`, { type: 'module' });
|
||||
}
|
||||
|
||||
async save(name, extension, defaultPath) {
|
||||
return `${defaultPath}.${extension}`;
|
||||
}
|
||||
|
||||
async export(file, blob) {
|
||||
const window = this.window;
|
||||
const document = this.document;
|
||||
const element = document.createElement('a');
|
||||
element.download = file;
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
element.href = url;
|
||||
document.body.appendChild(element);
|
||||
element.click();
|
||||
document.body.removeChild(element);
|
||||
window.URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
async execute(name /*, value */) {
|
||||
switch (name) {
|
||||
case 'open': {
|
||||
const openFileDialog = this._element('open-file-dialog');
|
||||
if (openFileDialog) {
|
||||
openFileDialog.value = '';
|
||||
openFileDialog.click();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'report-issue': {
|
||||
this.openURL(`${this.environment('repository')}/issues/new`);
|
||||
break;
|
||||
}
|
||||
case 'about': {
|
||||
this._view.about();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fetch(file, encoding, base) {
|
||||
const url = base ? `${base}/${file}` : this._url(file);
|
||||
return this._fetch(url, null, encoding);
|
||||
}
|
||||
|
||||
async asset(file) {
|
||||
this._assets = this._assets || new Map();
|
||||
if (!this._assets.has(file)) {
|
||||
const url = this._url(file);
|
||||
const separator = (/\?/).test(url) ? '&' : '?';
|
||||
const tag = this.version && this.version !== '0.0.0' ? `version=${this.version}` : `cb=${new Date().getTime()}`;
|
||||
const request = this._request(`${url}${separator}${tag}`, null, 'utf-8');
|
||||
request.catch(() => this._assets.delete(file));
|
||||
this._assets.set(file, request);
|
||||
}
|
||||
return this._assets.get(file);
|
||||
}
|
||||
|
||||
openURL(url) {
|
||||
const window = this.window;
|
||||
window.location = url;
|
||||
}
|
||||
|
||||
exception(error, fatal) {
|
||||
if (this._telemetry && error) {
|
||||
const name = error.name ? `${error.name}: ` : '';
|
||||
const message = error.message ? error.message : JSON.stringify(error);
|
||||
let context = '';
|
||||
let stack = '';
|
||||
if (error.stack) {
|
||||
const format = (file, line, column) => {
|
||||
return `${file.split('\\').join('/').split('/').pop()}:${line}:${column}`;
|
||||
};
|
||||
const match = error.stack.match(/\n {4}at (.*) \((.*):(\d*):(\d*)\)/);
|
||||
if (match) {
|
||||
stack = `${match[1]} (${format(match[2], match[3], match[4])})`;
|
||||
} else {
|
||||
const match = error.stack.match(/\n {4}at (.*):(\d*):(\d*)/);
|
||||
if (match) {
|
||||
stack = `(${format(match[1], match[2], match[3])})`;
|
||||
} else {
|
||||
const match = error.stack.match(/\n {4}at (.*)\((.*)\)/);
|
||||
if (match) {
|
||||
stack = `(${format(match[1], match[2], match[3])})`;
|
||||
} else {
|
||||
const match = error.stack.match(/\s*@\s*(.*):(.*):(.*)/);
|
||||
if (match) {
|
||||
stack = `(${format(match[1], match[2], match[3])})`;
|
||||
} else {
|
||||
const match = error.stack.match(/.*\n\s*(.*)\s*/);
|
||||
if (match) {
|
||||
[, stack] = match;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error.context) {
|
||||
context = typeof error.context === 'string' ? error.context : JSON.stringify(error.context);
|
||||
}
|
||||
this._telemetry.send('exception', {
|
||||
app_name: this.type,
|
||||
app_version: this.version,
|
||||
error_name: name,
|
||||
error_message: message,
|
||||
error_context: context,
|
||||
error_stack: stack,
|
||||
error_fatal: fatal ? true : false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
event(name, params) {
|
||||
if (name && params) {
|
||||
params.app_name = this.type;
|
||||
params.app_version = this.version;
|
||||
this._telemetry.send(name, params);
|
||||
}
|
||||
}
|
||||
|
||||
async _fetch(url, headers, encoding, callback, timeout) {
|
||||
if (!url.startsWith('data:')) {
|
||||
const separator = (/\?/).test(url) ? '&' : '?';
|
||||
url = `${url}${separator}cb=${new Date().getTime()}`;
|
||||
}
|
||||
return this._request(url, headers, encoding, callback, timeout);
|
||||
}
|
||||
|
||||
async _request(url, headers, encoding, callback, timeout) {
|
||||
const window = this.window;
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = new window.XMLHttpRequest();
|
||||
if (!encoding) {
|
||||
request.responseType = 'arraybuffer';
|
||||
}
|
||||
if (timeout) {
|
||||
request.timeout = timeout;
|
||||
}
|
||||
const progress = (value) => {
|
||||
if (callback) {
|
||||
callback(value);
|
||||
}
|
||||
};
|
||||
request.onload = () => {
|
||||
progress(0);
|
||||
if (request.status === 200) {
|
||||
let value = null;
|
||||
if (request.responseType === 'arraybuffer') {
|
||||
const buffer = new Uint8Array(request.response);
|
||||
value = new base.BinaryStream(buffer);
|
||||
} else {
|
||||
value = request.responseText;
|
||||
}
|
||||
resolve(value);
|
||||
} else {
|
||||
const error = new Error(`The web request failed with status code '${request.status}'.`);
|
||||
error.context = url;
|
||||
reject(error);
|
||||
}
|
||||
};
|
||||
request.onerror = () => {
|
||||
progress(0);
|
||||
const error = new Error(`The web request failed.`);
|
||||
error.context = url;
|
||||
reject(error);
|
||||
};
|
||||
request.ontimeout = () => {
|
||||
progress(0);
|
||||
request.abort();
|
||||
const error = new Error('The web request timed out.', 'timeout', url);
|
||||
error.context = url;
|
||||
reject(error);
|
||||
};
|
||||
request.onprogress = (e) => {
|
||||
if (e && e.lengthComputable) {
|
||||
progress(e.loaded / e.total * 100);
|
||||
}
|
||||
};
|
||||
request.open('GET', url, true);
|
||||
if (headers) {
|
||||
for (const [name, value] of Object.entries(headers)) {
|
||||
request.setRequestHeader(name, value);
|
||||
}
|
||||
}
|
||||
request.send();
|
||||
});
|
||||
}
|
||||
|
||||
_url(file) {
|
||||
if (file.startsWith('./')) {
|
||||
file = file.substring(2);
|
||||
} else if (file.startsWith('/')) {
|
||||
file = file.substring(1);
|
||||
}
|
||||
const window = this.window;
|
||||
const location = window.location;
|
||||
const pathname = location.pathname.endsWith('/') ? location.pathname : `${location.pathname.split('/').slice(0, -1).join('/')}/`;
|
||||
return `${location.protocol}//${location.host}${pathname}${file}`;
|
||||
}
|
||||
|
||||
async _openModel(url, identifier, name) {
|
||||
this._view.show('welcome spinner');
|
||||
let context = null;
|
||||
try {
|
||||
const progress = (value) => {
|
||||
this._view.progress(value);
|
||||
};
|
||||
let stream = await this._fetch(url, null, null, progress);
|
||||
if (url.startsWith('https://raw.githubusercontent.com/') && stream.length < 150) {
|
||||
const buffer = stream.peek();
|
||||
const content = Array.from(buffer).map((c) => String.fromCodePoint(c)).join('');
|
||||
if (content.split('\n')[0] === 'version https://git-lfs.github.com/spec/v1') {
|
||||
url = url.replace('https://raw.githubusercontent.com/', 'https://media.githubusercontent.com/media/');
|
||||
stream = await this._fetch(url, null, null, progress);
|
||||
}
|
||||
}
|
||||
context = new browser.Context(this, url, identifier, name, stream);
|
||||
this._telemetry.set('session_engaged', 1);
|
||||
} catch (error) {
|
||||
await this._view.error(error, 'Model load request failed.');
|
||||
this._view.show('welcome');
|
||||
return null;
|
||||
}
|
||||
return await this._openContext(context);
|
||||
}
|
||||
|
||||
async _open(file, files) {
|
||||
this._view.show('welcome spinner');
|
||||
const context = new browser.BrowserFileContext(this, file, files);
|
||||
try {
|
||||
await context.open();
|
||||
await this._openContext(context);
|
||||
} catch (error) {
|
||||
await this._view.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async _openGist(gist) {
|
||||
this._view.show('welcome spinner');
|
||||
const url = `https://api.github.com/gists/${gist}`;
|
||||
try {
|
||||
const text = await this._fetch(url, { 'Content-Type': 'application/json' }, 'utf-8');
|
||||
const json = JSON.parse(text);
|
||||
let message = json.message;
|
||||
let file = null;
|
||||
if (!message) {
|
||||
file = Object.values(json.files).find((file) => this._view.accept(file.filename));
|
||||
if (!file) {
|
||||
message = 'Gist does not contain a model file.';
|
||||
}
|
||||
}
|
||||
if (message) {
|
||||
const error = new Error(message);
|
||||
error.name = 'Error while loading Gist.';
|
||||
throw error;
|
||||
}
|
||||
const identifier = file.filename;
|
||||
const encoder = new TextEncoder();
|
||||
const buffer = encoder.encode(file.content);
|
||||
const stream = new base.BinaryStream(buffer);
|
||||
const context = new browser.Context(this, '', identifier, null, stream);
|
||||
await this._openContext(context);
|
||||
} catch (error) {
|
||||
await this._view.error(error, 'Error while loading Gist.');
|
||||
this._view.show('welcome');
|
||||
}
|
||||
}
|
||||
|
||||
async _openContext(context) {
|
||||
const document = this.document;
|
||||
this._telemetry.set('session_engaged', 1);
|
||||
try {
|
||||
const attachment = await this._view.attach(context);
|
||||
if (attachment) {
|
||||
this._view.show(null);
|
||||
return 'context-open-attachment';
|
||||
}
|
||||
const model = await this._view.open(context);
|
||||
if (model) {
|
||||
this._view.show(null);
|
||||
document.title = context.name || context.identifier;
|
||||
return '';
|
||||
}
|
||||
document.title = '';
|
||||
return 'context-open-failed';
|
||||
} catch (error) {
|
||||
await this._view.error(error, error.name);
|
||||
return 'context-open-error';
|
||||
}
|
||||
}
|
||||
|
||||
_setCookie(name, value, days) {
|
||||
const window = this.window;
|
||||
const document = this.document;
|
||||
document.cookie = `${name}=; Max-Age=0`;
|
||||
const location = window.location;
|
||||
const domain = location && location.hostname && location.hostname.indexOf('.') !== -1 ? `;domain=.${location.hostname.split('.').slice(-2).join('.')}` : '';
|
||||
const date = new Date();
|
||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||
document.cookie = `${name}=${value}${domain};path=/;expires=${date.toUTCString()}`;
|
||||
}
|
||||
|
||||
_getCookie(name) {
|
||||
const document = this.document;
|
||||
for (const cookie of document.cookie.split(';')) {
|
||||
const entry = cookie.split('=');
|
||||
if (entry[0].trim() === name) {
|
||||
return entry[1].trim();
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
get(name) {
|
||||
const window = this.window;
|
||||
try {
|
||||
if (typeof window.localStorage !== 'undefined') {
|
||||
const content = window.localStorage.getItem(name);
|
||||
return JSON.parse(content);
|
||||
}
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
set(name, value) {
|
||||
const window = this.window;
|
||||
try {
|
||||
if (typeof window.localStorage !== 'undefined') {
|
||||
window.localStorage.setItem(name, JSON.stringify(value));
|
||||
}
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
}
|
||||
|
||||
delete(name) {
|
||||
const window = this.window;
|
||||
try {
|
||||
if (typeof window.localStorage !== 'undefined') {
|
||||
window.localStorage.removeItem(name);
|
||||
}
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
}
|
||||
|
||||
_element(id) {
|
||||
const document = this.document;
|
||||
return document.getElementById(id);
|
||||
}
|
||||
|
||||
update() {
|
||||
}
|
||||
|
||||
async message(message, alert, action) {
|
||||
return new Promise((resolve) => {
|
||||
const document = this.document;
|
||||
const type = document.body.getAttribute('class');
|
||||
this._element('message-text').innerText = message || '';
|
||||
const button = this._element('message-button');
|
||||
if (action) {
|
||||
button.style.removeProperty('display');
|
||||
button.innerText = action;
|
||||
button.onclick = () => {
|
||||
button.onclick = null;
|
||||
document.body.setAttribute('class', type);
|
||||
resolve(0);
|
||||
};
|
||||
} else {
|
||||
button.style.display = 'none';
|
||||
button.onclick = null;
|
||||
}
|
||||
if (alert) {
|
||||
document.body.setAttribute('class', 'alert');
|
||||
} else {
|
||||
document.body.classList.add('notification');
|
||||
document.body.classList.remove('default');
|
||||
}
|
||||
if (action) {
|
||||
button.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
browser.BrowserFileContext = class {
|
||||
|
||||
constructor(host, file, blobs) {
|
||||
this._host = host;
|
||||
this._file = file;
|
||||
this._blobs = {};
|
||||
for (const blob of blobs) {
|
||||
this._blobs[blob.name] = blob;
|
||||
}
|
||||
}
|
||||
|
||||
get identifier() {
|
||||
return this._file.name;
|
||||
}
|
||||
|
||||
get stream() {
|
||||
return this._stream;
|
||||
}
|
||||
|
||||
async asset(file) {
|
||||
return this._host.asset(file);
|
||||
}
|
||||
|
||||
async fetch(file, encoding, basename) {
|
||||
if (basename !== undefined) {
|
||||
return this._host.fetch(file, encoding, basename);
|
||||
}
|
||||
const blob = this._blobs[file];
|
||||
if (!blob) {
|
||||
throw new Error(`File not found '${file}'.`);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const window = this._host.window;
|
||||
const reader = new window.FileReader();
|
||||
const size = 0x10000000;
|
||||
let position = 0;
|
||||
const chunks = [];
|
||||
reader.onload = (e) => {
|
||||
if (encoding) {
|
||||
resolve(e.target.result);
|
||||
} else {
|
||||
const buffer = new Uint8Array(e.target.result);
|
||||
if (position === 0 && buffer.length === blob.size) {
|
||||
const stream = new base.BinaryStream(buffer);
|
||||
resolve(stream);
|
||||
} else {
|
||||
chunks.push(buffer);
|
||||
position += buffer.length;
|
||||
if (position < blob.size) {
|
||||
const slice = blob.slice(position, Math.min(position + size, blob.size));
|
||||
reader.readAsArrayBuffer(slice);
|
||||
} else {
|
||||
const stream = new browser.FileStream(chunks, size, 0, position);
|
||||
resolve(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
reader.onerror = (event) => {
|
||||
event = event || this._host.window.event;
|
||||
let message = '';
|
||||
const error = event.target.error;
|
||||
switch (error.code) {
|
||||
case error.NOT_FOUND_ERR:
|
||||
message = `File not found '${file}'.`;
|
||||
break;
|
||||
case error.NOT_READABLE_ERR:
|
||||
message = `File not readable '${file}'.`;
|
||||
break;
|
||||
case error.SECURITY_ERR:
|
||||
message = `File access denied '${file}'.`;
|
||||
break;
|
||||
default:
|
||||
message = error.message ? error.message : `File read '${error.code}' error '${file}'.`;
|
||||
break;
|
||||
}
|
||||
reject(new Error(message));
|
||||
};
|
||||
if (encoding === 'utf-8') {
|
||||
reader.readAsText(blob, encoding);
|
||||
} else {
|
||||
const slice = blob.slice(position, Math.min(position + size, blob.size));
|
||||
reader.readAsArrayBuffer(slice);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async require(id) {
|
||||
return this._host.require(id);
|
||||
}
|
||||
|
||||
error(error, fatal) {
|
||||
this._host.exception(error, fatal);
|
||||
}
|
||||
|
||||
async open() {
|
||||
this._stream = await this.fetch(this._file.name, null);
|
||||
}
|
||||
};
|
||||
|
||||
browser.FileStream = class {
|
||||
|
||||
constructor(chunks, size, start, length) {
|
||||
this._chunks = chunks;
|
||||
this._size = size;
|
||||
this._start = start;
|
||||
this._length = length;
|
||||
this._position = 0;
|
||||
}
|
||||
|
||||
get position() {
|
||||
return this._position;
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this._length;
|
||||
}
|
||||
|
||||
stream(length) {
|
||||
const file = new browser.FileStream(this._chunks, this._size, this._start + this._position, length);
|
||||
this.skip(length);
|
||||
return file;
|
||||
}
|
||||
|
||||
seek(position) {
|
||||
this._position = position >= 0 ? position : this._length + position;
|
||||
}
|
||||
|
||||
skip(offset) {
|
||||
this._position += offset;
|
||||
if (this._position > this._length) {
|
||||
throw new Error(`Expected ${this._position - this._length} more bytes. The file might be corrupted. Unexpected end of file.`);
|
||||
}
|
||||
}
|
||||
|
||||
peek(length) {
|
||||
length = length === undefined ? this._length - this._position : length;
|
||||
if (length < 0x10000000) {
|
||||
const position = this._fill(length);
|
||||
this._position -= length;
|
||||
return this._buffer.subarray(position, position + length);
|
||||
}
|
||||
const position = this._start + this._position;
|
||||
if (position % this._size === 0) {
|
||||
const index = Math.floor(position / this._size);
|
||||
const chunk = this._chunks[index];
|
||||
if (chunk && chunk.length === length) {
|
||||
return chunk;
|
||||
}
|
||||
}
|
||||
const buffer = new Uint8Array(length);
|
||||
this._read(buffer, position);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
read(length) {
|
||||
length = length === undefined ? this._length - this._position : length;
|
||||
if (length < 0x10000000) {
|
||||
const position = this._fill(length);
|
||||
return this._buffer.slice(position, position + length);
|
||||
}
|
||||
const position = this._start + this._position;
|
||||
this.skip(length);
|
||||
if (position % this._size === 0) {
|
||||
const index = Math.floor(position / this._size);
|
||||
const chunk = this._chunks[index];
|
||||
if (chunk && chunk.length === length) {
|
||||
return chunk;
|
||||
}
|
||||
}
|
||||
const buffer = new Uint8Array(length);
|
||||
this._read(buffer, position);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
_fill(length) {
|
||||
if (this._position + length > this._length) {
|
||||
throw new Error(`Expected ${this._position + length - this._length} more bytes. The file might be corrupted. Unexpected end of file.`);
|
||||
}
|
||||
if (!this._buffer || this._position < this._offset || this._position + length > this._offset + this._buffer.length) {
|
||||
this._offset = this._start + this._position;
|
||||
const length = Math.min(0x10000000, this._start + this._length - this._offset);
|
||||
if (!this._buffer || length !== this._buffer.length) {
|
||||
this._buffer = new Uint8Array(length);
|
||||
}
|
||||
this._read(this._buffer, this._offset);
|
||||
}
|
||||
const position = this._start + this._position - this._offset;
|
||||
this._position += length;
|
||||
return position;
|
||||
}
|
||||
|
||||
_read(buffer, offset) {
|
||||
let index = Math.floor(offset / this._size);
|
||||
offset -= index * this._size;
|
||||
const chunk = this._chunks[index++];
|
||||
let destination = Math.min(chunk.length - offset, buffer.length);
|
||||
buffer.set(chunk.subarray(offset, offset + destination), 0);
|
||||
while (destination < buffer.length) {
|
||||
const chunk = this._chunks[index++];
|
||||
const size = Math.min(this._size, buffer.length - destination);
|
||||
buffer.set(chunk.subarray(0, size), destination);
|
||||
destination += size;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
browser.Context = class {
|
||||
|
||||
constructor(host, url, identifier, name, stream) {
|
||||
this._host = host;
|
||||
this._name = name;
|
||||
this._stream = stream;
|
||||
const parts = url.split('?')[0].split('/');
|
||||
this._identifier = parts.pop();
|
||||
this._base = parts.join('/');
|
||||
if (identifier) {
|
||||
this._identifier = identifier;
|
||||
}
|
||||
}
|
||||
|
||||
get identifier() {
|
||||
return this._identifier;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get stream() {
|
||||
return this._stream;
|
||||
}
|
||||
|
||||
async asset(file) {
|
||||
return this._host.asset(file);
|
||||
}
|
||||
|
||||
async fetch(file, encoding, base) {
|
||||
base = base === undefined ? this._base : base;
|
||||
return this._host.fetch(file, encoding, base);
|
||||
}
|
||||
|
||||
async require(id) {
|
||||
return this._host.require(id);
|
||||
}
|
||||
|
||||
error(error, fatal) {
|
||||
this._host.exception(error, fatal);
|
||||
}
|
||||
};
|
||||
|
||||
if (!('scrollBehavior' in window.document.documentElement.style)) {
|
||||
const __scrollTo__ = window.Element.prototype.scrollTo;
|
||||
window.Element.prototype.scrollTo = function(...args) {
|
||||
const [options] = args;
|
||||
if (options !== undefined) {
|
||||
if (options === null || typeof options !== 'object' || options.behavior === undefined || options.behavior === 'auto' || options.behavior === 'instant') {
|
||||
if (__scrollTo__) {
|
||||
__scrollTo__.apply(this, args);
|
||||
}
|
||||
} else {
|
||||
const now = () => window.performance && window.performance.now ? window.performance.now() : Date.now();
|
||||
const ease = (k) => 0.5 * (1 - Math.cos(Math.PI * k));
|
||||
const step = (context) => {
|
||||
const value = ease(Math.min((now() - context.startTime) / 468, 1));
|
||||
const x = context.startX + (context.x - context.startX) * value;
|
||||
const y = context.startY + (context.y - context.startY) * value;
|
||||
context.element.scrollLeft = x;
|
||||
context.element.scrollTop = y;
|
||||
if (x !== context.x || y !== context.y) {
|
||||
window.requestAnimationFrame(step.bind(window, context));
|
||||
}
|
||||
};
|
||||
const context = {
|
||||
element: this,
|
||||
x: typeof options.left === 'undefined' ? this.scrollLeft : ~~options.left,
|
||||
y: typeof options.top === 'undefined' ? this.scrollTop : ~~options.top,
|
||||
startX: this.scrollLeft,
|
||||
startY: this.scrollTop,
|
||||
startTime: now()
|
||||
};
|
||||
step(context);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined' && window.exports) {
|
||||
window.exports.browser = browser;
|
||||
}
|
||||
|
||||
export const Host = browser.Host;
|
||||
@@ -0,0 +1,462 @@
|
||||
[
|
||||
{
|
||||
"name": "Accuracy",
|
||||
"inputs": [
|
||||
{ "name": "predictions" },
|
||||
{ "name": "labels" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "AnnotatedData",
|
||||
"category": "Data",
|
||||
"outputs": [
|
||||
{ "name": "data" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "BatchNorm",
|
||||
"category": "Normalization",
|
||||
"attributes": [
|
||||
{ "name": "use_global_stats", "type": "boolean", "visible": false },
|
||||
{ "name": "eps", "type": "float32", "default": 0.00001 }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "gamma" },
|
||||
{ "name": "beta" },
|
||||
{ "name": "mean" },
|
||||
{ "name": "variance" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "BN",
|
||||
"category": "Normalization",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ColorConv",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Concat",
|
||||
"category": "Tensor",
|
||||
"inputs": [
|
||||
{ "name": "inputs", "option": "variadic" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ContrastiveLossParameter",
|
||||
"attributes": [
|
||||
{ "name": "margin", "default": 1 },
|
||||
{ "name": "legacy_version", "default": false }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Convolution",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "bias_term", "visible": false },
|
||||
{ "name": "weight_filler", "visible": false },
|
||||
{ "name": "bias_filler", "visible": false },
|
||||
{ "name": "num_output", "visible": false },
|
||||
{ "name": "pad", "default": [ 0 ] },
|
||||
{ "name": "kernel_size", "default": [] },
|
||||
{ "name": "stride", "default": [ 1 ] },
|
||||
{ "name": "dilation", "default": [] },
|
||||
{ "name": "group", "default": 1 }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "filter" },
|
||||
{ "name": "bias" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ConvolutionDepthwise",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "pad", "default": [ 0 ] },
|
||||
{ "name": "kernel_size", "default": [] },
|
||||
{ "name": "stride", "default": [ 1 ] },
|
||||
{ "name": "bias_term", "visible": false },
|
||||
{ "name": "weight_filler", "visible": false },
|
||||
{ "name": "bias_filler", "visible": false },
|
||||
{ "name": "num_output", "visible": false }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "filter" },
|
||||
{ "name": "bias" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Crop",
|
||||
"category": "Data",
|
||||
"inputs": [
|
||||
{ "name": "data" },
|
||||
{ "name": "size" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Data",
|
||||
"category": "Data",
|
||||
"outputs": [
|
||||
{ "name": "data" },
|
||||
{ "name": "label" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Deconvolution",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "bias_term", "visible": false },
|
||||
{ "name": "weight_filler", "visible": false },
|
||||
{ "name": "bias_filler", "visible": false },
|
||||
{ "name": "num_output", "visible": false },
|
||||
{ "name": "pad", "default": [] },
|
||||
{ "name": "kernel_size", "default": [] },
|
||||
{ "name": "stride", "default": [] },
|
||||
{ "name": "dilation", "default": [] }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "filter" },
|
||||
{ "name": "bias" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "DepthwiseConvolution",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "bias_term", "visible": false },
|
||||
{ "name": "weight_filler", "visible": false },
|
||||
{ "name": "bias_filler", "visible": false },
|
||||
{ "name": "num_output", "visible": false }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "filter" },
|
||||
{ "name": "bias" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Dropout",
|
||||
"category": "Dropout",
|
||||
"attributes": [
|
||||
{ "name": "dropout_ratio", "default": 0.5 }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "DummyData",
|
||||
"category": "Data",
|
||||
"outputs": [
|
||||
{ "name": "data" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Eltwise",
|
||||
"attributes": [
|
||||
{ "name": "operation", "type": "EltwiseParameter.EltwiseOp", "default": 1 },
|
||||
{ "name": "coeff", "type": "float32[]", "default": [] },
|
||||
{ "name": "stable_prod_grad", "type": "boolean", "default": true }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "inputs", "option": "variadic" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "EuclideanLoss",
|
||||
"inputs": [
|
||||
{ "name": "predictions" },
|
||||
{ "name": "targets" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Flatten",
|
||||
"category": "Shape",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "HDF5Data",
|
||||
"category": "Data",
|
||||
"outputs": [
|
||||
{ "name": "data" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ImageData",
|
||||
"category": "Data",
|
||||
"outputs": [
|
||||
{ "name": "data" },
|
||||
{ "name": "label" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "InnerProduct",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "bias_term", "visible": false },
|
||||
{ "name": "weight_filler", "visible": false },
|
||||
{ "name": "bias_filler", "visible": false },
|
||||
{ "name": "num_output", "visible": false }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "weights" },
|
||||
{ "name": "bias" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "LRN",
|
||||
"category": "Normalization",
|
||||
"attributes": [
|
||||
{ "name": "local_size", "type": "uint32", "default": 5 },
|
||||
{ "name": "alpha", "type": "float32", "default": 0.0001 },
|
||||
{ "name": "beta", "type": "float32", "default": 0.75 }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "LSTM",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "weight_filler", "visible": false },
|
||||
{ "name": "bias_filler", "visible": false },
|
||||
{ "name": "num_output", "visible": false }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "weights" },
|
||||
{ "name": "h_0" },
|
||||
{ "name": "c_0" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" },
|
||||
{ "name": "h_T" },
|
||||
{ "name": "c_T" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Parameter",
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Permute",
|
||||
"category": "Shape",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Pooling",
|
||||
"category": "Pool",
|
||||
"attributes": [
|
||||
{ "name": "pool", "type": "PoolingParameter.PoolMethod", "default": 0 },
|
||||
{ "name": "engine", "type": "PoolingParameter.Engine", "default": 0 }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "PReLU",
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "slope" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Python"
|
||||
},
|
||||
{
|
||||
"name": "ReLU",
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ReLU6",
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Reshape",
|
||||
"category": "Shape",
|
||||
"inputs": [
|
||||
{ "name": "data" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "reshaped" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Scale",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "filler", "visible": false },
|
||||
{ "name": "bias_term", "visible": false },
|
||||
{ "name": "bias_filler", "visible": false }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "scale" },
|
||||
{ "name": "bias" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Sigmoid",
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Slice",
|
||||
"category": "Tensor",
|
||||
"attributes": [
|
||||
{ "name": "axis", "default": 1 }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "outputs", "option": "variadic" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Softmax",
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SoftmaxLoss",
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "labels" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SoftmaxWithLoss",
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "labels" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Split",
|
||||
"category": "Tensor",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "outputs", "option": "variadic" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "WindowData",
|
||||
"category": "Data",
|
||||
"outputs": [
|
||||
{ "name": "data" },
|
||||
{ "name": "label" }
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,590 @@
|
||||
|
||||
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;
|
||||
@@ -0,0 +1,493 @@
|
||||
|
||||
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-next-line no-await-in-loop
|
||||
predict = await context.fetch(name);
|
||||
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;
|
||||
@@ -0,0 +1,266 @@
|
||||
|
||||
export const NCatBoostFbs = {};
|
||||
|
||||
NCatBoostFbs.TGuid = class TGuid {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new NCatBoostFbs.TGuid();
|
||||
$.dw0 = reader.uint32(position + 0);
|
||||
$.dw1 = reader.uint32(position + 4);
|
||||
$.dw2 = reader.uint32(position + 8);
|
||||
$.dw3 = reader.uint32(position + 12);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
NCatBoostFbs.ENanValueTreatment = {
|
||||
AsIs: 0, '0': 'AsIs',
|
||||
AsFalse: 1, '1': 'AsFalse',
|
||||
AsTrue: 2, '2': 'AsTrue'
|
||||
};
|
||||
|
||||
NCatBoostFbs.TFloatFeature = class TFloatFeature {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new NCatBoostFbs.TFloatFeature();
|
||||
$.HasNans = reader.bool_(position, 4, false);
|
||||
$.Index = reader.int32_(position, 6, -1);
|
||||
$.FlatIndex = reader.int32_(position, 8, -1);
|
||||
$.Borders = reader.array(position, 10, Float32Array);
|
||||
$.FeatureId = reader.string_(position, 12, null);
|
||||
$.NanValueTreatment = reader.int8_(position, 14, 0);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
NCatBoostFbs.TCatFeature = class TCatFeature {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new NCatBoostFbs.TCatFeature();
|
||||
$.Index = reader.int32_(position, 4, -1);
|
||||
$.FlatIndex = reader.int32_(position, 6, -1);
|
||||
$.FeatureId = reader.string_(position, 8, null);
|
||||
$.UsedInModel = reader.bool_(position, 10, true);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
NCatBoostFbs.TTextFeature = class TTextFeature {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new NCatBoostFbs.TTextFeature();
|
||||
$.Index = reader.int32_(position, 4, -1);
|
||||
$.FlatIndex = reader.int32_(position, 6, -1);
|
||||
$.FeatureId = reader.string_(position, 8, null);
|
||||
$.UsedInModel = reader.bool_(position, 10, true);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
NCatBoostFbs.TEmbeddingFeature = class TEmbeddingFeature {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new NCatBoostFbs.TEmbeddingFeature();
|
||||
$.Index = reader.int32_(position, 4, -1);
|
||||
$.FlatIndex = reader.int32_(position, 6, -1);
|
||||
$.FeatureId = reader.string_(position, 8, null);
|
||||
$.Dimension = reader.int32_(position, 10, 0);
|
||||
$.UsedInModel = reader.bool_(position, 12, true);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
NCatBoostFbs.ESourceFeatureType = {
|
||||
Text: 0, '0': 'Text',
|
||||
Embedding: 1, '1': 'Embedding'
|
||||
};
|
||||
|
||||
NCatBoostFbs.TEstimatedFeature = class TEstimatedFeature {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new NCatBoostFbs.TEstimatedFeature();
|
||||
$.SourceFeatureIndex = reader.int32_(position, 4, -1);
|
||||
$.CalcerId = reader.struct(position, 6, NCatBoostFbs.TGuid);
|
||||
$.LocalIndex = reader.int32_(position, 8, -1);
|
||||
$.Borders = reader.array(position, 10, Float32Array);
|
||||
$.SourceFeatureType = reader.int8_(position, 12, 0);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
NCatBoostFbs.TOneHotFeature = class TOneHotFeature {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new NCatBoostFbs.TOneHotFeature();
|
||||
$.Index = reader.int32_(position, 4, -1);
|
||||
$.Values = reader.array(position, 6, Int32Array);
|
||||
$.StringValues = reader.strings_(position, 8);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
NCatBoostFbs.TFloatSplit = class TFloatSplit {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new NCatBoostFbs.TFloatSplit();
|
||||
$.Index = reader.int32(position + 0);
|
||||
$.Border = reader.float32(position + 4);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
NCatBoostFbs.TOneHotSplit = class TOneHotSplit {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new NCatBoostFbs.TOneHotSplit();
|
||||
$.Index = reader.int32(position + 0);
|
||||
$.Value = reader.int32(position + 4);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
NCatBoostFbs.TFeatureCombination = class TFeatureCombination {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new NCatBoostFbs.TFeatureCombination();
|
||||
$.CatFeatures = reader.array(position, 4, Int32Array);
|
||||
$.FloatSplits = reader.structs(position, 6, NCatBoostFbs.TFloatSplit, 8);
|
||||
$.OneHotSplits = reader.structs(position, 8, NCatBoostFbs.TOneHotSplit, 8);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
NCatBoostFbs.ECtrType = {
|
||||
Borders: 0, '0': 'Borders',
|
||||
Buckets: 1, '1': 'Buckets',
|
||||
BinarizedTargetMeanValue: 2, '2': 'BinarizedTargetMeanValue',
|
||||
FloatTargetMeanValue: 3, '3': 'FloatTargetMeanValue',
|
||||
Counter: 4, '4': 'Counter',
|
||||
FeatureFreq: 5, '5': 'FeatureFreq'
|
||||
};
|
||||
|
||||
NCatBoostFbs.TModelCtrBase = class TModelCtrBase {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new NCatBoostFbs.TModelCtrBase();
|
||||
$.FeatureCombination = reader.table(position, 4, NCatBoostFbs.TFeatureCombination);
|
||||
$.CtrType = reader.int8_(position, 6, 0);
|
||||
$.TargetBorderClassifierIdx = reader.int32_(position, 8, 0);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
NCatBoostFbs.TModelCtr = class TModelCtr {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new NCatBoostFbs.TModelCtr();
|
||||
$.Base = reader.table(position, 4, NCatBoostFbs.TModelCtrBase);
|
||||
$.TargetBorderIdx = reader.int32_(position, 6, 0);
|
||||
$.PriorNum = reader.float32_(position, 8, 0);
|
||||
$.PriorDenom = reader.float32_(position, 10, 1);
|
||||
$.Shift = reader.float32_(position, 12, 0);
|
||||
$.Scale = reader.float32_(position, 14, 1);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
NCatBoostFbs.TCtrFeature = class TCtrFeature {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new NCatBoostFbs.TCtrFeature();
|
||||
$.Ctr = reader.table(position, 4, NCatBoostFbs.TModelCtr);
|
||||
$.Borders = reader.array(position, 6, Float32Array);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
NCatBoostFbs.TCtrValueTable = class TCtrValueTable {
|
||||
|
||||
static create(reader) {
|
||||
return NCatBoostFbs.TCtrValueTable.decode(reader, reader.root);
|
||||
}
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new NCatBoostFbs.TCtrValueTable();
|
||||
$.ModelCtrBase = reader.table(position, 4, NCatBoostFbs.TModelCtrBase);
|
||||
$.IndexHashRaw = reader.array(position, 6, Uint8Array);
|
||||
$.CTRBlob = reader.array(position, 8, Uint8Array);
|
||||
$.CounterDenominator = reader.int32_(position, 10, 0);
|
||||
$.TargetClassesCount = reader.int32_(position, 12, 0);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
NCatBoostFbs.TKeyValue = class TKeyValue {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new NCatBoostFbs.TKeyValue();
|
||||
$.Key = reader.string_(position, 4, null);
|
||||
$.Value = reader.string_(position, 6, null);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
NCatBoostFbs.TNonSymmetricTreeStepNode = class TNonSymmetricTreeStepNode {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new NCatBoostFbs.TNonSymmetricTreeStepNode();
|
||||
$.LeftSubtreeDiff = reader.uint16(position + 0);
|
||||
$.RightSubtreeDiff = reader.uint16(position + 2);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
NCatBoostFbs.TRepackedBin = class TRepackedBin {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new NCatBoostFbs.TRepackedBin();
|
||||
$.FeatureIndex = reader.uint16(position + 0);
|
||||
$.XorMask = reader.uint8(position + 2);
|
||||
$.SplitIdx = reader.uint8(position + 3);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
NCatBoostFbs.TModelTrees = class TModelTrees {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new NCatBoostFbs.TModelTrees();
|
||||
$.ApproxDimension = reader.int32_(position, 4, 0);
|
||||
$.TreeSplits = reader.array(position, 6, Int32Array);
|
||||
$.TreeSizes = reader.array(position, 8, Int32Array);
|
||||
$.TreeStartOffsets = reader.array(position, 10, Int32Array);
|
||||
$.CatFeatures = reader.tables(position, 12, NCatBoostFbs.TCatFeature);
|
||||
$.FloatFeatures = reader.tables(position, 14, NCatBoostFbs.TFloatFeature);
|
||||
$.OneHotFeatures = reader.tables(position, 16, NCatBoostFbs.TOneHotFeature);
|
||||
$.CtrFeatures = reader.tables(position, 18, NCatBoostFbs.TCtrFeature);
|
||||
$.LeafValues = reader.array(position, 20, Float64Array);
|
||||
$.LeafWeights = reader.array(position, 22, Float64Array);
|
||||
$.NonSymmetricStepNodes = reader.structs(position, 24, NCatBoostFbs.TNonSymmetricTreeStepNode, 4);
|
||||
$.NonSymmetricNodeIdToLeafId = reader.array(position, 26, Uint32Array);
|
||||
$.TextFeatures = reader.tables(position, 28, NCatBoostFbs.TTextFeature);
|
||||
$.EstimatedFeatures = reader.tables(position, 30, NCatBoostFbs.TEstimatedFeature);
|
||||
$.Scale = reader.float64_(position, 32, 1);
|
||||
$.Bias = reader.float64_(position, 34, 0);
|
||||
$.MultiBias = reader.array(position, 36, Float64Array);
|
||||
$.RepackedBins = reader.structs(position, 38, NCatBoostFbs.TRepackedBin, 4);
|
||||
$.EmbeddingFeatures = reader.tables(position, 40, NCatBoostFbs.TEmbeddingFeature);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
NCatBoostFbs.TModelCore = class TModelCore {
|
||||
|
||||
static create(reader) {
|
||||
return NCatBoostFbs.TModelCore.decode(reader, reader.root);
|
||||
}
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new NCatBoostFbs.TModelCore();
|
||||
$.FormatVersion = reader.string_(position, 4, null);
|
||||
$.ModelTrees = reader.table(position, 6, NCatBoostFbs.TModelTrees);
|
||||
$.InfoMap = reader.tables(position, 8, NCatBoostFbs.TKeyValue);
|
||||
$.ModelPartIds = reader.strings_(position, 10);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,187 @@
|
||||
|
||||
import * as flatbuffers from './flatbuffers.js';
|
||||
import * as python from './python.js';
|
||||
|
||||
const catboost = {};
|
||||
|
||||
catboost.ModelFactory = class {
|
||||
|
||||
async match(context) {
|
||||
const stream = context.stream;
|
||||
if (stream && stream.length > 4) {
|
||||
const buffer = stream.peek(4);
|
||||
const signature = Array.from(buffer).map((c) => String.fromCharCode(c)).join('');
|
||||
if (signature === 'CBM1') {
|
||||
return context.set('catboost.flatbuffers');
|
||||
}
|
||||
}
|
||||
const obj = await context.peek('pkl');
|
||||
if (obj && obj.__class__ && obj.__class__.__module__) {
|
||||
const name = `${obj.__class__.__module__}.${obj.__class__.__name__}`;
|
||||
if (name.startsWith('catboost.') || name.startsWith('autogluon.tabular.models.catboost.')) {
|
||||
return context.set('catboost.pickle', obj);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async open(context) {
|
||||
switch (context.type) {
|
||||
case 'catboost.flatbuffers': {
|
||||
const stream = context.stream;
|
||||
const buffer = stream.peek();
|
||||
const execution = new python.Execution();
|
||||
const obj = execution.invoke('catboost.CatBoostClassifier', []);
|
||||
obj._object.flatbuffers = flatbuffers;
|
||||
obj._object.schema = await context.require('./catboost-schema');
|
||||
obj.load_model(buffer);
|
||||
return new catboost.Model(obj._object);
|
||||
}
|
||||
case 'catboost.pickle': {
|
||||
let obj = context.value;
|
||||
const name = obj && obj.__class__ && obj.__class__.__module__ && obj.__class__.__name__ ? `${obj.__class__.__module__}.${obj.__class__.__name__}` : '';
|
||||
if (name === 'autogluon.tabular.models.catboost.catboost_model.CatBoostModel') {
|
||||
obj = obj.model;
|
||||
}
|
||||
obj._object.flatbuffers = flatbuffers;
|
||||
obj._object.schema = await context.require('./catboost-schema');
|
||||
obj.load_model();
|
||||
return new catboost.Model(obj._object);
|
||||
}
|
||||
default: {
|
||||
throw new catboost.Error(`Unsupported CatBoost format '${context.type}'.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
catboost.Model = class {
|
||||
|
||||
constructor(obj) {
|
||||
this.format = 'CatBoost';
|
||||
this.metadata = [];
|
||||
for (const [name, value] of obj._get_info_map()) {
|
||||
this.metadata.push({ name, value });
|
||||
}
|
||||
this.modules = [new catboost.Graph(obj)];
|
||||
}
|
||||
};
|
||||
|
||||
catboost.Graph = class {
|
||||
|
||||
constructor(obj) {
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.nodes = [];
|
||||
const features = [];
|
||||
for (const feature of obj._get_float_features()) {
|
||||
const name = feature.FeatureId || `float_feature_${feature.FlatIndex}`;
|
||||
const value = new catboost.Value(name, new catboost.TensorType('float32'));
|
||||
features.push(value);
|
||||
this.inputs.push(new catboost.Argument(name, [value]));
|
||||
}
|
||||
for (const feature of obj._get_cat_features()) {
|
||||
const name = feature.FeatureId || `cat_feature_${feature.FlatIndex}`;
|
||||
const value = new catboost.Value(name, new catboost.TensorType('int32'));
|
||||
features.push(value);
|
||||
this.inputs.push(new catboost.Argument(name, [value]));
|
||||
}
|
||||
for (const feature of obj._get_text_features()) {
|
||||
const name = feature.FeatureId || `text_feature_${feature.FlatIndex}`;
|
||||
const value = new catboost.Value(name, new catboost.TensorType('string'));
|
||||
features.push(value);
|
||||
this.inputs.push(new catboost.Argument(name, [value]));
|
||||
}
|
||||
for (const feature of obj._get_embedding_features()) {
|
||||
const name = feature.FeatureId || `embedding_feature_${feature.FlatIndex}`;
|
||||
const value = new catboost.Value(name, new catboost.TensorType('float32[]'));
|
||||
features.push(value);
|
||||
this.inputs.push(new catboost.Argument(name, [value]));
|
||||
}
|
||||
const node = new catboost.Node(obj, features);
|
||||
this.nodes.push(node);
|
||||
}
|
||||
};
|
||||
|
||||
catboost.Argument = class {
|
||||
|
||||
constructor(name, value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
};
|
||||
|
||||
catboost.Value = class {
|
||||
|
||||
constructor(name, type) {
|
||||
this.name = name;
|
||||
this.type = type || null;
|
||||
}
|
||||
};
|
||||
|
||||
catboost.TensorType = class {
|
||||
|
||||
constructor(dataType) {
|
||||
this.dataType = dataType;
|
||||
this.shape = null;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.dataType;
|
||||
}
|
||||
};
|
||||
|
||||
catboost.Node = class {
|
||||
|
||||
constructor(obj, features) {
|
||||
this.name = '';
|
||||
this.type = { name: 'CatBoost' };
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.attributes = [];
|
||||
if (features.length > 0) {
|
||||
this.inputs.push(new catboost.Argument('features', features));
|
||||
}
|
||||
const treeCount = obj._get_tree_count();
|
||||
if (treeCount > 0) {
|
||||
this.attributes.push(new catboost.Argument('tree_count', treeCount));
|
||||
}
|
||||
const treeSizes = obj._get_tree_sizes();
|
||||
if (treeSizes.length > 0) {
|
||||
this.attributes.push(new catboost.Argument('tree_sizes', Array.from(treeSizes)));
|
||||
}
|
||||
const treeSplits = obj._get_tree_splits();
|
||||
if (treeSplits.length > 0) {
|
||||
this.attributes.push(new catboost.Argument('tree_splits', Array.from(treeSplits)));
|
||||
}
|
||||
const leafValues = obj._get_leaf_values();
|
||||
if (leafValues.length > 0) {
|
||||
this.attributes.push(new catboost.Argument('leaf_values', Array.from(leafValues)));
|
||||
}
|
||||
const leafWeights = obj._get_leaf_weights();
|
||||
if (leafWeights.length > 0) {
|
||||
this.attributes.push(new catboost.Argument('leaf_weights', Array.from(leafWeights)));
|
||||
}
|
||||
const borders = obj._get_borders();
|
||||
if (borders.length > 0) {
|
||||
this.attributes.push(new catboost.Argument('borders', borders));
|
||||
}
|
||||
const [scale, bias] = obj._get_scale_and_bias();
|
||||
if (scale !== undefined && scale !== 1) {
|
||||
this.attributes.push(new catboost.Argument('scale', scale));
|
||||
}
|
||||
if (bias !== undefined && bias !== 0) {
|
||||
this.attributes.push(new catboost.Argument('bias', bias));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
catboost.Error = class extends Error {
|
||||
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'Error loading CatBoost model.';
|
||||
}
|
||||
};
|
||||
|
||||
export const ModelFactory = catboost.ModelFactory;
|
||||
@@ -0,0 +1,546 @@
|
||||
|
||||
import * as flatbuffers from './flatbuffers.js';
|
||||
import * as flexbuffers from './flexbuffers.js';
|
||||
import * as zip from './zip.js';
|
||||
|
||||
const circle = {};
|
||||
|
||||
circle.ModelFactory = class {
|
||||
|
||||
async match(context) {
|
||||
const reader = await context.peek('flatbuffers.binary');
|
||||
if (reader && reader.identifier === 'CIR0') {
|
||||
return context.set('circle.flatbuffers', reader);
|
||||
}
|
||||
const obj = await context.peek('json');
|
||||
if (obj && obj.subgraphs && obj.operator_codes) {
|
||||
return context.set('circle.flatbuffers.json', obj);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async open(context) {
|
||||
circle.schema = await context.require('./circle-schema');
|
||||
circle.schema = circle.schema.circle;
|
||||
let model = null;
|
||||
const attachments = new Map();
|
||||
switch (context.type) {
|
||||
case 'circle.flatbuffers.json': {
|
||||
try {
|
||||
const reader = await context.read('flatbuffers.text');
|
||||
model = circle.schema.Model.createText(reader);
|
||||
} catch (error) {
|
||||
const message = error && error.message ? error.message : error.toString();
|
||||
throw new circle.Error(`File text format is not circle.Model (${message.replace(/\.$/, '')}).`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'circle.flatbuffers': {
|
||||
try {
|
||||
const reader = context.value;
|
||||
model = circle.schema.Model.create(reader);
|
||||
} catch (error) {
|
||||
const message = error && error.message ? error.message : error.toString();
|
||||
throw new circle.Error(`File format is not circle.Model (${message.replace(/\.$/, '')}).`);
|
||||
}
|
||||
try {
|
||||
const stream = context.stream;
|
||||
const archive = zip.Archive.open(stream);
|
||||
if (archive) {
|
||||
for (const [name, value] of archive.entries) {
|
||||
attachments.set(name, value);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new circle.Error(`Unsupported Circle format '${context.type}'.`);
|
||||
}
|
||||
}
|
||||
const stream = context.stream;
|
||||
const metadata = await context.metadata('circle-metadata.json');
|
||||
return new circle.Model(metadata, model, stream);
|
||||
}
|
||||
};
|
||||
|
||||
circle.Model = class {
|
||||
|
||||
constructor(metadata, model, stream) {
|
||||
this.format = 'Circle';
|
||||
this.format = `${this.format} v${model.version}`;
|
||||
this.description = model.description || '';
|
||||
this.modules = [];
|
||||
this.metadata = [];
|
||||
const builtinOperators = new Map();
|
||||
const upperCase = new Set(['2D', 'LSH', 'SVDF', 'RNN', 'L2', 'LSTM']);
|
||||
for (const key of Object.keys(circle.schema.BuiltinOperator)) {
|
||||
const value = key === 'BATCH_MATMUL' ? 'BATCH_MAT_MUL' : key;
|
||||
const name = value.split('_').map((s) => (s.length < 1 || upperCase.has(s)) ? s : s[0] + s.substring(1).toLowerCase()).join('');
|
||||
const index = circle.schema.BuiltinOperator[key];
|
||||
builtinOperators.set(index, name);
|
||||
}
|
||||
const operators = model.operator_codes.map((operator) => {
|
||||
const code = Math.max(operator.deprecated_builtin_code, operator.builtin_code || 0);
|
||||
const value = {};
|
||||
if (code === circle.schema.BuiltinOperator.CUSTOM) {
|
||||
value.name = operator.custom_code ? operator.custom_code : 'Custom';
|
||||
value.version = operator.version;
|
||||
value.custom = true;
|
||||
} else {
|
||||
value.name = builtinOperators.has(code) ? builtinOperators.get(code) : code.toString();
|
||||
value.version = operator.version;
|
||||
value.custom = false;
|
||||
}
|
||||
return value;
|
||||
});
|
||||
let modelMetadata = null;
|
||||
for (const metadata of model.metadata) {
|
||||
const buffer = model.buffers[metadata.buffer];
|
||||
let data = null;
|
||||
const position = stream.position;
|
||||
if (buffer && buffer.data && buffer.data.length > 0) {
|
||||
data = buffer.data;
|
||||
} else if (buffer && buffer.offset !== 0n && buffer.size !== 0n) {
|
||||
const offset = buffer.offset.toNumber();
|
||||
const size = buffer.size.toNumber();
|
||||
stream.seek(offset);
|
||||
data = stream.read(size);
|
||||
}
|
||||
stream.seek(position);
|
||||
if (data) {
|
||||
switch (metadata.name) {
|
||||
case 'min_runtime_version': {
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
this.runtime = decoder.decode(data);
|
||||
break;
|
||||
}
|
||||
case 'TFLITE_METADATA': {
|
||||
const reader = flatbuffers.BinaryReader.open(data);
|
||||
if (!reader || !circle.schema.ModelMetadata.identifier(reader)) {
|
||||
throw new circle.Error('Invalid TensorFlow Lite metadata.');
|
||||
}
|
||||
modelMetadata = circle.schema.ModelMetadata.create(reader);
|
||||
if (modelMetadata.name) {
|
||||
this.name = modelMetadata.name;
|
||||
}
|
||||
if (modelMetadata.version) {
|
||||
this.version = modelMetadata.version;
|
||||
}
|
||||
if (modelMetadata.description) {
|
||||
this.description = this.description ? [this.description, modelMetadata.description].join(' ') : modelMetadata.description;
|
||||
}
|
||||
if (modelMetadata.author) {
|
||||
this.metadata.push(new circle.Argument('author', modelMetadata.author));
|
||||
}
|
||||
if (modelMetadata.license) {
|
||||
this.metadata.push(new circle.Argument('license', modelMetadata.license));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const value = data.length < 256 && data.every((c) => c >= 32 && c < 128) ? String.fromCharCode.apply(null, data) : '?';
|
||||
const argument = new circle.Argument(metadata.name, value);
|
||||
this.metadata.push(argument);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const subgraphs = model.subgraphs;
|
||||
const subgraphsMetadata = modelMetadata ? modelMetadata.subgraph_metadata : null;
|
||||
for (let i = 0; i < subgraphs.length; i++) {
|
||||
const subgraph = subgraphs[i];
|
||||
const name = subgraphs.length > 1 ? i.toString() : '';
|
||||
const subgraphMetadata = subgraphsMetadata && i < subgraphsMetadata.length ? subgraphsMetadata[i] : null;
|
||||
const signatures = model.signature_defs.filter((signature) => signature.subgraph_index === i);
|
||||
const graph = new circle.Graph(metadata, subgraph, signatures, subgraphMetadata, name, operators, model, stream);
|
||||
this.modules.push(graph);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
circle.Graph = class {
|
||||
|
||||
constructor(metadata, subgraph, signatures, subgraphMetadata, name, operators, model, stream) {
|
||||
this.name = subgraph.name || name;
|
||||
if (subgraph.operators.length === 0 && subgraph.tensors.length > 0 && operators.length === 0) {
|
||||
operators.push({ name: 'Weights', custom: true });
|
||||
const layers = new Map();
|
||||
for (let i = 0; i < subgraph.tensors.length; i++) {
|
||||
const tensor = subgraph.tensors[i];
|
||||
const parts = tensor.name.split('.');
|
||||
parts.pop();
|
||||
const key = parts.join('.');
|
||||
if (!layers.has(key)) {
|
||||
const operator = { opcode_index: 0, inputs: [], outputs: [] };
|
||||
layers.set(key, operator);
|
||||
subgraph.operators.push(operator);
|
||||
}
|
||||
const operator = layers.get(key);
|
||||
operator.inputs.push(i);
|
||||
}
|
||||
}
|
||||
const tensors = new Map();
|
||||
tensors.map = (index, metadata) => {
|
||||
if (index === -1) {
|
||||
return null;
|
||||
}
|
||||
if (!tensors.has(index)) {
|
||||
let tensor = { name: '' };
|
||||
let initializer = null;
|
||||
let description = '';
|
||||
let denotation = '';
|
||||
if (index < subgraph.tensors.length) {
|
||||
tensor = subgraph.tensors[index];
|
||||
const buffer = model.buffers[tensor.buffer];
|
||||
const is_variable = tensor.is_variable;
|
||||
const variable = is_variable || (buffer && buffer.data && buffer.data.length > 0) || (buffer && buffer.offset !== 0n && buffer.size !== 0n);
|
||||
initializer = variable ? new circle.Tensor(index, tensor, buffer, stream, is_variable) : null;
|
||||
}
|
||||
if (metadata) {
|
||||
description = metadata.description;
|
||||
const content = metadata.content;
|
||||
if (content) {
|
||||
const contentProperties = content.content_properties;
|
||||
if (contentProperties instanceof circle.schema.FeatureProperties) {
|
||||
denotation = 'Feature';
|
||||
} else if (contentProperties instanceof circle.schema.ImageProperties) {
|
||||
denotation = 'Image';
|
||||
switch (contentProperties.color_space) {
|
||||
case 0: denotation += '(Unknown)'; break;
|
||||
case 1: denotation += '(RGB)'; break;
|
||||
case 2: denotation += '(Grayscale)'; break;
|
||||
default: throw circle.Error(`Unsupported image color space '${contentProperties.color_space}'.`);
|
||||
}
|
||||
} else if (contentProperties instanceof circle.schema.BoundingBoxProperties) {
|
||||
denotation = 'BoundingBox';
|
||||
} else if (contentProperties instanceof circle.schema.AudioProperties) {
|
||||
denotation = `Audio(${contentProperties.sample_rate},${contentProperties.channels})`;
|
||||
}
|
||||
}
|
||||
}
|
||||
const value = new circle.Value(index, tensor, initializer, description, denotation);
|
||||
tensors.set(index, value);
|
||||
}
|
||||
return tensors.get(index);
|
||||
};
|
||||
this.inputs = Array.from(subgraph.inputs).map((tensor_index, index) => {
|
||||
const metadata = subgraphMetadata && index < subgraphMetadata.input_tensor_metadata.length ? subgraphMetadata.input_tensor_metadata[index] : null;
|
||||
const value = tensors.map(tensor_index, metadata);
|
||||
const values = value ? [value] : [];
|
||||
const name = value ? value.name.split('\n')[0] : '?';
|
||||
return new circle.Argument(name, values);
|
||||
});
|
||||
this.outputs = Array.from(subgraph.outputs).map((tensor_index, index) => {
|
||||
const metadata = subgraphMetadata && index < subgraphMetadata.output_tensor_metadata.length ? subgraphMetadata.output_tensor_metadata[index] : null;
|
||||
const value = tensors.map(tensor_index, metadata);
|
||||
const values = value ? [value] : [];
|
||||
const name = value ? value.name.split('\n')[0] : '?';
|
||||
return new circle.Argument(name, values);
|
||||
});
|
||||
this.signatures = signatures.map((signature) => {
|
||||
return new circle.Signature(signature, tensors);
|
||||
});
|
||||
this.nodes = Array.from(subgraph.operators).map((operator, index) => {
|
||||
const opcode_index = operator.opcode_index;
|
||||
const opcode = opcode_index < operators.length ? operators[opcode_index] : { name: `(${opcode_index})` };
|
||||
return new circle.Node(metadata, operator, opcode, index.toString(), tensors);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
circle.Signature = class {
|
||||
|
||||
constructor(signature, tensors) {
|
||||
this.name = signature.signature_key;
|
||||
this.inputs = signature.inputs.map((input) => {
|
||||
const value = tensors.map(input.tensor_index);
|
||||
const values = value ? [value] : [];
|
||||
return new circle.Argument(input.name, values);
|
||||
});
|
||||
this.outputs = signature.outputs.map((output) => {
|
||||
const value = tensors.map(output.tensor_index);
|
||||
const values = value ? [value] : [];
|
||||
return new circle.Argument(output.name, values);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
circle.Node = class {
|
||||
|
||||
constructor(metadata, node, type, identifier, tensors) {
|
||||
this.name = '';
|
||||
this.identifier = identifier;
|
||||
this.type = type.custom ? { name: type.name } : metadata.type(type.name);
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.attributes = [];
|
||||
if (node) {
|
||||
const attributes = [];
|
||||
const inputs = Array.from(node.inputs || new Int32Array(0));
|
||||
for (let i = 0; i < inputs.length;) {
|
||||
let count = 1;
|
||||
let name = null;
|
||||
let visible = true;
|
||||
const values = [];
|
||||
if (this.type && this.type.inputs && i < this.type.inputs.length) {
|
||||
const input = this.type.inputs[i];
|
||||
name = input.name;
|
||||
if (input.list) {
|
||||
count = inputs.length - i;
|
||||
}
|
||||
if (input.visible === false) {
|
||||
visible = false;
|
||||
}
|
||||
}
|
||||
for (const index of inputs.slice(i, i + count)) {
|
||||
const value = tensors.map(index);
|
||||
if (value) {
|
||||
values.push(value);
|
||||
}
|
||||
}
|
||||
name = name ? name : (i + 1).toString();
|
||||
i += count;
|
||||
const argument = new circle.Argument(name, values, null, visible);
|
||||
this.inputs.push(argument);
|
||||
}
|
||||
const outputs = Array.from(node.outputs || new Int32Array(0));
|
||||
for (let i = 0; i < outputs.length; i++) {
|
||||
const index = outputs[i];
|
||||
const value = tensors.map(index);
|
||||
const values = value ? [value] : [];
|
||||
let name = (i + 1).toString();
|
||||
if (this.type && this.type.outputs && i < this.type.outputs.length) {
|
||||
const output = this.type.outputs[i];
|
||||
if (output && output.name) {
|
||||
name = output.name;
|
||||
}
|
||||
}
|
||||
const argument = new circle.Argument(name, values);
|
||||
this.outputs.push(argument);
|
||||
}
|
||||
if (type.custom && node.custom_options && node.custom_options.length > 0) {
|
||||
let decoded = false;
|
||||
if (node.custom_options_format === circle.schema.CustomOptionsFormat.FLEXBUFFERS) {
|
||||
try {
|
||||
const reader = flexbuffers.BinaryReader.open(node.custom_options);
|
||||
if (reader) {
|
||||
const custom_options = reader.read();
|
||||
if (Array.isArray(custom_options)) {
|
||||
attributes.push([null, 'custom_options', custom_options]);
|
||||
decoded = true;
|
||||
} else if (custom_options) {
|
||||
for (const [key, value] of Object.entries(custom_options)) {
|
||||
const schema = metadata.attribute(type.name, key);
|
||||
attributes.push([schema, key, value]);
|
||||
}
|
||||
decoded = true;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
}
|
||||
if (!decoded) {
|
||||
const schema = metadata.attribute(type.name, 'custom');
|
||||
attributes.push([schema, 'custom', Array.from(node.custom_options)]);
|
||||
}
|
||||
}
|
||||
const options = node.builtin_options;
|
||||
if (options) {
|
||||
for (const [name, value] of Object.entries(options)) {
|
||||
if (name === 'fused_activation_function' && value) {
|
||||
const ActivationFunctionType = circle.schema.ActivationFunctionType;
|
||||
let type = '';
|
||||
switch (value) {
|
||||
case ActivationFunctionType.RELU: type = 'Relu'; break;
|
||||
case ActivationFunctionType.RELU_N1_TO_1: type = 'ReluN1To1'; break;
|
||||
case ActivationFunctionType.RELU6: type = 'Relu6'; break;
|
||||
case ActivationFunctionType.TANH: type = 'Tanh'; break;
|
||||
case ActivationFunctionType.SIGN_BIT: type = 'SignBit'; break;
|
||||
case 6: type = 'Sigmoid'; break;
|
||||
default: type = value.toString(); break;
|
||||
}
|
||||
const node = new circle.Node(metadata, null, { name: type }, null, []);
|
||||
this.chain = [node];
|
||||
}
|
||||
const schema = metadata.attribute(type.name, name);
|
||||
attributes.push([schema, name, value]);
|
||||
}
|
||||
}
|
||||
this.attributes = attributes.map(([metadata, name, value]) => {
|
||||
const type = metadata && metadata.type ? metadata.type : null;
|
||||
value = ArrayBuffer.isView(value) ? Array.from(value) : value;
|
||||
let visible = true;
|
||||
if (name === 'fused_activation_function') {
|
||||
visible = false;
|
||||
}
|
||||
if (type) {
|
||||
const enumType = circle.schema[type];
|
||||
if (enumType) {
|
||||
value = enumType[value] || value;
|
||||
}
|
||||
}
|
||||
if (metadata) {
|
||||
if (metadata.visible === false) {
|
||||
visible = false;
|
||||
} else if (metadata.default !== undefined) {
|
||||
if (typeof value === 'function') {
|
||||
value = value();
|
||||
}
|
||||
if (value === metadata.default) {
|
||||
visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new circle.Argument(name, value, type, visible);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
circle.Argument = class {
|
||||
|
||||
constructor(name, value, type = null, visible = true) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
this.visible = visible;
|
||||
}
|
||||
};
|
||||
|
||||
circle.Value = class {
|
||||
|
||||
constructor(index, tensor, initializer, description, denotation) {
|
||||
const name = tensor.name || '';
|
||||
this.name = `${name}\n${index}`;
|
||||
this.identifier = index.toString();
|
||||
this.type = tensor.type !== undefined && tensor.shape !== undefined ? new circle.TensorType(tensor, denotation) : null;
|
||||
this.initializer = initializer;
|
||||
this.description = description;
|
||||
const quantization = tensor.quantization;
|
||||
if (quantization && (quantization.scale.length > 0 || quantization.zero_point.length > 0 || quantization.min.length > 0 || quantization.max.length)) {
|
||||
this.quantization = {
|
||||
type: 'linear',
|
||||
dimension: quantization.quantized_dimension,
|
||||
scale: quantization.scale,
|
||||
offset: quantization.zero_point,
|
||||
min: quantization.min,
|
||||
max: quantization.max
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
circle.Tensor = class {
|
||||
|
||||
constructor(index, tensor, buffer, stream, is_variable) {
|
||||
this.identifier = index.toString();
|
||||
this.name = tensor.name;
|
||||
this.type = new circle.TensorType(tensor);
|
||||
this.category = is_variable ? 'Variable' : '';
|
||||
this.encoding = this.type.dataType === 'string' ? '|' : '<';
|
||||
if (buffer && buffer.data && buffer.data.length > 0) {
|
||||
this._data = buffer.data.slice(0);
|
||||
} else if (buffer && buffer.offset !== 0n && buffer.size !== 0n) {
|
||||
const offset = buffer.offset.toNumber();
|
||||
const size = buffer.size.toNumber();
|
||||
stream.seek(offset);
|
||||
this._data = stream.stream(size);
|
||||
} else {
|
||||
this._data = null;
|
||||
}
|
||||
}
|
||||
|
||||
get values() {
|
||||
switch (this.type.dataType) {
|
||||
case 'string': {
|
||||
let offset = 0;
|
||||
if (!this._data || this._data.byteLength < 4) {
|
||||
throw new circle.Error(`Invalid string tensor '${this.name}'.`);
|
||||
}
|
||||
const data = new DataView(this._data.buffer, this._data.byteOffset, this._data.byteLength);
|
||||
const count = data.getInt32(0, true);
|
||||
if (count < 0 || count > Math.floor((data.byteLength - 4) / 4)) {
|
||||
throw new circle.Error(`Invalid string tensor '${this.name}'.`);
|
||||
}
|
||||
offset += 4;
|
||||
const offsetTable = [];
|
||||
for (let j = 0; j < count; j++) {
|
||||
offsetTable.push(data.getInt32(offset, true));
|
||||
offset += 4;
|
||||
}
|
||||
offsetTable.push(this._data.length);
|
||||
const stringTable = [];
|
||||
const utf8Decoder = new TextDecoder('utf-8');
|
||||
for (let k = 0; k < count; k++) {
|
||||
const start = offsetTable[k];
|
||||
const end = offsetTable[k + 1];
|
||||
if (start < offset || start > end || end > this._data.length) {
|
||||
throw new circle.Error(`Invalid string tensor '${this.name}'.`);
|
||||
}
|
||||
const textArray = this._data.subarray(start, end);
|
||||
stringTable.push(utf8Decoder.decode(textArray));
|
||||
}
|
||||
return stringTable;
|
||||
}
|
||||
default: {
|
||||
if (this._data instanceof Uint8Array) {
|
||||
return this._data;
|
||||
}
|
||||
if (this._data && this._data.peek) {
|
||||
return this._data.peek();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
circle.TensorType = class {
|
||||
|
||||
constructor(tensor, denotation) {
|
||||
const shape = tensor.shape_signature && tensor.shape_signature.length > 0 ? tensor.shape_signature : tensor.shape;
|
||||
switch (tensor.type) {
|
||||
case circle.schema.TensorType.BOOL: this.dataType = 'boolean'; break;
|
||||
default: {
|
||||
const name = circle.schema.TensorType[tensor.type];
|
||||
this.dataType = name ? name.toLowerCase() : '?';
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.shape = new circle.TensorShape(Array.from(shape || []));
|
||||
this.denotation = denotation;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.dataType + this.shape.toString();
|
||||
}
|
||||
};
|
||||
|
||||
circle.TensorShape = class {
|
||||
|
||||
constructor(dimensions) {
|
||||
this.dimensions = dimensions;
|
||||
}
|
||||
|
||||
toString() {
|
||||
if (!this.dimensions || this.dimensions.length === 0) {
|
||||
return '';
|
||||
}
|
||||
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
|
||||
}
|
||||
};
|
||||
|
||||
circle.Error = class extends Error {
|
||||
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'Error loading Circle model.';
|
||||
}
|
||||
};
|
||||
|
||||
export const ModelFactory = circle.ModelFactory;
|
||||
@@ -0,0 +1,795 @@
|
||||
[
|
||||
{
|
||||
"name": "Abs",
|
||||
"identifier": 8
|
||||
},
|
||||
{
|
||||
"name": "Acos",
|
||||
"identifier": 82
|
||||
},
|
||||
{
|
||||
"name": "Asin",
|
||||
"identifier": 81
|
||||
},
|
||||
{
|
||||
"name": "Asinh",
|
||||
"identifier": 86
|
||||
},
|
||||
{
|
||||
"name": "Assign",
|
||||
"identifier": 73
|
||||
},
|
||||
{
|
||||
"name": "Atan",
|
||||
"identifier": 96
|
||||
},
|
||||
{
|
||||
"name": "Atanh",
|
||||
"identifier": 85
|
||||
},
|
||||
{
|
||||
"name": "AveragePooling",
|
||||
"category": "Pool",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "BatchNormalization",
|
||||
"identifier": 40,
|
||||
"category": "Normalization",
|
||||
"attributes": [
|
||||
{ "name": "disableRegularization", "default": false },
|
||||
{ "name": "useCuDNNEngine", "visible": false },
|
||||
{ "name": "useCntkEngine", "visible": false },
|
||||
{ "name": "runCountUntied", "visible": false },
|
||||
{ "name": "epsilon", "default": 0.00001 },
|
||||
{ "name": "normalizationTimeConstant", "default": 0 },
|
||||
{ "name": "disableRegularization", "default": false },
|
||||
{ "name": "blendTimeConstant", "default": 0 },
|
||||
{ "name": "imageLayoutKind", "type": "ImageLayoutKind", "visible": false }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "scale" },
|
||||
{ "name": "bias" },
|
||||
{ "name": "mean" },
|
||||
{ "name": "variance" },
|
||||
{ "name": "count" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Block",
|
||||
"identifier": 57
|
||||
},
|
||||
{
|
||||
"name": "Cast",
|
||||
"identifier": 91
|
||||
},
|
||||
{
|
||||
"name": "ClassificationError",
|
||||
"identifier": 36,
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Clip",
|
||||
"identifier": 41
|
||||
},
|
||||
{
|
||||
"name": "Combine",
|
||||
"identifier": 44,
|
||||
"category": "Tensor",
|
||||
"inputs": [
|
||||
{ "name": "inputs", "type": "Tensor[]" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ConstantOp",
|
||||
"identifier": 89
|
||||
},
|
||||
{
|
||||
"name": "Convolution",
|
||||
"identifier": 33,
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "transpose", "default": false },
|
||||
{ "name": "maxTempMemSizeInSamples", "default": 0 },
|
||||
{ "name": "dilation", "default": [ 1, null ] },
|
||||
{ "name": "outputShape", "default": 0 },
|
||||
{ "name": "sharing", "default": [ true, null ] },
|
||||
{ "name": "strides", "default": [ 1, null ] },
|
||||
{ "name": "includePad", "default": false },
|
||||
{ "name": "ceilOutDim", "default": false },
|
||||
{ "name": "autoPadding", "default": [ true, null ] },
|
||||
{ "name": "lowerPad", "default": [ 0, null ] },
|
||||
{ "name": "upperPad", "default": [ 0, null ] },
|
||||
{ "name": "convolution2D", "visible": false },
|
||||
{ "name": "poolKind", "type": "PoolKind", "default": "None" },
|
||||
{ "name": "imageLayoutKind", "type": "ImageLayoutKind", "visible": false }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "W" },
|
||||
{ "name": "b" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ConvolutionSequenceShape",
|
||||
"identifier": 97
|
||||
},
|
||||
{
|
||||
"name": "Cos",
|
||||
"identifier": 55
|
||||
},
|
||||
{
|
||||
"name": "CosDistance",
|
||||
"identifier": 53
|
||||
},
|
||||
{
|
||||
"name": "CosDistanceWithNegativeSamples",
|
||||
"identifier": 67
|
||||
},
|
||||
{
|
||||
"name": "Cosh",
|
||||
"identifier": 78
|
||||
},
|
||||
{
|
||||
"name": "Crop",
|
||||
"identifier": 84,
|
||||
"category": "Data"
|
||||
},
|
||||
{
|
||||
"name": "CrossEntropyWithSoftmax",
|
||||
"identifier": 35,
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "CustomProxyOp",
|
||||
"identifier": 93
|
||||
},
|
||||
{
|
||||
"name": "Dropout",
|
||||
"identifier": 15,
|
||||
"category": "Dropout",
|
||||
"attributes": [
|
||||
{ "name": "rngSeed", "visible": false },
|
||||
{ "name": "rngOffset", "visible": false }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "EditDistanceError",
|
||||
"identifier": 61
|
||||
},
|
||||
{
|
||||
"name": "ElementTimes",
|
||||
"identifier": 21,
|
||||
"inputs": [
|
||||
{ "name": "A" },
|
||||
{ "name": "B" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "C" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ELU",
|
||||
"identifier": 65
|
||||
},
|
||||
{
|
||||
"name": "Equal",
|
||||
"identifier": 22,
|
||||
"inputs": [
|
||||
{ "name": "A" },
|
||||
{ "name": "B" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "C" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Exp",
|
||||
"identifier": 4,
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "EyeLikeOp",
|
||||
"identifier": 92
|
||||
},
|
||||
{
|
||||
"name": "Floor",
|
||||
"identifier": 7,
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ForwardBackward",
|
||||
"identifier": 66
|
||||
},
|
||||
{
|
||||
"name": "FutureValue",
|
||||
"identifier": 38,
|
||||
"attributes": [
|
||||
{ "name": "offset", "type": "uint32", "default": 1 }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "initialState" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Gather",
|
||||
"identifier": 74,
|
||||
"category": "Transform"
|
||||
},
|
||||
{
|
||||
"name": "GatherPacked",
|
||||
"identifier": 29,
|
||||
"inputs": [
|
||||
{ "name": "index" },
|
||||
{ "name": "source" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Greater",
|
||||
"identifier": 26,
|
||||
"inputs": [
|
||||
{ "name": "A" },
|
||||
{ "name": "B" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "C" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "GreaterEqual",
|
||||
"identifier": 27,
|
||||
"inputs": [
|
||||
{ "name": "A" },
|
||||
{ "name": "B" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "C" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Hardmax",
|
||||
"identifier": 11,
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "InvStdDev",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "LabelsToGraph",
|
||||
"identifier": 63
|
||||
},
|
||||
{
|
||||
"name": "LambdaRank",
|
||||
"identifier": 59
|
||||
},
|
||||
{
|
||||
"name": "LatticeSequenceWithSoftmax",
|
||||
"identifier": 90
|
||||
},
|
||||
{
|
||||
"name": "Less",
|
||||
"identifier": 24,
|
||||
"inputs": [
|
||||
{ "name": "A" },
|
||||
{ "name": "B" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "C" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "LessEqual",
|
||||
"identifier": 25,
|
||||
"inputs": [
|
||||
{ "name": "A" },
|
||||
{ "name": "B" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "C" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Log",
|
||||
"identifier": 5,
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Logistic",
|
||||
"identifier": 48
|
||||
},
|
||||
{
|
||||
"name": "LogPlus",
|
||||
"identifier": 52
|
||||
},
|
||||
{
|
||||
"name": "LogSoftmax",
|
||||
"identifier": 51
|
||||
},
|
||||
{
|
||||
"name": "MaxPooling",
|
||||
"category": "Pool",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Mean",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Minus",
|
||||
"identifier": 20,
|
||||
"inputs": [
|
||||
{ "name": "A" },
|
||||
{ "name": "B" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "C" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "NDCG",
|
||||
"identifier": 60
|
||||
},
|
||||
{
|
||||
"name": "Negate",
|
||||
"identifier": 0,
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "NoOp",
|
||||
"identifier": 62,
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "NotEqual",
|
||||
"identifier": 23,
|
||||
"inputs": [
|
||||
{ "name": "A" },
|
||||
{ "name": "B" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "C" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OneHot",
|
||||
"identifier": 68
|
||||
},
|
||||
{
|
||||
"name": "OptimizedRNNStack",
|
||||
"identifier": 49
|
||||
},
|
||||
{
|
||||
"name": "PackedIndex",
|
||||
"identifier": 28,
|
||||
"inputs": [
|
||||
{ "name": "source" },
|
||||
{ "name": "index" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Pad",
|
||||
"identifier": 83,
|
||||
"category": "Tensor",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Pass",
|
||||
"identifier": 56
|
||||
},
|
||||
{
|
||||
"name": "PastValue",
|
||||
"identifier": 37,
|
||||
"attributes": [
|
||||
{ "name": "offset", "type": "uint32", "default": 1 }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "initialState" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Plus",
|
||||
"identifier": 19,
|
||||
"inputs": [
|
||||
{ "name": "A" },
|
||||
{ "name": "B" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "C" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Pooling",
|
||||
"identifier": 17,
|
||||
"category": "Pool",
|
||||
"attributes": [
|
||||
{ "name": "transpose", "default": false },
|
||||
{ "name": "includePad", "default": false },
|
||||
{ "name": "ceilOutDim", "default": false },
|
||||
{ "name": "autoPadding", "default": [ false, null ] },
|
||||
{ "name": "sharing", "default": [ true, null ] },
|
||||
{ "name": "strides", "default": [ 1, null ] },
|
||||
{ "name": "lowerPad", "default": [ 0, null ] },
|
||||
{ "name": "upperPad", "default": [ 0, null ] },
|
||||
{ "name": "outputShape", "default": 0 },
|
||||
{ "name": "maxTempMemSizeInSamples", "default": 0 },
|
||||
{ "name": "poolingType", "type": "PoolingType", "default": "Max" },
|
||||
{ "name": "poolKind", "type": "PoolKind", "default": "None" },
|
||||
{ "name": "imageLayoutKind", "type": "ImageLayoutKind", "visible": false }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Pow",
|
||||
"identifier": 69
|
||||
},
|
||||
{
|
||||
"name": "RandomDistribution",
|
||||
"identifier": 76
|
||||
},
|
||||
{
|
||||
"name": "RandomSample",
|
||||
"identifier": 45
|
||||
},
|
||||
{
|
||||
"name": "RandomSampleInclusionFrequency",
|
||||
"identifier": 46
|
||||
},
|
||||
{
|
||||
"name": "Reciprocal",
|
||||
"identifier": 9,
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ReconcileDynamicAxis",
|
||||
"identifier": 50
|
||||
},
|
||||
{
|
||||
"name": "RectifiedLinear",
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ReduceElements",
|
||||
"identifier": 39,
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ReLU",
|
||||
"identifier": 3,
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Reshape",
|
||||
"identifier": 16,
|
||||
"category": "Shape",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ROIPooling",
|
||||
"identifier": 47,
|
||||
"category": "Pool",
|
||||
"attributes": [
|
||||
{ "name": "spatialScale", "default": 0.0625 },
|
||||
{ "name": "poolKind", "type": "PoolKind", "default": "None" }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "inputs" },
|
||||
{ "name": "ROIs" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "outputs" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ScatterPacked",
|
||||
"identifier": 30
|
||||
},
|
||||
{
|
||||
"name": "Select",
|
||||
"identifier": 42
|
||||
},
|
||||
{
|
||||
"name": "Sigmoid",
|
||||
"identifier": 1,
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Sin",
|
||||
"identifier": 54
|
||||
},
|
||||
{
|
||||
"name": "Sinh",
|
||||
"identifier": 77
|
||||
},
|
||||
{
|
||||
"name": "Slice",
|
||||
"identifier": 14,
|
||||
"category": "Tensor",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "begin" },
|
||||
{ "name": "end" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Softmax",
|
||||
"identifier": 10,
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Splice",
|
||||
"identifier": 43,
|
||||
"category": "Tensor",
|
||||
"inputs": [
|
||||
{ "name": "inputs", "type": "Tensor[]" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Sqrt",
|
||||
"identifier": 6,
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SquaredError",
|
||||
"identifier": 34
|
||||
},
|
||||
{
|
||||
"name": "Squeeze",
|
||||
"identifier": 88,
|
||||
"category": "Transform"
|
||||
},
|
||||
{
|
||||
"name": "StableSigmoid",
|
||||
"identifier": 75,
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "StopGradient",
|
||||
"identifier": 64
|
||||
},
|
||||
{
|
||||
"name": "StraightThrough",
|
||||
"identifier": 94
|
||||
},
|
||||
{
|
||||
"name": "SumAll",
|
||||
"identifier": 18
|
||||
},
|
||||
{
|
||||
"name": "Tan",
|
||||
"identifier": 95
|
||||
},
|
||||
{
|
||||
"name": "Tanh",
|
||||
"identifier": 2,
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Times",
|
||||
"identifier": 31,
|
||||
"attributes": [
|
||||
{ "name": "outputRank", "default": 1 },
|
||||
{ "name": "inferInputRankToMap", "visible": false, "default": -1 }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "A" },
|
||||
{ "name": "B" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "C" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ToBatch",
|
||||
"identifier": 80
|
||||
},
|
||||
{
|
||||
"name": "TopK",
|
||||
"identifier": 87,
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ToSequence",
|
||||
"identifier": 70
|
||||
},
|
||||
{
|
||||
"name": "ToSequenceLike",
|
||||
"identifier": 71
|
||||
},
|
||||
{
|
||||
"name": "TransposeAxes",
|
||||
"identifier": 12,
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "TransposeDimensions",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "TransposeTimes",
|
||||
"identifier": 32
|
||||
},
|
||||
{
|
||||
"name": "UnpackBatch",
|
||||
"identifier": 79
|
||||
},
|
||||
{
|
||||
"name": "UnpackSequence",
|
||||
"identifier": 72
|
||||
},
|
||||
{
|
||||
"name": "Unpooling",
|
||||
"identifier": 58
|
||||
},
|
||||
{
|
||||
"name": "Where",
|
||||
"identifier": 13,
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" }
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,346 @@
|
||||
|
||||
export const CNTK = {};
|
||||
|
||||
CNTK.proto = {};
|
||||
|
||||
CNTK.proto.NDShape = class NDShape {
|
||||
|
||||
constructor() {
|
||||
this.shape_dim = [];
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new CNTK.proto.NDShape();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.shape_dim = reader.array(message.shape_dim, () => reader.uint64(), tag);
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
CNTK.proto.Axis = class Axis {
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new CNTK.proto.Axis();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.static_axis_idx = reader.int32();
|
||||
break;
|
||||
case 2:
|
||||
message.name = reader.string();
|
||||
break;
|
||||
case 3:
|
||||
message.is_ordered_dynamic_axis = reader.bool();
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
CNTK.proto.Axis.prototype.static_axis_idx = 0;
|
||||
CNTK.proto.Axis.prototype.name = "";
|
||||
CNTK.proto.Axis.prototype.is_ordered_dynamic_axis = false;
|
||||
|
||||
CNTK.proto.NDArrayView = class NDArrayView {
|
||||
|
||||
get values() {
|
||||
CNTK.proto.NDArrayView.valuesSet = CNTK.proto.NDArrayView.valuesSet || new Set(["float_values", "double_values", "bytes_value", "sint32_values"]);
|
||||
return Object.keys(this).find((key) => CNTK.proto.NDArrayView.valuesSet.has(key) && this[key] !== null);
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new CNTK.proto.NDArrayView();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.data_type = reader.int32();
|
||||
break;
|
||||
case 2:
|
||||
message.storage_format = reader.int32();
|
||||
break;
|
||||
case 3:
|
||||
message.shape = CNTK.proto.NDShape.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 4:
|
||||
message.float_values = CNTK.proto.NDArrayView.FloatValues.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 5:
|
||||
message.double_values = CNTK.proto.NDArrayView.DoubleValues.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 6:
|
||||
message.bytes_value = CNTK.proto.NDArrayView.BytesValue.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 7:
|
||||
message.sint32_values = CNTK.proto.NDArrayView.IntValues.decode(reader, reader.uint32());
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
CNTK.proto.NDArrayView.prototype.data_type = 0;
|
||||
CNTK.proto.NDArrayView.prototype.storage_format = 0;
|
||||
CNTK.proto.NDArrayView.prototype.shape = null;
|
||||
|
||||
CNTK.proto.NDArrayView.DataType = {
|
||||
"Unknown": 0,
|
||||
"Float": 1,
|
||||
"Double": 2,
|
||||
"Float16": 4,
|
||||
"Int8": 5,
|
||||
"Int16": 6
|
||||
};
|
||||
|
||||
CNTK.proto.NDArrayView.StorageFormat = {
|
||||
"Dense": 0,
|
||||
"SparseCSC": 1,
|
||||
"SparseBlockCol": 2
|
||||
};
|
||||
|
||||
CNTK.proto.NDArrayView.FloatValues = class FloatValues {
|
||||
|
||||
constructor() {
|
||||
this.value = [];
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new CNTK.proto.NDArrayView.FloatValues();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.value = reader.floats(message.value, tag);
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
CNTK.proto.NDArrayView.DoubleValues = class DoubleValues {
|
||||
|
||||
constructor() {
|
||||
this.value = [];
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new CNTK.proto.NDArrayView.DoubleValues();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.value = reader.doubles(message.value, tag);
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
CNTK.proto.NDArrayView.BytesValue = class BytesValue {
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new CNTK.proto.NDArrayView.BytesValue();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.value = reader.bytes();
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
CNTK.proto.NDArrayView.BytesValue.prototype.value = new Uint8Array([]);
|
||||
|
||||
CNTK.proto.NDArrayView.IntValues = class IntValues {
|
||||
|
||||
constructor() {
|
||||
this.value = [];
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new CNTK.proto.NDArrayView.IntValues();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.value = reader.array(message.value, () => reader.sint32(), tag);
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
CNTK.proto.Vector = class Vector {
|
||||
|
||||
constructor() {
|
||||
this.value = [];
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new CNTK.proto.Vector();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.value.push(CNTK.proto.DictionaryValue.decode(reader, reader.uint32()));
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
CNTK.proto.Dictionary = class Dictionary {
|
||||
|
||||
constructor() {
|
||||
this.data = {};
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new CNTK.proto.Dictionary();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.version = reader.uint64();
|
||||
break;
|
||||
case 2:
|
||||
reader.entry(message.data, () => reader.string(), () => CNTK.proto.DictionaryValue.decode(reader, reader.uint32()));
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
CNTK.proto.Dictionary.prototype.version = 0n;
|
||||
|
||||
CNTK.proto.DictionaryValue = class DictionaryValue {
|
||||
|
||||
get value() {
|
||||
CNTK.proto.DictionaryValue.valueSet = CNTK.proto.DictionaryValue.valueSet || new Set(["bool_value", "int_value", "size_t_value", "float_value", "double_value", "string_value", "nd_shape_value", "axis_value", "vector_value", "dictionary_value", "nd_array_view_value"]);
|
||||
return Object.keys(this).find((key) => CNTK.proto.DictionaryValue.valueSet.has(key) && this[key] !== null);
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new CNTK.proto.DictionaryValue();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.version = reader.uint64();
|
||||
break;
|
||||
case 2:
|
||||
message.value_type = reader.int32();
|
||||
break;
|
||||
case 3:
|
||||
message.bool_value = reader.bool();
|
||||
break;
|
||||
case 4:
|
||||
message.int_value = reader.int32();
|
||||
break;
|
||||
case 5:
|
||||
message.size_t_value = reader.uint64();
|
||||
break;
|
||||
case 6:
|
||||
message.float_value = reader.float();
|
||||
break;
|
||||
case 7:
|
||||
message.double_value = reader.double();
|
||||
break;
|
||||
case 8:
|
||||
message.string_value = reader.string();
|
||||
break;
|
||||
case 9:
|
||||
message.nd_shape_value = CNTK.proto.NDShape.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 10:
|
||||
message.axis_value = CNTK.proto.Axis.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 11:
|
||||
message.vector_value = CNTK.proto.Vector.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 12:
|
||||
message.dictionary_value = CNTK.proto.Dictionary.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 13:
|
||||
message.nd_array_view_value = CNTK.proto.NDArrayView.decode(reader, reader.uint32());
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
CNTK.proto.DictionaryValue.prototype.version = 0n;
|
||||
CNTK.proto.DictionaryValue.prototype.value_type = 0;
|
||||
|
||||
CNTK.proto.DictionaryValue.Type = {
|
||||
"None": 0,
|
||||
"Bool": 1,
|
||||
"Int": 2,
|
||||
"SizeT": 3,
|
||||
"Float": 4,
|
||||
"Double": 5,
|
||||
"String": 6,
|
||||
"NDShape": 7,
|
||||
"Axis": 8,
|
||||
"Vector": 9,
|
||||
"Dictionary": 10,
|
||||
"NDArrayView": 11
|
||||
};
|
||||
@@ -0,0 +1,497 @@
|
||||
[
|
||||
{
|
||||
"name": "activation",
|
||||
"category": "Activation",
|
||||
"description": "Applies specified type of activation function to input."
|
||||
},
|
||||
{
|
||||
"name": "add",
|
||||
"description": "A layer that performs elementwise addition.",
|
||||
"inputs": [
|
||||
{ "name": "x" },
|
||||
{ "name": "y" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "z" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "average",
|
||||
"description": "A layer that computes the elementwise average of the inputs."
|
||||
},
|
||||
{
|
||||
"name": "batchnorm",
|
||||
"category": "Normalization",
|
||||
"description": "A layer that performs batch normalization, which is performed along the channel axis, and repeated along the other axes, if present.",
|
||||
"attributes": [
|
||||
{ "name": "epsilon", "default": 0.000009999999747378752 },
|
||||
{ "name": "computeMeanVar", "visible": false },
|
||||
{ "name": "instanceNormalization", "visible": false }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bias",
|
||||
"category": "Layer",
|
||||
"description": "A layer that performs elementwise addition of a bias, which is broadcasted to match the input shape."
|
||||
},
|
||||
{
|
||||
"name": "biDirectionalLSTM",
|
||||
"category": "Layer",
|
||||
"description": "Bidirectional long short-term memory (LSTM) layer. The first LSTM operates on the input sequence in the forward direction. The second LSTM operates on the input sequence in the reverse direction.",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "h" },
|
||||
{ "name": "c" },
|
||||
{ "name": "h_rev" },
|
||||
{ "name": "c_rev" },
|
||||
{ "name": "inputGateWeightMatrix", "visible": false },
|
||||
{ "name": "forgetGateWeightMatrix", "visible": false },
|
||||
{ "name": "blockInputWeightMatrix", "visible": false },
|
||||
{ "name": "outputGateWeightMatrix", "visible": false },
|
||||
{ "name": "inputGateRecursionMatrix", "visible": false },
|
||||
{ "name": "forgetGateRecursionMatrix", "visible": false },
|
||||
{ "name": "blockInputRecursionMatrix", "visible": false },
|
||||
{ "name": "outputGateRecursionMatrix", "visible": false },
|
||||
{ "name": "inputGateBiasVector", "visible": false },
|
||||
{ "name": "forgetGateBiasVector", "visible": false },
|
||||
{ "name": "blockInputBiasVector", "visible": false },
|
||||
{ "name": "outputGateBiasVector", "visible": false },
|
||||
{ "name": "inputGateWeightMatrix_rev", "visible": false },
|
||||
{ "name": "forgetGateWeightMatrix_rev", "visible": false },
|
||||
{ "name": "blockInputWeightMatrix_rev", "visible": false },
|
||||
{ "name": "outputGateWeightMatrix_rev", "visible": false },
|
||||
{ "name": "inputGateRecursionMatrix_rev", "visible": false },
|
||||
{ "name": "forgetGateRecursionMatrix_rev", "visible": false },
|
||||
{ "name": "blockInputRecursionMatrix_rev", "visible": false },
|
||||
{ "name": "outputGateRecursionMatrix_rev", "visible": false },
|
||||
{ "name": "inputGateBiasVector_rev", "visible": false },
|
||||
{ "name": "forgetGateBiasVector_rev", "visible": false },
|
||||
{ "name": "blockInputBiasVector_rev", "visible": false },
|
||||
{ "name": "outputGateBiasVector_rev", "visible": false }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" },
|
||||
{ "name": "h" },
|
||||
{ "name": "c" },
|
||||
{ "name": "h_rev" },
|
||||
{ "name": "c_rev" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "concat",
|
||||
"category": "Tensor",
|
||||
"description": "A layer that concatenates along the channel axis (default) or sequence axis.",
|
||||
"inputs": [
|
||||
{ "name": "inputs", "type": "Tensor[]" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "convolution",
|
||||
"category": "Layer",
|
||||
"description": "A layer that performs spatial convolution or deconvolution.",
|
||||
"attributes": [
|
||||
{ "name": "outputShape", "type": "uint64[]", "description": "Either None or a 2-tuple, specifying the output shape (output_height, output_width). Used only when is_deconv == True. When is_deconv == False, this parameter is ignored. If it is None, the output shape is calculated automatically using the border_mode. Kindly refer to NeuralNetwork.proto for details.", "visible": false },
|
||||
{ "name": "outputChannels", "type": "uint64", "description": "The number of kernels. Same as ``C_out`` used in the layer description.", "visible": false },
|
||||
{ "name": "kernelChannels", "type": "uint64", "description": "Channel dimension of the kernels. Must be equal to ``inputChannels / nGroups``, if isDeconvolution == False. Must be equal to ``inputChannels``, if isDeconvolution == True.", "visible": false },
|
||||
{ "name": "nGroups", "type": "uint64", "description": "Group convolution, i.e. weight reuse along channel axis. Input and kernels are divided into g groups and convolution / deconvolution is applied within the groups independently. If not set or 0, it is set to the default value 1.", "default": 1 },
|
||||
{ "name": "isDeconvolution", "type": "boolean", "description": "Flag to specify whether it is a deconvolution layer." },
|
||||
{ "name": "valid", "type": "ValidPadding", "visible": false },
|
||||
{ "name": "same", "type": "SamePadding", "visible": false },
|
||||
{ "name": "dilationFactor", "type": "uint64[]", "default": [ 1, 1 ] },
|
||||
{ "name": "stride", "type": "uint64[]", "default": [ 1, 1 ] },
|
||||
{ "name": "kernelSize", "type": "uint64[]", "default": [ 3, 3 ] },
|
||||
{ "name": "hasBias", "type": "boolean", "description": "Flag to specify whether a bias is to be added or not.", "visible": false }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "crop",
|
||||
"category": "Data",
|
||||
"description": "A layer that crops the spatial dimensions of an input. If two inputs are provided, the shape of the second input is used as the reference shape.",
|
||||
"inputs": [
|
||||
{ "name": "x1" },
|
||||
{ "name": "x2" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "y" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dot",
|
||||
"description": "If true, inputs are normalized first, thereby computing the cosine similarity."
|
||||
},
|
||||
{
|
||||
"name": "embedding",
|
||||
"category": "Transform",
|
||||
"description": "A layer that performs a matrix lookup and optionally adds a bias."
|
||||
},
|
||||
{
|
||||
"name": "featureVectorizer",
|
||||
"inputs": [
|
||||
{ "name": "inputs", "type": "Tensor[]" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "flatten",
|
||||
"category": "Shape",
|
||||
"description": "A layer that flattens the input.",
|
||||
"attributes": [
|
||||
{ "name": "mode", "type": "FlattenLayerParams.FlattenOrder" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "gather",
|
||||
"category": "Transform",
|
||||
"description": "Gather layer that gathers elements from the first input, along a specified axis, at indices specified in the second input.",
|
||||
"inputs": [
|
||||
{ "name": "input", "type": "Tensor" },
|
||||
{ "name": "indices", "type": "Tensor" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "gelu",
|
||||
"category": "Activation",
|
||||
"description": "Gaussian error linear unit activation.",
|
||||
"attributes": [
|
||||
{ "name": "mode", "type": "GeluLayerParams.GeluMode" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "gru",
|
||||
"category": "Layer",
|
||||
"description": "Gated-Recurrent Unit (GRU) Layer",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "h" },
|
||||
{ "name": "updateGateWeightMatrix", "visible": false },
|
||||
{ "name": "resetGateWeightMatrix", "visible": false },
|
||||
{ "name": "outputGateWeightMatrix", "visible": false },
|
||||
{ "name": "updateGateRecursionMatrix", "visible": false },
|
||||
{ "name": "resetGateRecursionMatrix", "visible": false },
|
||||
{ "name": "outputGateRecursionMatrix", "visible": false },
|
||||
{ "name": "updateGateBiasVector", "visible": false },
|
||||
{ "name": "resetGateBiasVector", "visible": false },
|
||||
{ "name": "outputGateBiasVector", "visible": false }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" },
|
||||
{ "name": "h" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "innerProduct",
|
||||
"category": "Layer",
|
||||
"description": "A layer that performs a matrix vector product. This is equivalent to a fully-connected, or dense layer.",
|
||||
"attributes": [
|
||||
{ "name": "inputChannels", "type": "uint64", "visible": false },
|
||||
{ "name": "outputChannels", "type": "uint64", "visible": false },
|
||||
{ "name": "hasBias", "type": "boolean", "visible": false }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "int64ClassLabels",
|
||||
"category": "Data",
|
||||
"outputs": [
|
||||
{ "name": "probabilities" },
|
||||
{ "name": "feature" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "itemSimilarityRecommender",
|
||||
"inputs": [
|
||||
{ "name": "item" },
|
||||
{ "name": "numRecommendations" },
|
||||
{ "name": "itemRestriction" },
|
||||
{ "name": "itemExclusion" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "recommendedItemList" },
|
||||
{ "name": "recommendedItemScore" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "l2normalize",
|
||||
"category": "Normalization",
|
||||
"description": "A layer that performs L2 normalization, i.e. divides by the the square root of the sum of squares of all elements of input."
|
||||
},
|
||||
{
|
||||
"name": "loadConstant",
|
||||
"category": "Data"
|
||||
},
|
||||
{
|
||||
"name": "lrn",
|
||||
"category": "Normalization",
|
||||
"description": "A layer that performs local response normalization (LRN).",
|
||||
"attributes": [
|
||||
{ "name": "k", "default": 1 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "max",
|
||||
"description": "A layer that computes the elementwise maximum over the inputs."
|
||||
},
|
||||
{
|
||||
"name": "min",
|
||||
"description": "A layer that computes the elementwise minimum over the inputs."
|
||||
},
|
||||
{
|
||||
"name": "multiply",
|
||||
"description": "A layer that performs elementwise multiplication.",
|
||||
"inputs": [
|
||||
{ "name": "x" },
|
||||
{ "name": "y" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "z" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "mvn",
|
||||
"category": "Normalization",
|
||||
"description": "A layer that performs mean variance normalization, along axis = -3."
|
||||
},
|
||||
{
|
||||
"name": "nonMaximumSuppression",
|
||||
"attributes": [
|
||||
{ "name": "iouThreshold" },
|
||||
{ "name": "confidenceThreshold" }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "confidence" },
|
||||
{ "name": "coordinates" },
|
||||
{ "name": "iouThreshold" },
|
||||
{ "name": "confidenceThreshold" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "confidence" },
|
||||
{ "name": "coordinates" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "padding",
|
||||
"category": "Shape",
|
||||
"description": "Fill a constant value in the padded region.",
|
||||
"attributes": [
|
||||
{ "name": "paddingAmounts", "visible": false }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "permute",
|
||||
"category": "Shape",
|
||||
"description": "A layer that rearranges the dimensions and data of an input."
|
||||
},
|
||||
{
|
||||
"name": "pooling",
|
||||
"category": "Pool",
|
||||
"description": "Spatial Pooling layer to reduce dimensions of input using the specified kernel size and type.",
|
||||
"attributes": [
|
||||
{ "name": "includeLastPixel", "type": "ValidCompletePadding", "visible": false },
|
||||
{ "name": "same", "type": "SamePadding", "visible": false },
|
||||
{ "name": "valid", "type": "ValidCompletePadding", "visible": false },
|
||||
{ "name": "type", "type": "PoolingLayerParams.PoolingType" },
|
||||
{ "name": "globalPooling", "type": "boolean", "default": false },
|
||||
{ "name": "stride", "type": "uint64", "default": [ 1, 1 ] },
|
||||
{ "name": "kernelSize", "type": "uint64[]", "default": [ 3, 3 ] },
|
||||
{ "name": "avgPoolExcludePadding", "type": "boolean", "default": false }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "reduce",
|
||||
"description": "A layer that reduces the input using a specified operation."
|
||||
},
|
||||
{
|
||||
"name": "reorganizeData",
|
||||
"category": "Shape",
|
||||
"description": "A layer that reorganizes data in the input in: 1. SPACE_TO_DEPTH, 2. DEPTH_TO_SPACE."
|
||||
},
|
||||
{
|
||||
"name": "reshape",
|
||||
"category": "Shape",
|
||||
"description": "A layer that recasts the input into a new shape."
|
||||
},
|
||||
{
|
||||
"name": "scale",
|
||||
"category": "Layer",
|
||||
"description": "A layer that performs elmentwise multiplication by a scale factor and optionally adds a bias.",
|
||||
"attributes": [
|
||||
{ "name": "hasBias", "type": "boolean", "visible": false }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "scaler",
|
||||
"category": "Data"
|
||||
},
|
||||
{
|
||||
"name": "sequenceRepeat",
|
||||
"category": "Shape",
|
||||
"description": "A layer that repeats a sequence."
|
||||
},
|
||||
{
|
||||
"name": "slice",
|
||||
"description": "A layer that uniformly splits across the channel dimension to produce a specified number of outputs."
|
||||
},
|
||||
{
|
||||
"name": "softmax",
|
||||
"category": "Activation",
|
||||
"description": "A layer that performs softmax normalization. Normalization is done along the channel axis."
|
||||
},
|
||||
{
|
||||
"name": "softmaxND",
|
||||
"category": "Activation",
|
||||
"description": "A layer that performs softmax normalization along a specified axis."
|
||||
},
|
||||
{
|
||||
"name": "squeeze",
|
||||
"category": "Transform"
|
||||
},
|
||||
{
|
||||
"name": "stringClassLabels",
|
||||
"category": "Data",
|
||||
"outputs": [
|
||||
{ "name": "probabilities" },
|
||||
{ "name": "feature" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "textClassifier",
|
||||
"attributes": [
|
||||
{ "name": "revision", "visible": false }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "unary",
|
||||
"description": "A layer that applies a unary function.",
|
||||
"attributes": [
|
||||
{ "name": "type", "type": "UnaryFunctionLayerParams.Operation" },
|
||||
{ "name": "alpha", "default": 1 },
|
||||
{ "name": "scale", "default": 1 },
|
||||
{ "name": "epsilon", "default": 9.999999974752427e-7 }
|
||||
],
|
||||
"inputs": [
|
||||
{ "name": "x" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "z" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "uniDirectionalLSTM",
|
||||
"category": "Layer",
|
||||
"description": "A unidirectional long short-term memory (LSTM) layer.",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "h" },
|
||||
{ "name": "c" },
|
||||
{ "name": "inputGateWeightMatrix", "visible": false },
|
||||
{ "name": "forgetGateWeightMatrix", "visible": false },
|
||||
{ "name": "blockInputWeightMatrix", "visible": false },
|
||||
{ "name": "outputGateWeightMatrix", "visible": false },
|
||||
{ "name": "inputGateRecursionMatrix", "visible": false },
|
||||
{ "name": "forgetGateRecursionMatrix", "visible": false },
|
||||
{ "name": "blockInputRecursionMatrix", "visible": false },
|
||||
{ "name": "outputGateRecursionMatrix", "visible": false },
|
||||
{ "name": "inputGateBiasVector", "visible": false },
|
||||
{ "name": "forgetGateBiasVector", "visible": false },
|
||||
{ "name": "blockInputBiasVector", "visible": false },
|
||||
{ "name": "outputGateBiasVector", "visible": false }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output" },
|
||||
{ "name": "h" },
|
||||
{ "name": "c" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "upsample",
|
||||
"category": "Data",
|
||||
"description": "A layer that scales up spatial dimensions. It supports two modes: nearest neighbour (default) and bilinear."
|
||||
},
|
||||
{
|
||||
"name": "transpose",
|
||||
"category": "Transform"
|
||||
},
|
||||
{
|
||||
"name": "wordTagger",
|
||||
"attributes": [
|
||||
{ "name": "revision", "visible": false }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "tokens" },
|
||||
{ "name": "tags" },
|
||||
{ "name": "locations" },
|
||||
{ "name": "lengths" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "program:conv",
|
||||
"category": "Layer",
|
||||
"inputs": [
|
||||
{ "name": "x" },
|
||||
{ "name": "weight" },
|
||||
{ "name": "bias" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "program:batch_norm",
|
||||
"category": "Normalization",
|
||||
"inputs": [
|
||||
{ "name": "x" },
|
||||
{ "name": "mean" },
|
||||
{ "name": "variance" },
|
||||
{ "name": "gamma" },
|
||||
{ "name": "beta" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "program:linear",
|
||||
"category": "Layer",
|
||||
"inputs": [
|
||||
{ "name": "x" },
|
||||
{ "name": "weight" },
|
||||
{ "name": "bias" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "program:pad",
|
||||
"category": "Tensor"
|
||||
},
|
||||
{
|
||||
"name": "program:transpose",
|
||||
"category": "Transform"
|
||||
},
|
||||
{
|
||||
"name": "program:sigmoid",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "program:softmax",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "program:relu",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "program:relu6",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "program:reshape",
|
||||
"category": "Shape"
|
||||
},
|
||||
{
|
||||
"name": "program:concat",
|
||||
"category": "Tensor"
|
||||
},
|
||||
{
|
||||
"name": "program:layer_norm",
|
||||
"category": "Normalization"
|
||||
},
|
||||
{
|
||||
"name": "program:max_pool",
|
||||
"category": "Pool"
|
||||
},
|
||||
{
|
||||
"name": "program:gather",
|
||||
"category": "Transform"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,480 @@
|
||||
[
|
||||
{
|
||||
"name": "avgpool",
|
||||
"category": "Pool"
|
||||
},
|
||||
{
|
||||
"name": "batchnorm",
|
||||
"category": "Normalization"
|
||||
},
|
||||
{
|
||||
"name": "connected",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "output", "type": "int32", "visible": false, "default": 1 },
|
||||
{ "name": "activation", "type": "string", "default": "logistic", "description": "options are: LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU, SWISH, MISH" },
|
||||
{ "name": "batch_normalize", "type": "int32", "default": 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "conv_lstm",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "batch_normalize", "type": "int32", "default": 0 },
|
||||
{ "name": "activation", "type": "string", "default": "linear", "description": "options are: LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU, SWISH, MISH" },
|
||||
{ "name": "size", "type": "int32", "default": 3 },
|
||||
{ "name": "stride", "type": "int32", "default": 1 },
|
||||
{ "name": "dilation", "default": 1 },
|
||||
{ "name": "groups", "type": "int32", "default": 1 },
|
||||
{ "name": "padding", "type": "int32", "default": 0 },
|
||||
{ "name": "pad", "type": "int32", "default": 0 },
|
||||
{ "name": "xnor", "type": "int32", "default": 0 },
|
||||
{ "name": "shortcut", "default": 0 },
|
||||
{ "name": "output", "type": "int32", "default": 1 },
|
||||
{ "name": "state_constrain", "type": "int32", "default": 16 },
|
||||
{ "name": "peephole", "type": "int32", "default": 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "convolutional",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "filters", "type": "int32", "default": 1 },
|
||||
{ "name": "size", "type": "int32", "default": 1 },
|
||||
{ "name": "stride", "type": "int32", "default": 1 },
|
||||
{ "name": "stride_x", "type": "int32", "default": -1 },
|
||||
{ "name": "stride_y", "type": "int32", "default": -1 },
|
||||
{ "name": "groups", "type": "int32", "default": 1 },
|
||||
{ "name": "padding", "type": "int32", "default": 0 },
|
||||
{ "name": "pad", "type": "int32", "default": 0 },
|
||||
{ "name": "dilation", "default": 1 },
|
||||
{ "name": "share_index", "default": -1000000000 },
|
||||
{ "name": "binary", "type": "int32", "default": 0 },
|
||||
{ "name": "xnor", "type": "int32", "default": 0 },
|
||||
{ "name": "bin_output", "type": "int32", "default": 0 },
|
||||
{ "name": "flipped", "type": "int32", "default": 0 },
|
||||
{ "name": "dot", "type": "float32", "default": 0 },
|
||||
{ "name": "batch_normalize", "type": "int32", "default": 0 },
|
||||
{ "name": "activation", "type": "string", "default": "logistic", "description": "options are: LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU, SWISH, MISH" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "cost",
|
||||
"attributes": [
|
||||
{ "name": "type", "type": "string", "default": "sse" },
|
||||
{ "name": "scale", "type": "float32", "default": 1 },
|
||||
{ "name": "ratio", "type": "float32", "default": 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "crnn",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "batch_normalize", "type": "int32", "default": 0 },
|
||||
{ "name": "activation", "type": "string", "default": "logistic" },
|
||||
{ "name": "dilation", "default": 1 },
|
||||
{ "name": "padding", "default": 0 },
|
||||
{ "name": "pad", "type": "int32", "default": 0 },
|
||||
{ "name": "groups", "type": "int32", "default": 1 },
|
||||
{ "name": "xnor", "type": "int32", "default": 0 },
|
||||
{ "name": "shortcut", "type": "int32", "default": 0 },
|
||||
{ "name": "output_filters", "default": 1 },
|
||||
{ "name": "hidden_filters", "default": 1 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "crop",
|
||||
"category": "Shape",
|
||||
"attributes": [
|
||||
{ "name": "crop_height", "type": "int32", "default": 1 },
|
||||
{ "name": "crop_width", "type": "int32", "default": 1 },
|
||||
{ "name": "flip", "type": "int32", "default": 0 },
|
||||
{ "name": "exposure", "type": "float32", "default": 1 },
|
||||
{ "name": "saturation", "type": "float32", "default": 1 },
|
||||
{ "name": "angle", "type": "float32", "default": 0 },
|
||||
{ "name": "noadjust", "default": 0 },
|
||||
{ "name": "shift", "type": "float32", "default": 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "deconvolutional",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "filters", "type": "int32", "visible": false, "default": 1 },
|
||||
{ "name": "size", "type": "int32", "default": 1 },
|
||||
{ "name": "stride", "type": "int32", "default": 1 },
|
||||
{ "name": "padding", "type": "int32", "default": 0 },
|
||||
{ "name": "pad", "type": "int32", "default": 0 },
|
||||
{ "name": "batch_normalize", "type": "int32", "default": 0 },
|
||||
{ "name": "activation", "type": "string", "default": "logistic", "description": "options are: LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU, SWISH, MISH" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "detection",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "classes", "type": "int32", "default": 1 },
|
||||
{ "name": "coord", "type": "int32", "default": 1 },
|
||||
{ "name": "num", "type": "int32", "default": 1 },
|
||||
{ "name": "jitter", "type": "float32", "default": 0.2 },
|
||||
{ "name": "coord_scale", "type": "float32", "default": 1 },
|
||||
{ "name": "object_scale", "type": "float32", "default": 1 },
|
||||
{ "name": "noobject_scale", "type": "float32", "default": 1 },
|
||||
{ "name": "class_scale", "type": "float32", "default": 1 },
|
||||
{ "name": "forced", "type": "int32", "default": 0 },
|
||||
{ "name": "side", "type": "int32", "default": 7 },
|
||||
{ "name": "softmax", "type": "int32", "default": 0 },
|
||||
{ "name": "sqrt", "type": "int32", "default": 0 },
|
||||
{ "name": "max", "type": "int32", "default": 30 },
|
||||
{ "name": "rescore", "type": "int32", "default": 0 },
|
||||
{ "name": "random", "type": "int32", "default": 0 },
|
||||
{ "name": "reorg", "type": "int32", "default": 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dropout",
|
||||
"category": "Dropout",
|
||||
"attributes": [
|
||||
{ "name": "probability", "type": "float32", "default": 0.5 },
|
||||
{ "name": "dropblock", "type": "int32", "default": 0 },
|
||||
{ "name": "dropblock_size_rel", "type": "float32", "default": 0 },
|
||||
{ "name": "dropblock_size_abs ", "type": "int32", "default": 7 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "elu",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "gaussian_yolo",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "classes", "type": "int32", "default": 20 },
|
||||
{ "name": "num", "type": "int32", "default": 1 },
|
||||
{ "name": "mask", "type": "string", "default": 0 },
|
||||
{ "name": "jitter", "type": "float32", "default": 0.2 },
|
||||
{ "name": "label_smooth_eps", "type": "float32", "default": 0 },
|
||||
{ "name": "scale_x_y", "type": "float32", "default": 1 },
|
||||
{ "name": "uc_normalizer", "type": "float32", "default": 1 },
|
||||
{ "name": "iou_normalizer", "type": "float32", "default": 0.75 },
|
||||
{ "name": "cls_normalizer", "type": "float32", "default": 1 },
|
||||
{ "name": "iou_loss", "type": "string", "default": "mse", "description": "options are: mse, giou, diou, and ciou" },
|
||||
{ "name": "max", "default": 90 },
|
||||
{ "name": "ignore_thresh", "type": "float32", "default": 0.5 },
|
||||
{ "name": "truth_thresh", "default": 1 },
|
||||
{ "name": "iou_thresh", "type": "float32", "default": 1, "description": "recommended to use iou_thresh=0.213" },
|
||||
{ "name": "random", "type": "int32", "default": 0 },
|
||||
{ "name": "map", "type": "string", "default": 0 },
|
||||
{ "name": "beta_nms", "type": "float32", "default": 0.6 },
|
||||
{ "name": "nms_kind", "type": "string", "default": "default", "description": "options are: greedynms, diounms, cornersnms, or defaultnms" },
|
||||
{ "name": "anchors", "type": "string", "default": 0 },
|
||||
{ "name": "yolo_point", "type": "string", "default": "center", "description": "options are: center, left_top, and right_bottom" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "gru",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "output", "type": "int32", "visible": false, "default": 1 },
|
||||
{ "name": "batch_normalize", "type": "int32", "default": 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "h_swish",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "hardtan",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "leaky",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "lhtan",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "linear",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "local",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "filters", "type": "int32", "visible": false, "default": 1 },
|
||||
{ "name": "size", "type": "int32", "default": 1 },
|
||||
{ "name": "stride", "type": "int32", "default": 1 },
|
||||
{ "name": "padding", "type": "int32", "default": 0 },
|
||||
{ "name": "pad", "type": "int32", "default": 0 },
|
||||
{ "name": "activation", "type": "string", "default": "logistic", "description": "options are: LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU, SWISH, MISH" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "loggy",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "logistic",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "lstm",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "output", "type": "int32", "visible": false, "default": 1 },
|
||||
{ "name": "batch_normalize", "type": "int32", "default": 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "maxpool",
|
||||
"category": "Pool",
|
||||
"attributes": [
|
||||
{ "name": "stride", "type": "int32", "default": 1 },
|
||||
{ "name": "stride_x", "type": "int32", "default": 1 },
|
||||
{ "name": "stride_y", "type": "int32", "default": 1 },
|
||||
{ "name": "size", "type": "int32", "default": 1 },
|
||||
{ "name": "padding", "type": "int32", "default": 0 },
|
||||
{ "name": "maxpool_depth", "type": "int32", "default": 0 },
|
||||
{ "name": "out_channels", "default": 1 },
|
||||
{ "name": "antialiasing", "type": "int32", "default": 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "mish",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "net",
|
||||
"attributes": [
|
||||
{ "name": "batch", "type": "int32", "default": 1 },
|
||||
{ "name": "max_batches", "type": "int32", "default": 0, "description": "Limits the maximum number of iterations" },
|
||||
{ "name": "learning_rate", "type": "float32", "default": 0.001 },
|
||||
{ "name": "momentum", "type": "float32", "default": 0.9 },
|
||||
{ "name": "decay", "type": "float32", "default": 0.0001 },
|
||||
{ "name": "subdivisions", "type": "int32", "default": 1, "description": "In concert with batch property, this greatly affect memory usage, minimal working number is recommended" },
|
||||
{ "name": "time_steps", "type": "int32", "default": 1 },
|
||||
{ "name": "notruth", "type": "int32", "default": 0 },
|
||||
{ "name": "random", "type": "int32", "default": 0 },
|
||||
{ "name": "adam", "type": "int32", "default": 0 },
|
||||
{ "name": "B1", "type": "float32", "default": 0.9 },
|
||||
{ "name": "B2", "type": "float32", "default": 0.999 },
|
||||
{ "name": "eps", "type": "float32", "default": 1e-7 },
|
||||
{ "name": "height", "type": "int32", "default": 0 },
|
||||
{ "name": "width", "type": "int32", "default": 0 },
|
||||
{ "name": "channels", "type": "int32", "default": 0 },
|
||||
{ "name": "inputs", "type": "int32" },
|
||||
{ "name": "max_crop", "type": "int32" },
|
||||
{ "name": "min_crop", "type": "int32" },
|
||||
{ "name": "max_ratio", "type": "float32" },
|
||||
{ "name": "min_ratio", "type": "float32" },
|
||||
{ "name": "center", "type": "int32", "default": 0 },
|
||||
{ "name": "clip", "type": "int32", "default": 0 },
|
||||
{ "name": "angle", "type": "float32", "default": 0 },
|
||||
{ "name": "aspect", "type": "float32", "default": 1 },
|
||||
{ "name": "saturation", "type": "float32", "default": 1 },
|
||||
{ "name": "exposure", "type": "float32", "default": 1 },
|
||||
{ "name": "hue", "type": "float32", "default": 0 },
|
||||
{ "name": "power", "type": "float32", "default": 4 },
|
||||
{ "name": "flip", "type": "int32", "default": 1, "description": "Enables augmentation method: horizontal flip" },
|
||||
{ "name": "blur", "type": "int32", "default": 0, "description": "Enables augmentation method: backgound blurring" },
|
||||
{ "name": "mixup", "type": "int32", "default": 0, "description": "Enables augmentation method: images mixup" },
|
||||
{ "name": "cutmix", "type": "int32", "default": 0, "description": "Enables augmentation method: images cutmix" },
|
||||
{ "name": "mosaic", "type": "int32", "default": 0, "description": "Enables augmentation method: images mosaicing" },
|
||||
{ "name": "letter_box", "type": "int32", "default": 0, "description": "Enables letter-box resizing (keeping the aspect ratio)" },
|
||||
{ "name": "policy", "type": "string", "default": "constant" },
|
||||
{ "name": "burn_in", "type": "int32", "default": 0, "description": "Is used for MAP calculation: permit a minimal number of iteration before first MAP check" },
|
||||
{ "name": "letter_box", "type": "int32", "default": 0 },
|
||||
{ "name": "optimized_memory", "type": "int32", "default": 0, "description": "can offload memory from GPU into CPU at the cost of speed, 3 options are possible please look at: https://github.com/AlexeyAB/darknet/issues/4386" },
|
||||
{ "name": "workspace_size_limit_MB", "type": "float32", "default": 1024 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "norm_chan",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "norm_chan_softmax",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "normalization",
|
||||
"category": "Normalization",
|
||||
"attributes": [
|
||||
{ "name": "alpha", "type": "float32", "default": 0.0001 },
|
||||
{ "name": "beta", "type": "float32", "default": 0.75 },
|
||||
{ "name": "kappa", "type": "float32", "default": 1 },
|
||||
{ "name": "size", "default": 5 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "plse",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "ramp",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "region",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "classes", "type": "int32", "default": 20 },
|
||||
{ "name": "coord", "default": 4 },
|
||||
{ "name": "num", "default": 1 },
|
||||
{ "name": "mask", "type": "string", "default": 0 },
|
||||
{ "name": "jitter", "type": "float32", "default": 0.2 },
|
||||
{ "name": "classfix", "type": "int32", "default": 0 },
|
||||
{ "name": "coord_scale", "type": "float32", "default": 1 },
|
||||
{ "name": "object_scale", "type": "float32", "default": 1 },
|
||||
{ "name": "noobject_scale", "type": "float32", "default": 1 },
|
||||
{ "name": "mask_scale", "type": "float32", "default": 1 },
|
||||
{ "name": "class_scale", "type": "float32", "default": 1 },
|
||||
{ "name": "bias_match", "type": "int32", "default": 0 },
|
||||
{ "name": "focal_loss", "type": "int32", "default": 0 },
|
||||
{ "name": "max", "type": "int32", "default": 90 },
|
||||
{ "name": "softmax", "type": "int32", "default": 0 },
|
||||
{ "name": "rescore", "type": "int32", "default": 0 },
|
||||
{ "name": "thresh", "type": "float32", "default": 0.5 },
|
||||
{ "name": "random", "type": "int32", "default": 0 },
|
||||
{ "name": "map", "type": "string", "default": 0 },
|
||||
{ "name": "tree", "type": "string", "default": 0 },
|
||||
{ "name": "anchors", "type": "string", "default": 0 },
|
||||
{ "name": "absolute", "default": 0 },
|
||||
{ "name": "log", "default": 0 },
|
||||
{ "name": "sqrt", "default": 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "relie",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "relu",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "relu6",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "reorg",
|
||||
"category": "Shape",
|
||||
"attributes": [
|
||||
{ "name": "stride", "default": 1 },
|
||||
{ "name": "reverse", "type": "int32", "default": 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "rnn",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "output", "type": "int32", "visible": false },
|
||||
{ "name": "hidden", "visible": false, "default": 1 },
|
||||
{ "name": "activation", "type": "string", "default": "logistic", "description": "options are: LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU, SWISH, MISH" },
|
||||
{ "name": "groups", "type": "int32", "default": 1 },
|
||||
{ "name": "xnor", "type": "int32", "default": 0 },
|
||||
{ "name": "shortcut", "default": 0 },
|
||||
{ "name": "logistic", "default": 0 },
|
||||
{ "name": "batch_normalize", "type": "int32", "default": 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "route",
|
||||
"category": "Tensor",
|
||||
"attributes": [
|
||||
{ "name": "groups_id", "type": "int32", "default": 0 },
|
||||
{ "name": "groups", "type": "int32", "default": 1 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "sam",
|
||||
"category": "Tensor",
|
||||
"attributes": [
|
||||
{ "name": "activation", "type": "string", "default": "linear", "description": "options are: LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU" },
|
||||
{ "name": "from", "description": "This params link the layer to another one, the index of the layer is either positive in which case it's a direct address, if negative it's relative to the layer position" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "scale_channels",
|
||||
"category": "Tensor",
|
||||
"attributes": [
|
||||
{ "name": "activation", "type": "string", "default": "linear", "description": "options are: LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU" },
|
||||
{ "name": "scale_wh", "default": 0 },
|
||||
{ "name": "from", "description": "This params link the layer to another one, the index of the layer is either positive in which case it's a direct address, if negative it's relative to the layer position" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "selu",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "silu",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "shortcut",
|
||||
"category": "Tensor",
|
||||
"attributes": [
|
||||
{ "name": "activation", "type": "string", "default": "logistic", "description": "options are: LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU, SWISH, MISH" },
|
||||
{ "name": "assisted_excitation", "default": 0 },
|
||||
{ "name": "from", "description": "This params link the layer to another one, the index of the layer is either positive in which case it's a direct address, if negative it's relative to the layer position" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "softmax",
|
||||
"category": "Activation",
|
||||
"attributes": [
|
||||
{ "name": "groups", "type": "int32", "default": 1 },
|
||||
{ "name": "temperature", "type": "float32", "default": 1 },
|
||||
{ "name": "tree", "type": "string", "default": 0 },
|
||||
{ "name": "spatial", "type": "int32", "default": 0 },
|
||||
{ "name": "noloss", "type": "int32", "default": 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stair",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "swish",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "tanh",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "upsample",
|
||||
"category": "Data",
|
||||
"attributes": [
|
||||
{ "name": "stride", "type": "int32", "default": 2 },
|
||||
{ "name": "scale", "type": "float32", "default": 1 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "yolo",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "classes", "type": "int32", "default": 20 },
|
||||
{ "name": "num", "type": "int32", "default": 1 },
|
||||
{ "name": "mask", "type": "int32[]", "default": 0 },
|
||||
{ "name": "jitter", "type": "float32", "default": 0.2 },
|
||||
{ "name": "label_smooth_eps", "type": "float32", "default": 0 },
|
||||
{ "name": "scale_x_y", "type": "float32", "default": 1 },
|
||||
{ "name": "iou_normalizer", "type": "float32", "default": 0.75 },
|
||||
{ "name": "cls_normalizer", "type": "float32", "default": 1 },
|
||||
{ "name": "iou_loss", "type": "string", "default": "mse", "description": "options are: mse, giou, diou, and ciou" },
|
||||
{ "name": "focal_loss", "type": "int32", "default": 0 },
|
||||
{ "name": "max", "type": "int32", "default": 90 },
|
||||
{ "name": "ignore_thresh", "type": "float32", "default": 0.5 },
|
||||
{ "name": "truth_thresh", "type": "float32", "default": 1 },
|
||||
{ "name": "iou_thresh", "type": "float32", "default": 1, "description": "recommended to use iou_thresh=0.213" },
|
||||
{ "name": "random", "type": "int32", "default": 0 },
|
||||
{ "name": "map", "type": "string", "default": 0 },
|
||||
{ "name": "nms_kind", "type": "string", "default": "default", "description": "options are: greedynms, diounms, cornersnms, or defaultnms" },
|
||||
{ "name": "anchors", "type": "int32[]", "default": 0 }
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,681 @@
|
||||
|
||||
import * as base from './base.js';
|
||||
import * as child_process from 'child_process';
|
||||
import * as electron from 'electron';
|
||||
import * as fs from 'fs';
|
||||
import * as http from 'http';
|
||||
import * as https from 'https';
|
||||
import * as node from './node.js';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as url from 'url';
|
||||
import * as view from './view.js';
|
||||
|
||||
const desktop = {};
|
||||
|
||||
desktop.Host = class {
|
||||
|
||||
constructor() {
|
||||
this._document = window.document;
|
||||
this._window = window;
|
||||
this._global = global;
|
||||
this._telemetry = new base.Telemetry(this._window);
|
||||
process.on('uncaughtException', (error) => {
|
||||
this.exception(error, true);
|
||||
this.message(error.message);
|
||||
});
|
||||
this._global.eval = () => {
|
||||
throw new Error('eval.eval() not supported.');
|
||||
};
|
||||
this._window.eval = () => {
|
||||
throw new Error('window.eval() not supported.');
|
||||
};
|
||||
this._window.addEventListener('unload', () => {
|
||||
if (typeof __coverage__ !== 'undefined') {
|
||||
const dir = path.join(process.cwd(), 'dist', 'nyc', '.nyc_output');
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
const base = path.basename(window.location.pathname, '.html');
|
||||
const file = path.join(dir, `${base}.json`);
|
||||
/* eslint-disable-next-line no-undef */
|
||||
fs.writeFileSync(file, JSON.stringify(__coverage__));
|
||||
}
|
||||
});
|
||||
this._environment = electron.ipcRenderer.sendSync('get-environment', {});
|
||||
this._environment.menu = this._environment.titlebar && this._environment.platform !== 'darwin';
|
||||
this._files = [];
|
||||
electron.ipcRenderer.on('open', (sender, data) => {
|
||||
this._open(data);
|
||||
});
|
||||
this._element('menu-button').style.opacity = 0;
|
||||
if (!/^\d+\.\d+\.\d+$/.test(this.version)) {
|
||||
throw new Error('Invalid version.');
|
||||
}
|
||||
const metadata = [];
|
||||
metadata.push(os.arch());
|
||||
let packager = '';
|
||||
if (process.platform === 'linux') {
|
||||
try {
|
||||
child_process.execFileSync('dpkg', ['-S', process.execPath]);
|
||||
packager = 'deb';
|
||||
} catch {
|
||||
try {
|
||||
child_process.execFileSync("rpm", ["-qf", process.execPath]);
|
||||
packager = 'rpm';
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
}
|
||||
}
|
||||
metadata.push(packager);
|
||||
this._metadata = metadata.join('|');
|
||||
}
|
||||
|
||||
get window() {
|
||||
return this._window;
|
||||
}
|
||||
|
||||
get document() {
|
||||
return this._document;
|
||||
}
|
||||
|
||||
get version() {
|
||||
return this._environment.version;
|
||||
}
|
||||
|
||||
get type() {
|
||||
return 'Electron';
|
||||
}
|
||||
|
||||
get metadata() {
|
||||
return this._metadata;
|
||||
}
|
||||
|
||||
async view(view) {
|
||||
this._view = view;
|
||||
const age = async () => {
|
||||
const days = (new Date() - new Date(this._environment.date)) / (24 * 60 * 60 * 1000);
|
||||
if (days > 180) {
|
||||
this.document.body.classList.remove('spinner');
|
||||
const link = this._element('logo-github').href;
|
||||
for (;;) {
|
||||
/* eslint-disable-next-line no-await-in-loop */
|
||||
await this.message('Please update to the newest version.', null, 'Download');
|
||||
this.openURL(link);
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
const consent = async () => {
|
||||
const time = this.get('consent');
|
||||
if (!time || (Date.now() - time) > 30 * 24 * 60 * 60 * 1000) {
|
||||
let consent = true;
|
||||
try {
|
||||
const content = await this._request('https://ipinfo.io/json', { 'Content-Type': 'application/json' }, 2000);
|
||||
const json = JSON.parse(content);
|
||||
const countries = ['AT', 'BE', 'BG', 'HR', 'CZ', 'CY', 'DK', 'EE', 'FI', 'FR', 'DE', 'EL', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'NO', 'PL', 'PT', 'SK', 'ES', 'SE', 'GB', 'UK', 'GR', 'EU', 'RO'];
|
||||
if (json && json.country && countries.indexOf(json.country) === -1) {
|
||||
consent = false;
|
||||
}
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
if (consent) {
|
||||
this.document.body.classList.remove('spinner');
|
||||
await this.message('This app uses cookies to report errors and anonymous usage information.', null, 'Accept');
|
||||
}
|
||||
this.set('consent', Date.now());
|
||||
}
|
||||
};
|
||||
const telemetry = async () => {
|
||||
if (this._environment.packaged) {
|
||||
const measurement_id = '848W2NVWVH';
|
||||
const user = this.get('user') || null;
|
||||
const session = this.get('session') || null;
|
||||
await this._telemetry.start(`G-${measurement_id}`, user && user.indexOf('.') !== -1 ? user : null, session);
|
||||
this._telemetry.send('page_view', {
|
||||
app_name: this.type,
|
||||
app_version: this.version,
|
||||
app_metadata: this.metadata
|
||||
});
|
||||
this._telemetry.send('scroll', {
|
||||
percent_scrolled: 90,
|
||||
app_name: this.type,
|
||||
app_version: this.version,
|
||||
app_metadata: this.metadata
|
||||
});
|
||||
this.set('user', this._telemetry.get('client_id'));
|
||||
this.set('session', this._telemetry.session);
|
||||
}
|
||||
};
|
||||
await age();
|
||||
await consent();
|
||||
await telemetry();
|
||||
}
|
||||
|
||||
async start() {
|
||||
if (this._files) {
|
||||
const files = this._files;
|
||||
delete this._files;
|
||||
if (files.length > 0) {
|
||||
const data = files.pop();
|
||||
this._open(data);
|
||||
}
|
||||
}
|
||||
this._window.addEventListener('focus', () => {
|
||||
this._document.body.classList.add('active');
|
||||
});
|
||||
this._window.addEventListener('blur', () => {
|
||||
this._document.body.classList.remove('active');
|
||||
});
|
||||
if (this._document.hasFocus()) {
|
||||
this._document.body.classList.add('active');
|
||||
}
|
||||
electron.ipcRenderer.on('recents', (sender, data) => {
|
||||
this._view.recents(data);
|
||||
});
|
||||
electron.ipcRenderer.on('export', (sender, data) => {
|
||||
this._view.export(data.file);
|
||||
});
|
||||
electron.ipcRenderer.on('cut', () => {
|
||||
this.document.execCommand('cut');
|
||||
});
|
||||
electron.ipcRenderer.on('copy', () => {
|
||||
this.document.execCommand('copy');
|
||||
});
|
||||
electron.ipcRenderer.on('paste', () => {
|
||||
if (this.document.queryCommandSupported('paste')) {
|
||||
this.document.execCommand('paste');
|
||||
} else if (this.document.queryCommandSupported('insertText')) {
|
||||
const content = electron.clipboard.readText();
|
||||
this.document.execCommand('insertText', false, content);
|
||||
}
|
||||
});
|
||||
electron.ipcRenderer.on('selectall', () => {
|
||||
this.document.execCommand('selectall');
|
||||
});
|
||||
electron.ipcRenderer.on('toggle', (sender, name) => {
|
||||
this._view.toggle(name);
|
||||
this.update({ ...this._view.options });
|
||||
});
|
||||
electron.ipcRenderer.on('zoom-in', () => {
|
||||
this._element('zoom-in-button').click();
|
||||
});
|
||||
electron.ipcRenderer.on('zoom-out', () => {
|
||||
this._element('zoom-out-button').click();
|
||||
});
|
||||
electron.ipcRenderer.on('zoom-reset', () => {
|
||||
this._view.resetZoom();
|
||||
});
|
||||
electron.ipcRenderer.on('show-properties', () => {
|
||||
this._element('sidebar-target-button').click();
|
||||
});
|
||||
electron.ipcRenderer.on('find', () => {
|
||||
this._view.find();
|
||||
});
|
||||
electron.ipcRenderer.on('about', () => {
|
||||
this._view.about();
|
||||
});
|
||||
this._element('titlebar-close').addEventListener('click', () => {
|
||||
electron.ipcRenderer.sendSync('window-close', {});
|
||||
});
|
||||
this._element('titlebar-toggle').addEventListener('click', () => {
|
||||
electron.ipcRenderer.sendSync('window-toggle', {});
|
||||
});
|
||||
this._element('titlebar-minimize').addEventListener('click', () => {
|
||||
electron.ipcRenderer.sendSync('window-minimize', {});
|
||||
});
|
||||
electron.ipcRenderer.on('window-state', (sender, data) => {
|
||||
if (this._environment.titlebar) {
|
||||
this._element('target').style.marginTop = '32px';
|
||||
this._element('target').style.height = 'calc(100% - 32px)';
|
||||
this._element('sidebar-title').style.marginTop = '24px';
|
||||
this._element('sidebar-closebutton').style.marginTop = '24px';
|
||||
this._element('titlebar').classList.add('titlebar-visible');
|
||||
}
|
||||
if (this._environment.titlebar && this._environment.platform !== 'darwin' && !data.fullscreen) {
|
||||
this._element('titlebar-control-box').classList.add('titlebar-control-box-visible');
|
||||
} else {
|
||||
this._element('titlebar-control-box').classList.remove('titlebar-control-box-visible');
|
||||
}
|
||||
this._element('menu-button').style.opacity = this._environment.menu ? 1 : 0;
|
||||
this._element('titlebar-maximize').style.opacity = data.maximized ? 0 : 1;
|
||||
this._element('titlebar-restore').style.opacity = data.maximized ? 1 : 0;
|
||||
this._element('titlebar-toggle').setAttribute('title', data.maximized ? 'Restore' : 'Maximize');
|
||||
});
|
||||
electron.ipcRenderer.sendSync('update-window-state', {});
|
||||
const openFileButton = this._element('open-file-button');
|
||||
if (openFileButton) {
|
||||
openFileButton.addEventListener('click', async () => {
|
||||
await this.execute('open');
|
||||
});
|
||||
}
|
||||
this.document.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
this.document.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
this.document.body.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
const files = Array.from(e.dataTransfer.files);
|
||||
const paths = files.map((file) => electron.webUtils.getPathForFile(file));
|
||||
if (paths.length > 0) {
|
||||
electron.ipcRenderer.send('drop-paths', { paths });
|
||||
}
|
||||
return false;
|
||||
});
|
||||
this._view.show('welcome');
|
||||
}
|
||||
|
||||
environment(name) {
|
||||
return this._environment[name];
|
||||
}
|
||||
|
||||
async error(message) {
|
||||
await this.message(message, true, 'OK');
|
||||
}
|
||||
|
||||
async require(id) {
|
||||
return import(`${id}.js`);
|
||||
}
|
||||
|
||||
worker(id) {
|
||||
return new this.window.Worker(`${id}.js`, { type: 'module' });
|
||||
}
|
||||
|
||||
async save(name, extension, defaultPath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
electron.ipcRenderer.once('show-save-dialog-complete', (event, data) => {
|
||||
if (data.error) {
|
||||
reject(new Error(data.error));
|
||||
} else if (data.canceled) {
|
||||
resolve(null);
|
||||
} else {
|
||||
resolve(data.filePath);
|
||||
}
|
||||
});
|
||||
electron.ipcRenderer.send('show-save-dialog', {
|
||||
title: 'Export Tensor',
|
||||
defaultPath,
|
||||
buttonLabel: 'Export',
|
||||
filters: [{ name, extensions: [extension] }]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async export(file, blob) {
|
||||
const window = this.window;
|
||||
const reader = new window.FileReader();
|
||||
reader.onload = (e) => {
|
||||
const data = new Uint8Array(e.target.result);
|
||||
fs.writeFile(file, data, null, async (error) => {
|
||||
if (error) {
|
||||
await this._view.error(error, 'Error writing file.');
|
||||
}
|
||||
});
|
||||
};
|
||||
let error = null;
|
||||
if (!blob) {
|
||||
error = new Error(`Export blob is '${JSON.stringify(blob)}'.`);
|
||||
} else if (blob instanceof window.Blob === false) {
|
||||
error = new Error(`Export blob type is '${typeof blob}'.`);
|
||||
}
|
||||
if (error) {
|
||||
await this._view.error(error, 'Error exporting image.');
|
||||
} else {
|
||||
reader.readAsArrayBuffer(blob);
|
||||
}
|
||||
}
|
||||
|
||||
async execute(name, value) {
|
||||
return new Promise((resolve, reject) => {
|
||||
electron.ipcRenderer.once('execute-complete', (event, data) => {
|
||||
if (data.error) {
|
||||
reject(new Error(data.error));
|
||||
} else {
|
||||
resolve(data.value);
|
||||
}
|
||||
});
|
||||
electron.ipcRenderer.send('execute', { name, value });
|
||||
});
|
||||
}
|
||||
|
||||
async asset(file) {
|
||||
return this.fetch(file, 'utf-8', null);
|
||||
}
|
||||
|
||||
async fetch(file, encoding, basename) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
||||
const root = path.resolve(basename || dirname);
|
||||
const pathname = path.resolve(root, file);
|
||||
const relative = path.relative(root, pathname);
|
||||
if (relative !== '' && (relative.startsWith('..') || path.isAbsolute(relative))) {
|
||||
reject(new Error(`The path '${pathname}' is invalid.`));
|
||||
return;
|
||||
}
|
||||
fs.stat(pathname, (err, stat) => {
|
||||
if (err && err.code === 'ENOENT') {
|
||||
reject(new Error(`The file '${file}' does not exist.`));
|
||||
} else if (err) {
|
||||
reject(err);
|
||||
} else if (!stat.isFile()) {
|
||||
reject(new Error(`The path '${file}' is not a file.`));
|
||||
} else if (stat && stat.size < 0x40000000) {
|
||||
fs.readFile(pathname, encoding, (err, data) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(encoding ? data : new base.BinaryStream(data));
|
||||
}
|
||||
});
|
||||
} else if (encoding) {
|
||||
reject(new Error(`The file '${file}' size (${stat.size.toString()}) for encoding '${encoding}' is greater than 2 GB.`));
|
||||
} else {
|
||||
const stream = new node.FileStream(pathname, 0, stat.size, stat.mtimeMs);
|
||||
resolve(stream);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
openURL(url) {
|
||||
electron.shell.openExternal(url);
|
||||
}
|
||||
|
||||
exception(error, fatal) {
|
||||
if (this._telemetry && error) {
|
||||
try {
|
||||
const name = error.name ? `${error.name}: ` : '';
|
||||
const message = error.message ? error.message : JSON.stringify(error);
|
||||
let context = '';
|
||||
let stack = '';
|
||||
if (error.stack) {
|
||||
const format = (file, line, column) => {
|
||||
return `${file.split('\\').join('/').split('/').pop()}:${line}:${column}`;
|
||||
};
|
||||
const match = error.stack.match(/\n {4}at (.*) \((.*):(\d*):(\d*)\)/);
|
||||
if (match) {
|
||||
stack = `${match[1]} (${format(match[2], match[3], match[4])})`;
|
||||
} else {
|
||||
const match = error.stack.match(/\n {4}at (.*):(\d*):(\d*)/);
|
||||
if (match) {
|
||||
stack = `(${format(match[1], match[2], match[3])})`;
|
||||
} else {
|
||||
const match = error.stack.match(/.*\n\s*(.*)\s*/);
|
||||
if (match) {
|
||||
[, stack] = match;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error.context) {
|
||||
context = typeof error.context === 'string' ? error.context : JSON.stringify(error.context);
|
||||
}
|
||||
this._telemetry.send('exception', {
|
||||
app_name: this.type,
|
||||
app_version: this.version,
|
||||
app_metadata: this.metadata,
|
||||
error_name: name,
|
||||
error_message: message,
|
||||
error_context: context,
|
||||
error_stack: stack,
|
||||
error_fatal: fatal ? true : false
|
||||
});
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
event(name, params) {
|
||||
if (name && params) {
|
||||
params.app_name = this.type;
|
||||
params.app_version = this.version;
|
||||
params.app_metadata = this.metadata;
|
||||
this._telemetry.send(name, params);
|
||||
}
|
||||
}
|
||||
|
||||
async _context(location) {
|
||||
const basename = path.basename(location);
|
||||
const stat = fs.statSync(location);
|
||||
if (stat.isFile()) {
|
||||
const dirname = path.dirname(location);
|
||||
const stream = await this.fetch(basename, null, dirname);
|
||||
return new desktop.Context(this, dirname, basename, stream);
|
||||
} else if (stat.isDirectory()) {
|
||||
const entries = new Map();
|
||||
const walk = (dir) => {
|
||||
for (const item of fs.readdirSync(dir)) {
|
||||
const pathname = path.join(dir, item);
|
||||
const stat = fs.statSync(pathname);
|
||||
if (stat.isDirectory()) {
|
||||
walk(pathname);
|
||||
} else if (stat.isFile()) {
|
||||
const stream = new node.FileStream(pathname, 0, stat.size, stat.mtimeMs);
|
||||
const name = pathname.split(path.sep).join(path.posix.sep);
|
||||
entries.set(name, stream);
|
||||
}
|
||||
}
|
||||
};
|
||||
walk(location);
|
||||
return new desktop.Context(this, location, basename, null, entries);
|
||||
}
|
||||
throw new Error(`Unsupported path stat '${JSON.stringify(stat)}'.`);
|
||||
}
|
||||
|
||||
async _open(location) {
|
||||
if (this._files) {
|
||||
this._files.push(location);
|
||||
return;
|
||||
}
|
||||
const path = location.path;
|
||||
const stat = fs.existsSync(path) ? fs.statSync(path) : null;
|
||||
const size = stat && stat.isFile() ? stat.size : 0;
|
||||
if (path && this._view.accept(path, size)) {
|
||||
this._view.show('welcome spinner');
|
||||
let context = null;
|
||||
try {
|
||||
context = await this._context(path);
|
||||
this._telemetry.set('session_engaged', 1);
|
||||
} catch (error) {
|
||||
await this._view.error(error, 'Error while reading file.');
|
||||
this.update({ path: null });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const attachment = await this._view.attach(context);
|
||||
if (attachment) {
|
||||
this._view.show(null);
|
||||
} else {
|
||||
const model = await this._view.open(context);
|
||||
this._view.show(null);
|
||||
const options = { ...this._view.options };
|
||||
if (model) {
|
||||
options.path = path;
|
||||
this._title(location.label);
|
||||
} else {
|
||||
options.path = path;
|
||||
this._title('');
|
||||
}
|
||||
electron.ipcRenderer.send('update-recents', { path });
|
||||
this.update(options);
|
||||
}
|
||||
} catch (error) {
|
||||
const options = { ...this._view.options };
|
||||
if (error) {
|
||||
await this._view.error(error);
|
||||
}
|
||||
this.update(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_request(location, headers, timeout) {
|
||||
const window = this.window;
|
||||
return new Promise((resolve, reject) => {
|
||||
const url = new window.URL(location);
|
||||
const protocol = url.protocol === 'https:' ? https : http;
|
||||
const options = {};
|
||||
options.headers = headers;
|
||||
if (timeout) {
|
||||
options.timeout = timeout;
|
||||
}
|
||||
const request = protocol.request(location, options, (response) => {
|
||||
if (response.statusCode === 200) {
|
||||
let data = '';
|
||||
response.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
response.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
response.on('end', () => {
|
||||
resolve(data);
|
||||
});
|
||||
} else {
|
||||
const error = new Error(`The web request failed with status code '${response.statusCode}'.`);
|
||||
error.context = location;
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
request.on("error", (err) => {
|
||||
reject(err);
|
||||
});
|
||||
request.on("timeout", () => {
|
||||
request.destroy();
|
||||
const error = new Error('The web request timed out.');
|
||||
error.context = url;
|
||||
reject(error);
|
||||
});
|
||||
request.end();
|
||||
});
|
||||
}
|
||||
|
||||
get(name) {
|
||||
try {
|
||||
return electron.ipcRenderer.sendSync('get-configuration', { name });
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
set(name, value) {
|
||||
try {
|
||||
electron.ipcRenderer.sendSync('set-configuration', { name, value });
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
}
|
||||
|
||||
delete(name) {
|
||||
try {
|
||||
electron.ipcRenderer.sendSync('delete-configuration', { name });
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
}
|
||||
|
||||
_title(label) {
|
||||
const element = this._element('titlebar-content-text');
|
||||
if (element) {
|
||||
element.innerHTML = '';
|
||||
if (label) {
|
||||
const path = label.split(this._environment.separator || '/');
|
||||
for (let i = 0; i < path.length; i++) {
|
||||
const span = this.document.createElement('span');
|
||||
span.innerHTML = ` ${path[i]} ${i === path.length - 1 ? '' : '<svg class="titlebar-icon" aria-hidden="true"><use xlink:href="#icon-arrow-right"></use></svg>'}`;
|
||||
element.appendChild(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_element(id) {
|
||||
return this.document.getElementById(id);
|
||||
}
|
||||
|
||||
update(data) {
|
||||
electron.ipcRenderer.send('window-update', data);
|
||||
}
|
||||
|
||||
async message(message, alert, action) {
|
||||
return new Promise((resolve) => {
|
||||
const type = this.document.body.getAttribute('class');
|
||||
this._element('message-text').innerText = message || '';
|
||||
const button = this._element('message-button');
|
||||
if (action) {
|
||||
button.style.removeProperty('display');
|
||||
button.innerText = action;
|
||||
button.onclick = () => {
|
||||
button.onclick = null;
|
||||
this.document.body.setAttribute('class', type);
|
||||
resolve(0);
|
||||
};
|
||||
} else {
|
||||
button.style.display = 'none';
|
||||
button.onclick = null;
|
||||
}
|
||||
if (alert) {
|
||||
this.document.body.setAttribute('class', 'alert');
|
||||
} else {
|
||||
this.document.body.classList.add('notification');
|
||||
this.document.body.classList.remove('default');
|
||||
}
|
||||
if (action) {
|
||||
button.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
desktop.Context = class {
|
||||
|
||||
constructor(host, folder, identifier, stream, entries) {
|
||||
this._host = host;
|
||||
this._folder = folder;
|
||||
this._identifier = identifier;
|
||||
this._stream = stream;
|
||||
this._entries = entries || new Map();
|
||||
}
|
||||
|
||||
get identifier() {
|
||||
return this._identifier;
|
||||
}
|
||||
|
||||
get stream() {
|
||||
return this._stream;
|
||||
}
|
||||
|
||||
get entries() {
|
||||
return this._entries;
|
||||
}
|
||||
|
||||
async asset(file) {
|
||||
return this._host.asset(file);
|
||||
}
|
||||
|
||||
async fetch(file, encoding, base) {
|
||||
return this._host.fetch(file, encoding, base === undefined ? this._folder : base);
|
||||
}
|
||||
|
||||
async require(id) {
|
||||
return this._host.require(id);
|
||||
}
|
||||
|
||||
error(error, fatal) {
|
||||
this._host.exception(error, fatal);
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.addEventListener('load', () => {
|
||||
const value = new desktop.Host();
|
||||
window.__view__ = new view.View(value);
|
||||
window.__view__.start();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
[
|
||||
{
|
||||
"name": "BatchNormalization",
|
||||
"category": "Normalization",
|
||||
"attributes": [
|
||||
{ "name": "eps" },
|
||||
{ "name": "gamma" },
|
||||
{ "name": "decay" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Convolution",
|
||||
"category": "Layer",
|
||||
"attributes": [
|
||||
{ "name": "dilation" },
|
||||
{ "name": "kernelSize" },
|
||||
{ "name": "padding" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Dense",
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "Dropout",
|
||||
"category": "Dropout"
|
||||
},
|
||||
{
|
||||
"name": "GlobalPooling",
|
||||
"category": "Pool"
|
||||
},
|
||||
{
|
||||
"name": "LReLU",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "Merge",
|
||||
"category": "Tensor"
|
||||
},
|
||||
{
|
||||
"name": "Output",
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "ReLU",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "SeparableConvolution2D",
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "Sigmoid",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "Softmax",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "Subsampling",
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "TanH",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "Upsampling2D",
|
||||
"category": "Layer"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,433 @@
|
||||
|
||||
// Experimental
|
||||
|
||||
const dl4j = {};
|
||||
|
||||
dl4j.ModelFactory = class {
|
||||
|
||||
async match(context) {
|
||||
const identifier = context.identifier;
|
||||
if (identifier === 'configuration.json') {
|
||||
const obj = await context.peek('json');
|
||||
if (obj && (obj.confs || obj.vertices)) {
|
||||
return context.set('dl4j.configuration', obj);
|
||||
}
|
||||
} else if (identifier === 'coefficients.bin') {
|
||||
const signature = [0x00, 0x07, 0x4A, 0x41, 0x56, 0x41, 0x43, 0x50, 0x50]; // JAVACPP
|
||||
const stream = context.stream;
|
||||
if (signature.length <= stream.length && stream.peek(signature.length).every((value, index) => value === signature[index])) {
|
||||
return context.set('dl4j.coefficients');
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
filter(context, match) {
|
||||
return context.type !== 'dl4j.configuration' || (match.type !== 'dl4j.coefficients' && match.type !== 'openvino.bin');
|
||||
}
|
||||
|
||||
async open(context) {
|
||||
const metadata = await context.metadata('dl4j-metadata.json');
|
||||
switch (context.type) {
|
||||
case 'dl4j.configuration': {
|
||||
const obj = context.value;
|
||||
try {
|
||||
const content = await context.fetch('coefficients.bin');
|
||||
const reader = await content.read('binary.big-endian');
|
||||
return new dl4j.Model(metadata, obj, reader);
|
||||
} catch {
|
||||
return new dl4j.Model(metadata, obj, null);
|
||||
}
|
||||
}
|
||||
case 'dl4j.coefficients': {
|
||||
const content = await context.fetch('configuration.json');
|
||||
const obj = await content.read('json');
|
||||
const reader = await context.read('binary.big-endian');
|
||||
return new dl4j.Model(metadata, obj, reader);
|
||||
}
|
||||
default: {
|
||||
throw new dl4j.Error(`Unsupported Deeplearning4j format '${context.type}'.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dl4j.Model = class {
|
||||
|
||||
constructor(metadata, configuration, coefficients) {
|
||||
this.format = 'Deeplearning4j';
|
||||
this.modules = [new dl4j.Graph(metadata, configuration, coefficients)];
|
||||
}
|
||||
};
|
||||
|
||||
dl4j.Graph = class {
|
||||
|
||||
constructor(metadata, configuration, coefficients) {
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.nodes = [];
|
||||
coefficients = coefficients ? new dl4j.NDArray(coefficients) : null;
|
||||
const dataType = coefficients ? coefficients.dataType : '?';
|
||||
const values = new Map();
|
||||
values.map = (name, type, tensor) => {
|
||||
if (name.length === 0 && tensor) {
|
||||
return new dl4j.Value(name, type || null, tensor);
|
||||
}
|
||||
if (!values.has(name)) {
|
||||
values.set(name, new dl4j.Value(name, type || null, tensor || null));
|
||||
} else if (type || tensor) {
|
||||
throw new dl4j.Error(`Duplicate value '${name}'.`);
|
||||
}
|
||||
return values.get(name);
|
||||
};
|
||||
if (configuration.networkInputs) {
|
||||
for (const input of configuration.networkInputs) {
|
||||
const value = values.map(input);
|
||||
const argument = new dl4j.Argument(input, [value]);
|
||||
this.inputs.push(argument);
|
||||
}
|
||||
}
|
||||
if (configuration.networkOutputs) {
|
||||
for (const output of configuration.networkOutputs) {
|
||||
const value = values.map(output);
|
||||
const argument = new dl4j.Argument(output, [value]);
|
||||
this.outputs.push(argument);
|
||||
}
|
||||
}
|
||||
let inputs = null;
|
||||
// Computation Graph
|
||||
if (configuration.vertices) {
|
||||
for (const [name,obj] of Object.entries(configuration.vertices)) {
|
||||
const vertex = dl4j.Node._object(obj);
|
||||
inputs = configuration.vertexInputs[name];
|
||||
let variables = [];
|
||||
let layer = null;
|
||||
switch (vertex.__type__) {
|
||||
case 'LayerVertex':
|
||||
layer = dl4j.Node._object(vertex.layerConf.layer);
|
||||
variables = vertex.layerConf.variables;
|
||||
break;
|
||||
case 'MergeVertex':
|
||||
layer = { __type__: 'Merge', layerName: name };
|
||||
break;
|
||||
case 'ElementWiseVertex':
|
||||
layer = { __type__: 'ElementWise', layerName: name, op: vertex.op };
|
||||
break;
|
||||
case 'PreprocessorVertex':
|
||||
layer = { __type__: 'Preprocessor', layerName: name };
|
||||
break;
|
||||
default:
|
||||
throw new dl4j.Error(`Unsupported vertex class '${vertex['@class']}'.`);
|
||||
}
|
||||
const node = new dl4j.Node(metadata, layer, inputs, dataType, variables, values);
|
||||
this.nodes.push(node);
|
||||
}
|
||||
}
|
||||
// Multi Layer Network
|
||||
if (configuration.confs) {
|
||||
inputs = ['input'];
|
||||
this.inputs.push(new dl4j.Argument('input', [values.map('input')]));
|
||||
for (const conf of configuration.confs) {
|
||||
const layer = dl4j.Node._object(conf.layer);
|
||||
const node = new dl4j.Node(metadata, layer, inputs, dataType, conf.variables, values);
|
||||
this.nodes.push(node);
|
||||
inputs = [layer.layerName];
|
||||
}
|
||||
if (inputs && inputs.length > 0) {
|
||||
const argument = new dl4j.Argument('output', [values.map(inputs[0])]);
|
||||
this.outputs.push(argument);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dl4j.Argument = class {
|
||||
|
||||
constructor(name, value, visible = true) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.visible = visible;
|
||||
}
|
||||
};
|
||||
|
||||
dl4j.Value = class {
|
||||
|
||||
constructor(name, type, initializer) {
|
||||
if (typeof name !== 'string') {
|
||||
throw new dl4j.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
|
||||
}
|
||||
this.name = name;
|
||||
this.type = initializer ? initializer.type : type;
|
||||
this.initializer = initializer;
|
||||
}
|
||||
};
|
||||
|
||||
dl4j.Node = class {
|
||||
|
||||
constructor(metadata, layer, inputs, dataType, variables, values) {
|
||||
this.name = layer.layerName || '';
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.attributes = [];
|
||||
const type = layer.__type__;
|
||||
this.type = metadata.type(type) || { name: type };
|
||||
if (inputs && inputs.length > 0) {
|
||||
const argument = new dl4j.Argument(values.length < 2 ? 'input' : 'inputs', inputs.map((input) => values.map(input)));
|
||||
this.inputs.push(argument);
|
||||
}
|
||||
if (variables) {
|
||||
for (const variable of variables) {
|
||||
let tensor = null;
|
||||
switch (type) {
|
||||
case 'Convolution':
|
||||
switch (variable) {
|
||||
case 'W':
|
||||
tensor = new dl4j.Tensor(dataType, layer.kernelSize.concat([layer.nin, layer.nout]));
|
||||
break;
|
||||
case 'b':
|
||||
tensor = new dl4j.Tensor(dataType, [layer.nout]);
|
||||
break;
|
||||
default:
|
||||
throw new dl4j.Error(`Unsupported '${type}' variable '${variable}'.`);
|
||||
}
|
||||
break;
|
||||
case 'SeparableConvolution2D':
|
||||
switch (variable) {
|
||||
case 'W':
|
||||
tensor = new dl4j.Tensor(dataType, layer.kernelSize.concat([layer.nin, layer.nout]));
|
||||
break;
|
||||
case 'pW':
|
||||
tensor = new dl4j.Tensor(dataType, [layer.nout]);
|
||||
break;
|
||||
default:
|
||||
throw new dl4j.Error(`Unsupported '${type}' variable '${variable}'.`);
|
||||
}
|
||||
break;
|
||||
case 'Output':
|
||||
case 'Dense':
|
||||
switch (variable) {
|
||||
case 'W':
|
||||
tensor = new dl4j.Tensor(dataType, [layer.nout, layer.nin]);
|
||||
break;
|
||||
case 'b':
|
||||
tensor = new dl4j.Tensor(dataType, [layer.nout]);
|
||||
break;
|
||||
default:
|
||||
throw new dl4j.Error(`Unsupported '${this.type}' variable '${variable}'.`);
|
||||
}
|
||||
break;
|
||||
case 'BatchNormalization':
|
||||
tensor = new dl4j.Tensor(dataType, [layer.nin]);
|
||||
break;
|
||||
default:
|
||||
throw new dl4j.Error(`Unsupported '${type}' variable '${variable}'.`);
|
||||
}
|
||||
const argument = new dl4j.Argument(variable, [values.map('', null, tensor)]);
|
||||
this.inputs.push(argument);
|
||||
}
|
||||
}
|
||||
if (this.name) {
|
||||
const value = values.map(this.name);
|
||||
const argument = new dl4j.Argument('output', [value]);
|
||||
this.outputs.push(argument);
|
||||
}
|
||||
let attributes = layer;
|
||||
if (layer.activationFn) {
|
||||
const activation = dl4j.Node._object(layer.activationFn);
|
||||
if (activation.__type__ !== 'ActivationIdentity' && activation.__type__ !== 'Identity') {
|
||||
if (activation.__type__.startsWith('Activation')) {
|
||||
activation.__type__ = activation.__type__.substring('Activation'.length);
|
||||
}
|
||||
if (this.type === 'Activation') {
|
||||
this.type = activation.__type__;
|
||||
attributes = activation;
|
||||
} else {
|
||||
this.chain = this.chain || [];
|
||||
this.chain.push(new dl4j.Node(metadata, activation, [], null, null, values));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const [name, value] of Object.entries(attributes)) {
|
||||
switch (name) {
|
||||
case '__type__':
|
||||
case 'constraints':
|
||||
case 'layerName':
|
||||
case 'activationFn':
|
||||
case 'idropout':
|
||||
case 'hasBias':
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const definition = metadata.attribute(type, name);
|
||||
const visible = definition && definition.visible === false ? false : true;
|
||||
const attribute = new dl4j.Argument(name, value, visible);
|
||||
this.attributes.push(attribute);
|
||||
}
|
||||
if (layer.idropout) {
|
||||
const dropout = dl4j.Node._object(layer.idropout);
|
||||
if (dropout.p !== 1.0) {
|
||||
throw new dl4j.Error("Layer 'idropout' not implemented.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static _object(value) {
|
||||
let result = {};
|
||||
if (value['@class']) {
|
||||
result = value;
|
||||
let type = value['@class'].split('.').pop();
|
||||
if (type.endsWith('Layer')) {
|
||||
type = type.substring(0, type.length - 5);
|
||||
}
|
||||
delete value['@class'];
|
||||
result.__type__ = type;
|
||||
} else {
|
||||
let [key] = Object.keys(value);
|
||||
result = value[key];
|
||||
if (key.length > 0) {
|
||||
key = key[0].toUpperCase() + key.substring(1);
|
||||
}
|
||||
result.__type__ = key;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
dl4j.Tensor = class {
|
||||
|
||||
constructor(dataType, shape) {
|
||||
this.type = new dl4j.TensorType(dataType, new dl4j.TensorShape(shape));
|
||||
}
|
||||
};
|
||||
|
||||
dl4j.TensorType = class {
|
||||
|
||||
constructor(dataType, shape) {
|
||||
this.dataType = dataType;
|
||||
this.shape = shape;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return (this.dataType || '?') + this.shape.toString();
|
||||
}
|
||||
};
|
||||
|
||||
dl4j.TensorShape = class {
|
||||
|
||||
constructor(dimensions) {
|
||||
this.dimensions = dimensions;
|
||||
}
|
||||
|
||||
toString() {
|
||||
if (this.dimensions) {
|
||||
if (this.dimensions.length === 0) {
|
||||
return '';
|
||||
}
|
||||
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
dl4j.NDArray = class {
|
||||
|
||||
constructor(reader) {
|
||||
reader = new dl4j.BinaryReader(reader);
|
||||
const readHeader = (reader) => {
|
||||
const alloc = reader.string();
|
||||
let length = 0;
|
||||
switch (alloc) {
|
||||
case 'DIRECT':
|
||||
case 'HEAP':
|
||||
case 'JAVACPP':
|
||||
length = reader.int32();
|
||||
break;
|
||||
case 'LONG_SHAPE':
|
||||
case 'MIXED_DATA_TYPES':
|
||||
length = reader.int64().toNumber();
|
||||
break;
|
||||
default:
|
||||
throw new dl4j.Error(`Unsupported header alloc '${alloc}'.`);
|
||||
}
|
||||
const type = reader.string();
|
||||
return [alloc, length, type];
|
||||
};
|
||||
const headerShape = readHeader(reader);
|
||||
if (headerShape[2] !== 'INT') {
|
||||
throw new dl4j.Error(`Unsupported header shape type '${headerShape[2]}'.`);
|
||||
}
|
||||
const shapeInfo = new Array(headerShape[1]);
|
||||
for (let i = 0; i < shapeInfo.length; i++) {
|
||||
shapeInfo[i] = reader.int32();
|
||||
}
|
||||
const [rank] = shapeInfo;
|
||||
const shapeInfoLength = rank * 2 + 4;
|
||||
this.shape = shapeInfo.slice(1, 1 + rank);
|
||||
this.strides = shapeInfo.slice(1 + rank, 1 + (rank * 2));
|
||||
this.order = shapeInfo[shapeInfoLength - 1];
|
||||
const headerData = readHeader(reader);
|
||||
const dataTypes = new Map([
|
||||
['INT', ['int32', 4]],
|
||||
['FLOAT', ['float32', 4]],
|
||||
['DOUBLE', ['float64', 8]]
|
||||
]);
|
||||
if (!dataTypes.has(headerData[2])) {
|
||||
throw new dl4j.Error(`Unsupported header data type '${headerData[2]}'.`);
|
||||
}
|
||||
const [dataType, itemSize] = dataTypes.get(headerData[2]);
|
||||
this.dataType = dataType;
|
||||
const size = headerData[1] * itemSize;
|
||||
if ((reader.position + size) <= reader.length) {
|
||||
this.data = reader.read(size);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dl4j.BinaryReader = class {
|
||||
|
||||
constructor(reader) {
|
||||
this._reader = reader;
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this._reader.length;
|
||||
}
|
||||
|
||||
get position() {
|
||||
return this._reader.position;
|
||||
}
|
||||
|
||||
read(length) {
|
||||
return this._reader.read(length);
|
||||
}
|
||||
|
||||
int32() {
|
||||
return this._reader.int32();
|
||||
}
|
||||
|
||||
int64() {
|
||||
return this._reader.int64();
|
||||
}
|
||||
|
||||
uint16() {
|
||||
return this._reader.uint16();
|
||||
}
|
||||
|
||||
string() {
|
||||
const size = this.uint16();
|
||||
const buffer = this.read(size);
|
||||
this._decoder = this._decoder || new TextDecoder('ascii');
|
||||
return this._decoder.decode(buffer);
|
||||
}
|
||||
};
|
||||
|
||||
dl4j.Error = class extends Error {
|
||||
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'Error loading Deeplearning4j model.';
|
||||
}
|
||||
};
|
||||
|
||||
export const ModelFactory = dl4j.ModelFactory;
|
||||
@@ -0,0 +1,146 @@
|
||||
[
|
||||
{
|
||||
"name": "BatchNorm",
|
||||
"category": "Normalization"
|
||||
},
|
||||
{
|
||||
"name": "Batchnorm",
|
||||
"category": "Normalization",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "mean" },
|
||||
{ "name": "variance" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Concat",
|
||||
"category": "Tensor"
|
||||
},
|
||||
{
|
||||
"name": "Conv2d",
|
||||
"category": "Layer",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "weight" },
|
||||
{ "name": "bias" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Convolutional",
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "Deconvolution",
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "DepthWiseConv2d",
|
||||
"category": "Layer",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "weight" },
|
||||
{ "name": "bias" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "FullyConnected",
|
||||
"category": "Layer",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "weight" },
|
||||
{ "name": "bias" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Gather",
|
||||
"category": "Transform",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "indices" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Neuron",
|
||||
"category": "Activation",
|
||||
"attributes": [
|
||||
{ "name": "type", "type": "Activation" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Pad",
|
||||
"category": "Shape"
|
||||
},
|
||||
{
|
||||
"name": "Permute",
|
||||
"category": "Shape"
|
||||
},
|
||||
{
|
||||
"name": "Pool",
|
||||
"category": "Pool"
|
||||
},
|
||||
{
|
||||
"name": "PoolAvg2d",
|
||||
"category": "Pool"
|
||||
},
|
||||
{
|
||||
"name": "Pooling",
|
||||
"category": "Pool"
|
||||
},
|
||||
{
|
||||
"name": "PoolMax2d",
|
||||
"category": "Pool"
|
||||
},
|
||||
{
|
||||
"name": "Prelu",
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "weight" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Relu",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "Reshape",
|
||||
"category": "Shape"
|
||||
},
|
||||
{
|
||||
"name": "Sigmoid",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "Slice",
|
||||
"category": "Tensor"
|
||||
},
|
||||
{
|
||||
"name": "SoftMax",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "Softmax",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "Split",
|
||||
"category": "Tensor"
|
||||
},
|
||||
{
|
||||
"name": "StridedSlice",
|
||||
"category": "Tensor"
|
||||
},
|
||||
{
|
||||
"name": "Transpose",
|
||||
"category": "Transform"
|
||||
},
|
||||
{
|
||||
"name": "TransposeConv2d",
|
||||
"category": "Layer",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "weight" },
|
||||
{ "name": "bias" }
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,283 @@
|
||||
|
||||
export const dlc = {};
|
||||
|
||||
dlc.v3 = dlc.v3 || {};
|
||||
|
||||
dlc.v3.Model = class Model {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v3.Model();
|
||||
$.unk1 = reader.int32_(position, 4, 0);
|
||||
$.nodes = reader.tables(position, 6, dlc.v3.Node);
|
||||
$.unk2 = reader.array(position, 8, Int32Array);
|
||||
$.unk3 = reader.array(position, 10, Int32Array);
|
||||
$.attributes = reader.tables(position, 12, dlc.v3.Attribute);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v3.Node = class Node {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v3.Node();
|
||||
$.index = reader.int32_(position, 4, 0);
|
||||
$.name = reader.string_(position, 6, null);
|
||||
$.type = reader.string_(position, 8, null);
|
||||
$.inputs = reader.strings_(position, 10);
|
||||
$.outputs = reader.strings_(position, 12);
|
||||
$.attributes = reader.tables(position, 14, dlc.v3.Attribute);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v3.Tensor = class Tensor {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v3.Tensor();
|
||||
$.name = reader.string_(position, 4, null);
|
||||
$.shape = reader.array(position, 6, Int32Array);
|
||||
$.data = reader.table(position, 8, dlc.v3.TensorData);
|
||||
$.attributes = reader.tables(position, 10, dlc.v3.Attribute);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v3.TensorData = class TensorData {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v3.TensorData();
|
||||
$.dtype = reader.uint8_(position, 4, 0);
|
||||
$.bytes = reader.array(position, 6, Uint8Array);
|
||||
$.floats = reader.array(position, 8, Float32Array);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v3.Attribute = class Attribute {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v3.Attribute();
|
||||
$.name = reader.string_(position, 4, null);
|
||||
$.type = reader.uint8_(position, 6, 0);
|
||||
$.bool_value = reader.bool_(position, 8, false);
|
||||
$.int32_value = reader.int32_(position, 10, 0);
|
||||
$.uint32_value = reader.uint32_(position, 12, 0);
|
||||
$.float32_value = reader.float32_(position, 14, 0);
|
||||
$.string_value = reader.string_(position, 16, null);
|
||||
$.unk6 = reader.array(position, 18, Int8Array);
|
||||
$.byte_list = reader.array(position, 20, Int8Array);
|
||||
$.int32_list = reader.array(position, 22, Int32Array);
|
||||
$.float32_list = reader.array(position, 24, Float32Array);
|
||||
$.unk10 = reader.array(position, 26, Int8Array);
|
||||
$.attributes = reader.tables(position, 28, dlc.v3.Attribute);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v3.Activation = {
|
||||
ReLU: 1, '1': 'ReLU',
|
||||
Sigmoid: 3, '3': 'Sigmoid'
|
||||
};
|
||||
|
||||
dlc.v3.ModelParameters = class ModelParameters {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v3.ModelParameters();
|
||||
$.nodes = reader.tables(position, 4, dlc.v3.NodeParameters);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v3.NodeParameters = class NodeParameters {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v3.NodeParameters();
|
||||
$.name = reader.string_(position, 4, null);
|
||||
$.weights = reader.tables(position, 6, dlc.v3.Tensor);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v4 = dlc.v4 || {};
|
||||
|
||||
dlc.v4.Model = class Model {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v4.Model();
|
||||
$.graphs = reader.tables(position, 4, dlc.v4.Graph);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v4.Graph = class Graph {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v4.Graph();
|
||||
$.name = reader.string_(position, 4, null);
|
||||
$.nodes = reader.tables(position, 6, dlc.v4.Node);
|
||||
$.tensors = reader.tables(position, 8, dlc.v4.Tensor);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v4.Node = class Node {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v4.Node();
|
||||
$.name = reader.string_(position, 4, null);
|
||||
$.type = reader.string_(position, 6, null);
|
||||
$.inputs = reader.strings_(position, 8);
|
||||
$.outputs = reader.strings_(position, 10);
|
||||
$.attributes = reader.tables(position, 12, dlc.v4.Attribute);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v4.Attribute = class Attribute {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v4.Attribute();
|
||||
$.name = reader.string_(position, 4, null);
|
||||
$.kind = reader.int32_(position, 6, 0);
|
||||
$.flag = reader.uint8_(position, 8, 0);
|
||||
$.value = reader.table(position, 10, dlc.v4.Value);
|
||||
$.tensor = reader.table(position, 12, dlc.v4.Tensor);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v4.Value = class Value {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v4.Value();
|
||||
$.kind = reader.int32_(position, 4, 0);
|
||||
$.int32_value = reader.int32_(position, 6, 0);
|
||||
$.float32_value = reader.float32_(position, 8, 0);
|
||||
$.string_value = reader.string_(position, 10, null);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v4.Tensor = class Tensor {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v4.Tensor();
|
||||
$.unk1 = reader.uint32_(position, 4, 0);
|
||||
$.name = reader.string_(position, 6, null);
|
||||
$.location = reader.int32_(position, 8, 0);
|
||||
$.shape = reader.array(position, 10, Int32Array);
|
||||
$.unk2 = reader.int32_(position, 12, 0);
|
||||
$.info = reader.table(position, 14, dlc.v4.TensorInfo);
|
||||
$.dtype = reader.int32_(position, 16, 0);
|
||||
$.output_dtype = reader.int32_(position, 18, 0);
|
||||
$.unk6 = reader.uint8_(position, 20, 0);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v4.TensorInfo = class TensorInfo {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v4.TensorInfo();
|
||||
$.i1 = reader.int32_(position, 4, 0);
|
||||
$.b1 = reader.uint8_(position, 6, 0);
|
||||
$.a = reader.table(position, 8, dlc.v4.TensorInfo1);
|
||||
$.b = reader.table(position, 10, dlc.v4.TensorInfo2);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v4.TensorInfo1 = class TensorInfo1 {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v4.TensorInfo1();
|
||||
$.i1 = reader.int32_(position, 4, 0);
|
||||
$.f1 = reader.float32_(position, 6, 0);
|
||||
$.f2 = reader.float32_(position, 8, 0);
|
||||
$.f3 = reader.float32_(position, 10, 0);
|
||||
$.i2 = reader.int32_(position, 12, 0);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v4.TensorInfo2 = class TensorInfo2 {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v4.TensorInfo2();
|
||||
$.i1 = reader.int32_(position, 4, 0);
|
||||
$.l = reader.tables(position, 6, dlc.v4.TensorInfo3);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v4.TensorInfo3 = class TensorInfo3 {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v4.TensorInfo3();
|
||||
$.i1 = reader.int32_(position, 4, 0);
|
||||
$.f1 = reader.float32_(position, 6, 0);
|
||||
$.f2 = reader.float32_(position, 8, 0);
|
||||
$.f3 = reader.float32_(position, 10, 0);
|
||||
$.i2 = reader.int32_(position, 12, 0);
|
||||
$.b1 = reader.uint8_(position, 14, 0);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v4.ModelParameters64 = class ModelParameters64 {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v4.ModelParameters64();
|
||||
$.buffers = reader.tables(position, 4, dlc.v4.Buffer);
|
||||
$.params = reader.array(position, 6, Uint8Array);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v4.ModelParameters = class ModelParameters {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v4.ModelParameters();
|
||||
$.graphs = reader.tables(position, 4, dlc.v4.GraphParameters);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v4.GraphParameters = class GraphParameters {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v4.GraphParameters();
|
||||
$.name = reader.string_(position, 4, null);
|
||||
$.tensors = reader.tables(position, 6, dlc.v4.TensorData);
|
||||
$.nodes = reader.tables(position, 8, dlc.v4.NodeParameters);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v4.NodeParameters = class NodeParameters {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v4.NodeParameters();
|
||||
$.tensors = reader.tables(position, 4, dlc.v4.TensorData);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v4.TensorData = class TensorData {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v4.TensorData();
|
||||
$.name = reader.string_(position, 4, null);
|
||||
$.bytes = reader.array(position, 6, Uint8Array);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.v4.Buffer = class Buffer {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new dlc.v4.Buffer();
|
||||
$.bytes = reader.array(position, 4, Uint8Array);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,679 @@
|
||||
|
||||
import * as flatbuffers from './flatbuffers.js';
|
||||
import * as text from './text.js';
|
||||
|
||||
const dlc = {};
|
||||
|
||||
dlc.ModelFactory = class {
|
||||
|
||||
async match(context) {
|
||||
const container = await dlc.Container.open(context);
|
||||
if (container) {
|
||||
return context.set('dlc', container);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async open(context) {
|
||||
dlc.schema = await context.require('./dlc-schema');
|
||||
dlc.schema = dlc.schema.dlc;
|
||||
await context.value.read();
|
||||
const metadata = await context.metadata('dlc-metadata.json');
|
||||
return new dlc.Model(metadata, context.value);
|
||||
}
|
||||
};
|
||||
|
||||
dlc.Model = class {
|
||||
|
||||
constructor(metadata, target) {
|
||||
this.format = target.format;
|
||||
this.metadata = [];
|
||||
if (target.metadata.size > 0) {
|
||||
const version = target.metadata.get('model-version');
|
||||
if (version) {
|
||||
this.version = version;
|
||||
}
|
||||
const converter = target.metadata.get('converter-command');
|
||||
if (converter) {
|
||||
const source = converter.split(' ').shift().trim();
|
||||
if (source.length > 0) {
|
||||
const version = target.metadata.get('converter-version');
|
||||
this.source = version ? `${source} v${version}` : source;
|
||||
}
|
||||
}
|
||||
const license = target.metadata.get('model-copyright');
|
||||
if (license && license !== 'N/A') {
|
||||
this.metadata.push(new dlc.Argument('license', license));
|
||||
}
|
||||
}
|
||||
for (const graph of target.graphs) {
|
||||
this.modules = [new dlc.Graph(metadata, target.version.major, graph)];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dlc.Graph = class {
|
||||
|
||||
constructor(metadata, version, graph) {
|
||||
this.name = graph.name;
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
const values = new Map();
|
||||
switch (version) {
|
||||
case 3: {
|
||||
for (const node of graph.nodes) {
|
||||
for (const name of node.inputs) {
|
||||
if (!values.has(name)) {
|
||||
values.set(name, {});
|
||||
}
|
||||
}
|
||||
for (const name of node.outputs) {
|
||||
if (!values.has(name)) {
|
||||
values.set(name, {});
|
||||
}
|
||||
}
|
||||
let shapes = new Array(node.outputs.length);
|
||||
for (const attribute of node.attributes) {
|
||||
if (attribute.name === 'OutputDims' &&
|
||||
Array.isArray(attribute.attributes) && attribute.attributes.length > 0) {
|
||||
shapes = attribute.data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < node.outputs.length; i++) {
|
||||
const name = node.outputs[i];
|
||||
const value = values.get(name);
|
||||
if (!value.shape && i < shapes.length) {
|
||||
value.shape = shapes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
for (const tensor of graph.tensors) {
|
||||
values.set(tensor.name, tensor);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (const [name, tensor] of values) {
|
||||
const type = tensor.shape ? new dlc.TensorType(tensor.dtype, tensor.shape) : null;
|
||||
const initializer = tensor.data && tensor.data ? new dlc.Tensor(tensor.name, type, tensor.data) : null;
|
||||
const value = new dlc.Value(name, type, initializer);
|
||||
values.set(name, value);
|
||||
}
|
||||
const value = (name) => {
|
||||
if (!values.has(name)) {
|
||||
values.set(name, new dlc.Value(name));
|
||||
}
|
||||
return values.get(name);
|
||||
};
|
||||
this.nodes = [];
|
||||
for (const node of graph.nodes) {
|
||||
if (node.type === 'Input') {
|
||||
this.inputs.push(new dlc.Argument(node.name, node.inputs.map((input) => value(input))));
|
||||
continue;
|
||||
}
|
||||
this.nodes.push(new dlc.Node(metadata, version, node, value));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dlc.Argument = class {
|
||||
|
||||
constructor(name, value, type = null) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.Value = class {
|
||||
|
||||
constructor(name, type, initializer) {
|
||||
if (typeof name !== 'string') {
|
||||
throw new dlc.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
|
||||
}
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.initializer = initializer;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.Node = class {
|
||||
|
||||
constructor(metadata, version, obj, value) {
|
||||
this.type = metadata.type(obj.type);
|
||||
this.name = obj.name;
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.attributes = [];
|
||||
const inputs = Array.isArray(obj.inputs) ? Array.from(obj.inputs).map((name) => value(name)) : [];
|
||||
if (version !== 3 && Array.isArray(this.type.inputs) && inputs.length === this.type.inputs.length) {
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
const argument = new dlc.Argument(this.type.inputs[i].name, [inputs[i]]);
|
||||
this.inputs.push(argument);
|
||||
}
|
||||
} else if (inputs.length > 0) {
|
||||
const argument = new dlc.Argument(inputs.length === 1 ? 'input' : 'inputs', inputs);
|
||||
this.inputs.push(argument);
|
||||
}
|
||||
const outputs = Array.isArray(obj.outputs) ? Array.from(obj.outputs).map((name) => value(name)) : [];
|
||||
if (Array.isArray(this.type.outputs) && outputs.length === this.type.outputs.length) {
|
||||
for (let i = 0; i < outputs.length; i++) {
|
||||
const argument = new dlc.Argument(this.type.outputs[i].name, [outputs[i]]);
|
||||
this.outputs.push(argument);
|
||||
}
|
||||
} else if (outputs.length > 0) {
|
||||
const argument = new dlc.Argument(outputs.length === 1 ? 'output' : 'outputs', outputs);
|
||||
this.outputs.push(argument);
|
||||
}
|
||||
if (obj.attributes) {
|
||||
for (const attr of obj.attributes) {
|
||||
if (attr.name === 'OutputDims') {
|
||||
continue;
|
||||
}
|
||||
const schema = metadata.attribute(obj.type, attr.name);
|
||||
let type = attr.type;
|
||||
switch (type) {
|
||||
case 'tensor': {
|
||||
const tensor = attr.data;
|
||||
const type = new dlc.TensorType(tensor.dtype, tensor.shape);
|
||||
value = new dlc.Tensor(tensor.name, type, tensor.data);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
value = attr.data;
|
||||
}
|
||||
}
|
||||
if (schema && schema.type) {
|
||||
type = schema.type;
|
||||
let enumType = null;
|
||||
switch (version) {
|
||||
case 3: enumType = dlc.schema.v3[type]; break;
|
||||
case 4: enumType = dlc.schema.v4[type]; break;
|
||||
default: throw new dlc.Error(`Unsupported version '${version}'.`);
|
||||
}
|
||||
if (enumType) {
|
||||
value = enumType[value] || value;
|
||||
}
|
||||
}
|
||||
const attribute = new dlc.Argument(attr.name, value, type);
|
||||
this.attributes.push(attribute);
|
||||
}
|
||||
}
|
||||
if (obj.weights) {
|
||||
for (const tensor of obj.weights) {
|
||||
const type = new dlc.TensorType(tensor.data.dtype, tensor.shape);
|
||||
const value = new dlc.Value('', type, new dlc.Tensor(tensor.name, type, tensor.data));
|
||||
this.inputs.push(new dlc.Argument(tensor.name, [value]));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dlc.TensorType = class {
|
||||
|
||||
constructor(dataType, shape) {
|
||||
this.dataType = dataType || '?';
|
||||
this.shape = new dlc.TensorShape(shape);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.dataType + this.shape.toString();
|
||||
}
|
||||
};
|
||||
|
||||
dlc.TensorShape = class {
|
||||
|
||||
constructor(dimensions) {
|
||||
this.dimensions = Array.from(dimensions);
|
||||
}
|
||||
|
||||
toString() {
|
||||
if (Array.isArray(this.dimensions) && this.dimensions.length > 0) {
|
||||
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
dlc.Tensor = class {
|
||||
|
||||
constructor(name, type, data) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
if (data instanceof Uint8Array) {
|
||||
this.encoding = '<';
|
||||
this.values = data;
|
||||
} else {
|
||||
this.encoding = '|';
|
||||
switch (type.dataType) {
|
||||
case 'uint8': this.values = data.bytes; break;
|
||||
case 'float32': this.values = data.floats; break;
|
||||
default: throw new dlc.Error(`Unsupported tensor data type '${type.dataType}'.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dlc.Container = class {
|
||||
|
||||
static async open(context) {
|
||||
const entries = await context.peek('zip');
|
||||
if (entries instanceof Map) {
|
||||
const model = entries.get('model');
|
||||
const params = entries.get('model.params');
|
||||
const metadata = entries.get('dlc.metadata');
|
||||
if (model) {
|
||||
const signature = dlc.Container._signature(model);
|
||||
if (signature && (signature.identifier === 'NETD' || signature.major === 2)) {
|
||||
return new dlc.Container(context, model, params, metadata);
|
||||
}
|
||||
}
|
||||
if (params) {
|
||||
const signature = dlc.Container._signature(params);
|
||||
if (signature && signature.identifier === 'NETP') {
|
||||
return new dlc.Container(context, model, params, metadata);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
const stream = context.stream;
|
||||
const signature = dlc.Container._signature(stream);
|
||||
switch (signature.identifier) {
|
||||
case 'NETD':
|
||||
return new dlc.Container(context, stream, undefined, undefined);
|
||||
case 'NETP':
|
||||
case 'NR64':
|
||||
return new dlc.Container(context, undefined, stream, undefined);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
constructor(context, model, params, metadata) {
|
||||
this.context = context;
|
||||
this._model = model;
|
||||
this._params = params;
|
||||
this._metadata = metadata;
|
||||
}
|
||||
|
||||
async read() {
|
||||
if (this._model === undefined) {
|
||||
this._model = await this._fetch('model');
|
||||
}
|
||||
if (this._params === undefined) {
|
||||
this._params = await this._fetch('model.params');
|
||||
}
|
||||
if (this._metadata === undefined) {
|
||||
this._metadata = await this._fetch('dlc.metadata');
|
||||
}
|
||||
delete this.context;
|
||||
this.graphs = [];
|
||||
this.metadata = new Map();
|
||||
if (this._model) {
|
||||
this.format = 'DLC';
|
||||
const stream = this._model;
|
||||
delete this._model;
|
||||
const signature = dlc.Container._signature(stream);
|
||||
if (signature.major === 2) {
|
||||
throw new dlc.Error("File contains undocumented DLC v2 data.");
|
||||
} else if (signature.identifier === 'NETD' && (signature.major === 3 || signature.major === undefined)) {
|
||||
this.version = { major: signature.major || 3, minor: signature.minor || 0 };
|
||||
this.graph = dlc.Container._model3(stream, signature.offset);
|
||||
this.graphs = [this.graph];
|
||||
} else if (signature.identifier === 'NETD' && signature.major === 4) {
|
||||
this.version = { major: signature.major, minor: signature.minor };
|
||||
this.graphs = dlc.Container._model4(stream);
|
||||
} else {
|
||||
const buffer = stream.peek(Math.min(stream.length, 16));
|
||||
const content = Array.from(buffer).map((c) => (c < 16 ? '0' : '') + c.toString(16)).join('');
|
||||
throw new dlc.Error(`File contains undocumented '${content}' data.`);
|
||||
}
|
||||
}
|
||||
if (this._params) {
|
||||
this.format = this.format || 'DLC Weights';
|
||||
const stream = this._params;
|
||||
delete this._params;
|
||||
const signature = dlc.Container._signature(stream);
|
||||
if (signature.major === 2) {
|
||||
throw new dlc.Error("File contains undocumented DLC v2 data.");
|
||||
} else if (signature.identifier === 'NETP' && (signature.major === 3 || signature.major === undefined)) {
|
||||
this.version = this.graphs.length > 0 ? this.version : { major: signature.major || 3, minor: signature.minor || 0 };
|
||||
this.graph = dlc.Container._params3(stream, signature, this.graph);
|
||||
this.graphs = [this.graph];
|
||||
} else if ((signature.identifier === 'NETP' || signature.identifier === 'NR64') && signature.major === 4) {
|
||||
dlc.Container._params4(stream, this.graphs, signature);
|
||||
} else {
|
||||
const buffer = stream.peek(Math.min(stream.length, 16));
|
||||
const content = Array.from(buffer).map((c) => (c < 16 ? '0' : '') + c.toString(16)).join('');
|
||||
throw new dlc.Error(`File contains undocumented '${content}' data.`);
|
||||
}
|
||||
}
|
||||
if (this._metadata) {
|
||||
const stream = this._metadata;
|
||||
delete this._metadata;
|
||||
const reader = text.Reader.open(stream);
|
||||
for (;;) {
|
||||
const line = reader.read('\n');
|
||||
if (line === undefined) {
|
||||
break;
|
||||
}
|
||||
const index = line.indexOf('=');
|
||||
if (index === -1) {
|
||||
break;
|
||||
}
|
||||
const key = line.substring(0, index);
|
||||
const value = line.substring(index + 1);
|
||||
this.metadata.set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static _model3(stream, offset) {
|
||||
let model = null;
|
||||
try {
|
||||
const buffer = new Uint8Array(offset > 0 ? stream.peek().subarray(offset) : stream.peek());
|
||||
const reader = flatbuffers.BinaryReader.open(buffer);
|
||||
model = dlc.schema.v3.Model.decode(reader, reader.root);
|
||||
} catch (error) {
|
||||
const message = error && error.message ? error.message : error.toString();
|
||||
throw new dlc.Error(`File format is not dlc.v3.NETD (${message.replace(/\.$/, '')}).`);
|
||||
}
|
||||
model.tensors = [];
|
||||
const updateAttribute = (attr) => {
|
||||
switch (attr.type) {
|
||||
case 1: return ['boolean', attr.bool_value];
|
||||
case 2: return ['int32', attr.int32_value];
|
||||
case 3: return ['uint32', attr.uint32_value];
|
||||
case 4: return ['float32', attr.float32_value];
|
||||
case 5: return ['string', attr.string_value];
|
||||
case 7: return ['byte[]', Array.from(attr.byte_list)];
|
||||
case 8: return ['int32[]', Array.from(attr.int32_list)];
|
||||
case 9: return ['float32[]', Array.from(attr.float32_list)];
|
||||
case 11: {
|
||||
const obj = {};
|
||||
let index = 0;
|
||||
let list = true;
|
||||
for (const attribute of attr.attributes) {
|
||||
const name = attribute.name;
|
||||
const [, data] = updateAttribute(attribute);
|
||||
obj[name] = data;
|
||||
list = list && index.toString() === attribute.name;
|
||||
index++;
|
||||
}
|
||||
return list ? ['', Object.values(obj)] : ['', obj];
|
||||
}
|
||||
default:
|
||||
throw new dlc.Error(`Unsupported attribute type '${attr.type}'.`);
|
||||
}
|
||||
};
|
||||
for (const node of model.nodes) {
|
||||
for (const attribute of node.attributes) {
|
||||
const [type, data] = updateAttribute(attribute);
|
||||
attribute.type = type;
|
||||
attribute.data = data;
|
||||
}
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
static _model4(stream) {
|
||||
let model = null;
|
||||
try {
|
||||
const buffer = new Uint8Array(stream.peek().subarray(8));
|
||||
const reader = flatbuffers.BinaryReader.open(buffer);
|
||||
model = dlc.schema.v4.Model.decode(reader, reader.root);
|
||||
} catch (error) {
|
||||
const message = error && error.message ? error.message : error.toString();
|
||||
throw new dlc.Error(`File format is not dlc.v4.NETD (${message.replace(/\.$/, '')}).`);
|
||||
}
|
||||
const dataType = (value) => {
|
||||
switch (value) {
|
||||
case 0x0008: return 'int8';
|
||||
case 0x0016: return 'int16';
|
||||
case 0x0032: return 'int32';
|
||||
case 0x0064: return 'int64';
|
||||
case 0x0108: return 'uint8';
|
||||
case 0x0116: return 'uint16';
|
||||
case 0x0132: return 'uint32';
|
||||
case 0x0164: return 'uint64';
|
||||
case 0x0216: return 'float16';
|
||||
case 0x0232: return 'float32';
|
||||
case 0x0304: return 'qint4';
|
||||
case 0x0308: return 'qint8';
|
||||
case 0x0316: return 'qint16';
|
||||
case 0x0332: return 'qint32';
|
||||
case 0x0404: return 'quint4';
|
||||
case 0x0408: return 'quint8';
|
||||
case 0x0416: return 'quint16';
|
||||
case 0x0432: return 'quint32';
|
||||
case 0x0508: return 'boolean';
|
||||
case 0x0608: return 'string';
|
||||
case 0x7fffffff: return 'undefined';
|
||||
default: throw new dlc.Error(`Unsupported data type '${JSON.stringify(value)}'.`);
|
||||
}
|
||||
};
|
||||
const updateTensor = (tensor) => {
|
||||
tensor.dtype = dataType(tensor.dtype);
|
||||
tensor.output_dtype = dataType(tensor.output_dtype);
|
||||
};
|
||||
for (const graph of model.graphs) {
|
||||
for (const node of graph.nodes) {
|
||||
for (const attribute of node.attributes) {
|
||||
switch (attribute.kind) {
|
||||
case 0: {
|
||||
const value = attribute.value;
|
||||
switch (value.kind) {
|
||||
case 0x7fffffff:
|
||||
attribute.data = value.string_value;
|
||||
attribute.type = 'string';
|
||||
break;
|
||||
case 0x0032:
|
||||
attribute.data = value.int32_value;
|
||||
break;
|
||||
case 0x0108:
|
||||
attribute.data = value.int32_value;
|
||||
attribute.type = 'int8';
|
||||
break;
|
||||
case 0x0132:
|
||||
attribute.data = value.int32_value;
|
||||
attribute.type = 'int32';
|
||||
break;
|
||||
case 0x0232:
|
||||
attribute.data = value.float32_value;
|
||||
attribute.type = 'float32';
|
||||
break;
|
||||
case 0x0508:
|
||||
attribute.data = value.int32_value !== 0;
|
||||
attribute.type = 'boolean';
|
||||
break;
|
||||
case 0x0608:
|
||||
attribute.data = value.string_value;
|
||||
attribute.type = 'string';
|
||||
break;
|
||||
default:
|
||||
throw new dlc.Error(`Unknown attribute value kind '${value.kind}'.`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
const tensor = attribute.tensor;
|
||||
updateTensor(tensor);
|
||||
attribute.type = 'tensor';
|
||||
attribute.data = tensor;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new dlc.Error(`Unknown attribute kind '${attribute.kind}'.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const tensor of graph.tensors) {
|
||||
updateTensor(tensor);
|
||||
}
|
||||
}
|
||||
return model.graphs;
|
||||
}
|
||||
|
||||
static _params3(stream, signature, graph) {
|
||||
let params = null;
|
||||
try {
|
||||
const buffer = new Uint8Array(signature === 'NETP' ? stream.peek() : stream.peek().subarray(8));
|
||||
const reader = flatbuffers.BinaryReader.open(buffer);
|
||||
params = dlc.schema.v3.ModelParameters.decode(reader, reader.root);
|
||||
} catch (error) {
|
||||
const message = error && error.message ? error.message : error.toString();
|
||||
throw new dlc.Error(`File format is not dlc.v3.NETP (${message.replace(/\.$/, '')}).`);
|
||||
}
|
||||
if (!graph) {
|
||||
graph = new dlc.schema.v3.ModelParameters();
|
||||
graph.nodes = new Array(params.nodes.length);
|
||||
graph.tensors = [];
|
||||
for (let i = 0; i < graph.nodes.length; i++) {
|
||||
const node = new dlc.schema.v3.Node();
|
||||
node.type = 'Weights';
|
||||
node.name = params.nodes[i].name;
|
||||
node.inputs = [];
|
||||
node.outputs = [];
|
||||
node.attributes = [];
|
||||
graph.nodes[i] = node;
|
||||
}
|
||||
}
|
||||
const dataType = (value) => {
|
||||
switch (value) {
|
||||
case null: return '?';
|
||||
case 6: return 'uint8';
|
||||
case 9: return 'float32';
|
||||
default:
|
||||
throw new dlc.Error(`Unsupported data type '${JSON.stringify(value)}'.`);
|
||||
}
|
||||
};
|
||||
const weights = new Map(params.nodes.map((node) => [node.name, node.weights]));
|
||||
for (const node of graph.nodes) {
|
||||
if (weights.has(node.name)) {
|
||||
const tensors = weights.get(node.name);
|
||||
for (const tensor of tensors) {
|
||||
tensor.data.dtype = dataType(tensor.data.dtype);
|
||||
}
|
||||
node.weights = tensors;
|
||||
}
|
||||
}
|
||||
return graph;
|
||||
}
|
||||
|
||||
static _params4(stream, graphs, signature) {
|
||||
let buffer = stream.peek().subarray(8);
|
||||
let buffers = null;
|
||||
if (signature.major === 4 && signature.identifier === 'NR64') {
|
||||
try {
|
||||
const reader = flatbuffers.BinaryReader.open(buffer);
|
||||
const nr64 = dlc.schema.v4.ModelParameters64.decode(reader, reader.root);
|
||||
buffers = nr64.buffers;
|
||||
buffer = nr64.params;
|
||||
} catch (error) {
|
||||
const message = error && error.message ? error.message : error.toString();
|
||||
throw new dlc.Error(`File format is not dlc.v4.NR64 (${message.replace(/\.$/, '')}).`);
|
||||
}
|
||||
}
|
||||
let params = null;
|
||||
try {
|
||||
const reader = flatbuffers.BinaryReader.open(buffer);
|
||||
params = dlc.schema.v4.ModelParameters.decode(reader, reader.root);
|
||||
} catch (error) {
|
||||
const message = error && error.message ? error.message : error.toString();
|
||||
throw new dlc.Error(`File format is not dlc.v4.NETP (${message.replace(/\.$/, '')}).`);
|
||||
}
|
||||
if (graphs.length === 0) {
|
||||
throw new dlc.Error('Model definition not available.');
|
||||
}
|
||||
const weights = new Map(params.graphs.map((graph) => [graph.name, graph]));
|
||||
for (const graph of graphs) {
|
||||
const params = weights.get(graph.name);
|
||||
const tensors = new Map(params.tensors.map((tensor) => [tensor.name, tensor]));
|
||||
let index = 0;
|
||||
graph.tensors.sort((a, b) => a.name.localeCompare(b.name));
|
||||
for (const tensor of graph.tensors) {
|
||||
if (tensor.location === 4) {
|
||||
if (buffers && index < buffers.length) {
|
||||
tensor.data = buffers[index++].bytes;
|
||||
} else if (tensors.has(tensor.name)) {
|
||||
tensor.data = tensors.get(tensor.name).bytes;
|
||||
} else {
|
||||
throw new dlc.Error(`Unknown tensor `);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < graph.nodes.length; i++) {
|
||||
const node = graph.nodes[i];
|
||||
const tensors = new Map(params.nodes[i].tensors.map((tensor) => [tensor.name, tensor]));
|
||||
for (const attribute of node.attributes) {
|
||||
const tensor = attribute.tensor;
|
||||
if (tensor) {
|
||||
if (buffers && index < buffers.length) {
|
||||
tensor.data = buffers[index++].bytes;
|
||||
} else if (tensors.has(tensor.name)) {
|
||||
tensor.data = tensors.get(tensor.name).bytes;
|
||||
} else {
|
||||
throw new dlc.Error(`Unknown tensor `);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async _fetch(name) {
|
||||
try {
|
||||
const context = await this.context.fetch(name);
|
||||
return context.stream;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static _signature(stream) {
|
||||
const signature = {};
|
||||
signature.identifier = '?';
|
||||
signature.offset = 0;
|
||||
if (stream) {
|
||||
const buffer = stream.peek(Math.min(stream.length, 16));
|
||||
if (buffer[0] === 0xD5 && buffer[1] === 0x0A) {
|
||||
delete signature.identifier;
|
||||
if (buffer[3] === 0x00 && buffer[5] === 0x00 && buffer[6] === 0x00) {
|
||||
signature.major = buffer[2] | buffer[3] << 8;
|
||||
signature.minor = buffer[4] | buffer[5] << 8;
|
||||
if (signature.major > 2) {
|
||||
signature.identifier = '?';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (signature.identifier === '?') {
|
||||
const offset = signature.major === undefined ? 0 : 8;
|
||||
const reader = flatbuffers.BinaryReader.open(stream, offset);
|
||||
if (reader) {
|
||||
signature.identifier = reader.identifier;
|
||||
signature.offset = offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
return signature;
|
||||
}
|
||||
};
|
||||
|
||||
dlc.Error = class extends Error {
|
||||
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'Error loading DLC model.';
|
||||
}
|
||||
};
|
||||
|
||||
export const ModelFactory = dlc.ModelFactory;
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
[
|
||||
{
|
||||
"name": "batchnorm",
|
||||
"category": "Normalization",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "scale" },
|
||||
{ "name": "bias" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "concat",
|
||||
"category": "Tensor",
|
||||
"inputs": [
|
||||
{ "name": "input", "option": "variadic" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "const_v2",
|
||||
"category": "Constant"
|
||||
},
|
||||
{
|
||||
"name": "conv",
|
||||
"category": "Layer",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "weights" },
|
||||
{ "name": "biases" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "deconv",
|
||||
"category": "Layer",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "weights" },
|
||||
{ "name": "biases" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "depthdeconv",
|
||||
"category": "Layer",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "weights" },
|
||||
{ "name": "biases" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eltwise",
|
||||
"inputs": [
|
||||
{ "name": "input", "option": "variadic" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "linear",
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pad",
|
||||
"category": "Shape"
|
||||
},
|
||||
{
|
||||
"name": "pool",
|
||||
"category": "Pool",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "prelu",
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" },
|
||||
{ "name": "slope" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "relu",
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "relu6",
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "sigmoid",
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "softmax",
|
||||
"category": "Activation",
|
||||
"inputs": [
|
||||
{ "name": "input" }
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,353 @@
|
||||
|
||||
export const dnn = {};
|
||||
|
||||
dnn.Model = class Model {
|
||||
|
||||
constructor() {
|
||||
this.input_shape = [];
|
||||
this.input_name = [];
|
||||
this.node = [];
|
||||
this.input = [];
|
||||
this.output = [];
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new dnn.Model();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.name = reader.string();
|
||||
break;
|
||||
case 2:
|
||||
message.version = reader.int32();
|
||||
break;
|
||||
case 4:
|
||||
message.input_shape = reader.array(message.input_shape, () => reader.int32(), tag);
|
||||
break;
|
||||
case 7:
|
||||
message.input_name.push(reader.string());
|
||||
break;
|
||||
case 10:
|
||||
message.node.push(dnn.Node.decode(reader, reader.uint32()));
|
||||
break;
|
||||
case 12:
|
||||
message.input.push(dnn.Parameter.decode(reader, reader.uint32()));
|
||||
break;
|
||||
case 13:
|
||||
message.output.push(dnn.Parameter.decode(reader, reader.uint32()));
|
||||
break;
|
||||
case 14:
|
||||
message.a014 = reader.double();
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
dnn.Model.prototype.name = "";
|
||||
dnn.Model.prototype.version = 0;
|
||||
dnn.Model.prototype.a014 = 0;
|
||||
|
||||
dnn.Parameter = class Parameter {
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new dnn.Parameter();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.name = reader.string();
|
||||
break;
|
||||
case 2:
|
||||
message.shape = dnn.Shape.decode(reader, reader.uint32());
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
dnn.Parameter.prototype.name = "";
|
||||
dnn.Parameter.prototype.shape = null;
|
||||
|
||||
dnn.Shape = class Shape {
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new dnn.Shape();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.dim0 = reader.int32();
|
||||
break;
|
||||
case 2:
|
||||
message.dim1 = reader.int32();
|
||||
break;
|
||||
case 3:
|
||||
message.dim2 = reader.int32();
|
||||
break;
|
||||
case 4:
|
||||
message.dim3 = reader.int32();
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
dnn.Shape.prototype.dim0 = 0;
|
||||
dnn.Shape.prototype.dim1 = 0;
|
||||
dnn.Shape.prototype.dim2 = 0;
|
||||
dnn.Shape.prototype.dim3 = 0;
|
||||
|
||||
dnn.Node = class Node {
|
||||
|
||||
constructor() {
|
||||
this.input = [];
|
||||
this.output = [];
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new dnn.Node();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.layer = dnn.Layer.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 2:
|
||||
message.input.push(reader.string());
|
||||
break;
|
||||
case 3:
|
||||
message.output.push(reader.string());
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
dnn.Node.prototype.layer = null;
|
||||
|
||||
dnn.Layer = class Layer {
|
||||
|
||||
constructor() {
|
||||
this.weight = [];
|
||||
}
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new dnn.Layer();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.name = reader.string();
|
||||
break;
|
||||
case 2:
|
||||
message.type = reader.string();
|
||||
break;
|
||||
case 3:
|
||||
message.filters = reader.int32();
|
||||
break;
|
||||
case 7:
|
||||
message.a007 = reader.int32();
|
||||
break;
|
||||
case 8:
|
||||
message.a008 = reader.int32();
|
||||
break;
|
||||
case 9:
|
||||
message.groups = reader.int32();
|
||||
break;
|
||||
case 10:
|
||||
message.a010 = reader.int32();
|
||||
break;
|
||||
case 11:
|
||||
message.a011 = reader.int32();
|
||||
break;
|
||||
case 14:
|
||||
message.slope = reader.float();
|
||||
break;
|
||||
case 15:
|
||||
message.intercept = reader.float();
|
||||
break;
|
||||
case 50:
|
||||
message.weight.push(dnn.Tensor.decode(reader, reader.uint32()));
|
||||
break;
|
||||
case 72:
|
||||
message.operation = reader.int32();
|
||||
break;
|
||||
case 65:
|
||||
message.axis = reader.int32();
|
||||
break;
|
||||
case 77:
|
||||
message.a077 = reader.int32();
|
||||
break;
|
||||
case 79:
|
||||
message.scale = reader.float();
|
||||
break;
|
||||
case 80:
|
||||
message.pad_1 = reader.int32();
|
||||
break;
|
||||
case 81:
|
||||
message.pad_2 = reader.int32();
|
||||
break;
|
||||
case 82:
|
||||
message.pad_3 = reader.int32();
|
||||
break;
|
||||
case 83:
|
||||
message.pad_4 = reader.int32();
|
||||
break;
|
||||
case 84:
|
||||
message.pad_5 = reader.int32();
|
||||
break;
|
||||
case 85:
|
||||
message.a085 = reader.int32();
|
||||
break;
|
||||
case 90:
|
||||
message.a090 = reader.int32();
|
||||
break;
|
||||
case 101:
|
||||
message.is_quantized = reader.bool();
|
||||
break;
|
||||
case 104:
|
||||
message.quantization = dnn.Buffer.decode(reader, reader.uint32());
|
||||
break;
|
||||
case 109:
|
||||
message.stride_w = reader.int32();
|
||||
break;
|
||||
case 110:
|
||||
message.stride_h = reader.int32();
|
||||
break;
|
||||
case 111:
|
||||
message.kernel_w = reader.int32();
|
||||
break;
|
||||
case 112:
|
||||
message.kernel_h = reader.int32();
|
||||
break;
|
||||
case 115:
|
||||
message.a115 = reader.int32();
|
||||
break;
|
||||
case 116:
|
||||
message.a116 = reader.int32();
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
dnn.Layer.prototype.name = "";
|
||||
dnn.Layer.prototype.type = "";
|
||||
dnn.Layer.prototype.filters = 0;
|
||||
dnn.Layer.prototype.a007 = 0;
|
||||
dnn.Layer.prototype.a008 = 0;
|
||||
dnn.Layer.prototype.groups = 0;
|
||||
dnn.Layer.prototype.a010 = 0;
|
||||
dnn.Layer.prototype.a011 = 0;
|
||||
dnn.Layer.prototype.slope = 0;
|
||||
dnn.Layer.prototype.intercept = 0;
|
||||
dnn.Layer.prototype.operation = 0;
|
||||
dnn.Layer.prototype.axis = 0;
|
||||
dnn.Layer.prototype.a077 = 0;
|
||||
dnn.Layer.prototype.scale = 0;
|
||||
dnn.Layer.prototype.pad_1 = 0;
|
||||
dnn.Layer.prototype.pad_2 = 0;
|
||||
dnn.Layer.prototype.pad_3 = 0;
|
||||
dnn.Layer.prototype.pad_4 = 0;
|
||||
dnn.Layer.prototype.pad_5 = 0;
|
||||
dnn.Layer.prototype.a085 = 0;
|
||||
dnn.Layer.prototype.a090 = 0;
|
||||
dnn.Layer.prototype.is_quantized = false;
|
||||
dnn.Layer.prototype.quantization = null;
|
||||
dnn.Layer.prototype.stride_w = 0;
|
||||
dnn.Layer.prototype.stride_h = 0;
|
||||
dnn.Layer.prototype.kernel_w = 0;
|
||||
dnn.Layer.prototype.kernel_h = 0;
|
||||
dnn.Layer.prototype.a115 = 0;
|
||||
dnn.Layer.prototype.a116 = 0;
|
||||
|
||||
dnn.Buffer = class Buffer {
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new dnn.Buffer();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 5:
|
||||
message.data = reader.bytes();
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
dnn.Buffer.prototype.data = new Uint8Array([]);
|
||||
|
||||
dnn.Tensor = class Tensor {
|
||||
|
||||
static decode(reader, length) {
|
||||
const message = new dnn.Tensor();
|
||||
const end = length === undefined ? reader.length : reader.position + length;
|
||||
while (reader.position < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.dim0 = reader.int32();
|
||||
break;
|
||||
case 2:
|
||||
message.dim1 = reader.int32();
|
||||
break;
|
||||
case 3:
|
||||
message.dim2 = reader.int32();
|
||||
break;
|
||||
case 4:
|
||||
message.dim3 = reader.int32();
|
||||
break;
|
||||
case 5:
|
||||
message.data = reader.bytes();
|
||||
break;
|
||||
case 6:
|
||||
message.quantized_data = reader.bytes();
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
dnn.Tensor.prototype.dim0 = 0;
|
||||
dnn.Tensor.prototype.dim1 = 0;
|
||||
dnn.Tensor.prototype.dim2 = 0;
|
||||
dnn.Tensor.prototype.dim3 = 0;
|
||||
dnn.Tensor.prototype.data = new Uint8Array([]);
|
||||
dnn.Tensor.prototype.quantized_data = new Uint8Array([]);
|
||||
@@ -0,0 +1,247 @@
|
||||
|
||||
// Experimental
|
||||
|
||||
const dnn = {};
|
||||
|
||||
dnn.ModelFactory = class {
|
||||
|
||||
async match(context) {
|
||||
const tags = await context.tags('pb');
|
||||
if (tags.get(4) === 0 && tags.get(10) === 2) {
|
||||
return context.set('dnn');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async open(context) {
|
||||
dnn.proto = await context.require('./dnn-proto');
|
||||
dnn.proto = dnn.proto.dnn;
|
||||
let model = null;
|
||||
try {
|
||||
const reader = await context.read('protobuf.binary');
|
||||
model = dnn.proto.Model.decode(reader);
|
||||
} catch (error) {
|
||||
const message = error && error.message ? error.message : error.toString();
|
||||
throw new dnn.Error(`File format is not dnn.Graph (${message.replace(/\.$/, '')}).`);
|
||||
}
|
||||
const metadata = await context.metadata('dnn-metadata.json');
|
||||
return new dnn.Model(metadata, model);
|
||||
}
|
||||
};
|
||||
|
||||
dnn.Model = class {
|
||||
|
||||
constructor(metadata, model) {
|
||||
this.name = model.name || '';
|
||||
this.format = `SnapML${model.version ? ` v${model.version}` : ''}`;
|
||||
this.modules = [new dnn.Graph(metadata, model)];
|
||||
}
|
||||
};
|
||||
|
||||
dnn.Graph = class {
|
||||
|
||||
constructor(metadata, model) {
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.nodes = [];
|
||||
const scope = {};
|
||||
for (let i = 0; i < model.node.length; i++) {
|
||||
const node = model.node[i];
|
||||
node.input = node.input.map((input) => scope[input] ? scope[input] : input);
|
||||
node.output = node.output.map((output) => {
|
||||
scope[output] = scope[output] ? `${output}\n${i}` : output; // custom argument id
|
||||
return scope[output];
|
||||
});
|
||||
}
|
||||
const values = new Map();
|
||||
values.map = (name, type) => {
|
||||
if (!values.has(name)) {
|
||||
values.set(name, new dnn.Value(name, type));
|
||||
}
|
||||
return values.get(name);
|
||||
};
|
||||
for (const input of model.input) {
|
||||
const shape = input.shape;
|
||||
const type = new dnn.TensorType('float32', new dnn.TensorShape([shape.dim0, shape.dim1, shape.dim2, shape.dim3]));
|
||||
const argument = new dnn.Argument(input.name, [values.map(input.name, type)]);
|
||||
this.inputs.push(argument);
|
||||
}
|
||||
for (const output of model.output) {
|
||||
const shape = output.shape;
|
||||
const type = new dnn.TensorType('float32', new dnn.TensorShape([shape.dim0, shape.dim1, shape.dim2, shape.dim3]));
|
||||
const argument = new dnn.Argument(output.name, [values.map(output.name, type)]);
|
||||
this.outputs.push(argument);
|
||||
}
|
||||
if (this.inputs.length === 0 && model.input_name && model.input_shape && model.input_shape.length === model.input_name.length * 4) {
|
||||
for (let i = 0; i < model.input_name.length; i++) {
|
||||
const name = model.input_name[i];
|
||||
const shape = model.input_shape.slice(i * 4, (i * 4 + 4));
|
||||
const type = new dnn.TensorType('float32', new dnn.TensorShape([shape[1], shape[3], shape[2], shape[0]]));
|
||||
const argument = new dnn.Argument(name, [values.map(name, type)]);
|
||||
this.inputs.push(argument);
|
||||
}
|
||||
}
|
||||
if (this.inputs.length === 0 && model.input_shape && model.input_shape.length === 4 && model.node.length > 0 && model.node[0].input.length > 0) {
|
||||
const [name] = model.node[0].input;
|
||||
const shape = model.input_shape;
|
||||
const type = new dnn.TensorType('float32', new dnn.TensorShape([shape[1], shape[3], shape[2], shape[0]]));
|
||||
const argument = new dnn.Argument(name, [values.map(name, type)]);
|
||||
this.inputs.push(argument);
|
||||
}
|
||||
|
||||
for (const node of model.node) {
|
||||
this.nodes.push(new dnn.Node(metadata, node, values));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dnn.Argument = class {
|
||||
|
||||
constructor(name, value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
};
|
||||
|
||||
dnn.Value = class {
|
||||
|
||||
constructor(name, type = null, initializer = null, quantization = null) {
|
||||
if (typeof name !== 'string') {
|
||||
throw new dnn.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
|
||||
}
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.initializer = initializer;
|
||||
if (quantization) {
|
||||
this.quantization = {
|
||||
type: 'lookup',
|
||||
value: quantization
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dnn.Node = class {
|
||||
|
||||
constructor(metadata, node, values) {
|
||||
const layer = node.layer;
|
||||
this.name = layer.name;
|
||||
const type = layer.type;
|
||||
this.type = metadata.type(type) || { name: type };
|
||||
this.attributes = [];
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
const inputs = node.input.map((input) => values.map(input));
|
||||
for (const weight of layer.weight) {
|
||||
let quantization = null;
|
||||
if (layer.is_quantized && weight === layer.weight[0] && layer.quantization && layer.quantization.data) {
|
||||
const data = layer.quantization.data;
|
||||
quantization = new Array(data.length >> 2);
|
||||
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
||||
for (let i = 0; i < quantization.length; i++) {
|
||||
quantization[i] = view.getFloat32(i << 2, true);
|
||||
}
|
||||
}
|
||||
const initializer = new dnn.Tensor(weight, quantization);
|
||||
inputs.push(new dnn.Value('', initializer.type, initializer, quantization));
|
||||
}
|
||||
const outputs = node.output.map((output) => values.map(output));
|
||||
if (inputs && inputs.length > 0) {
|
||||
let inputIndex = 0;
|
||||
if (this.type && this.type.inputs) {
|
||||
for (const inputSchema of this.type.inputs) {
|
||||
if (inputIndex < inputs.length || inputSchema.option !== 'optional') {
|
||||
const inputCount = (inputSchema.option === 'variadic') ? (node.input.length - inputIndex) : 1;
|
||||
const inputArguments = inputs.slice(inputIndex, inputIndex + inputCount);
|
||||
this.inputs.push(new dnn.Argument(inputSchema.name, inputArguments));
|
||||
inputIndex += inputCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.inputs.push(...inputs.slice(inputIndex).map((input, index) => {
|
||||
const inputName = ((inputIndex + index) === 0) ? 'input' : (inputIndex + index).toString();
|
||||
return new dnn.Argument(inputName, [input]);
|
||||
}));
|
||||
}
|
||||
if (outputs.length > 0) {
|
||||
this.outputs = outputs.map((output, index) => {
|
||||
const inputName = (index === 0) ? 'output' : index.toString();
|
||||
return new dnn.Argument(inputName, [output]);
|
||||
});
|
||||
}
|
||||
for (const [key, obj] of Object.entries(layer)) {
|
||||
switch (key) {
|
||||
case 'name':
|
||||
case 'type':
|
||||
case 'weight':
|
||||
case 'is_quantized':
|
||||
case 'quantization':
|
||||
break;
|
||||
default: {
|
||||
const attribute = new dnn.Argument(key, obj);
|
||||
this.attributes.push(attribute);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dnn.Tensor = class {
|
||||
|
||||
constructor(weight, quantization) {
|
||||
const shape = new dnn.TensorShape([weight.dim0, weight.dim1, weight.dim2, weight.dim3]);
|
||||
this.values = quantization ? weight.quantized_data : weight.data;
|
||||
const size = shape.dimensions.reduce((a, b) => a * b, 1);
|
||||
const itemsize = Math.floor(this.values.length / size);
|
||||
const remainder = this.values.length - (itemsize * size);
|
||||
if (remainder < 0 || remainder > itemsize) {
|
||||
throw new dnn.Error(`Invalid tensor data size '${this.values.length}' tensor shape '[${shape.dimensions}]' '.`);
|
||||
}
|
||||
let dataType = '?';
|
||||
switch (itemsize) {
|
||||
case 1: dataType = 'int8'; break;
|
||||
case 2: dataType = 'float16'; break;
|
||||
case 4: dataType = 'float32'; break;
|
||||
default: dataType = '?'; break;
|
||||
}
|
||||
this.type = new dnn.TensorType(dataType, shape);
|
||||
}
|
||||
};
|
||||
|
||||
dnn.TensorType = class {
|
||||
|
||||
constructor(dataType, shape) {
|
||||
this.dataType = dataType;
|
||||
this.shape = shape;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.dataType + this.shape.toString();
|
||||
}
|
||||
};
|
||||
|
||||
dnn.TensorShape = class {
|
||||
|
||||
constructor(shape) {
|
||||
this.dimensions = shape;
|
||||
}
|
||||
|
||||
toString() {
|
||||
if (!this.dimensions || this.dimensions.length === 0) {
|
||||
return '';
|
||||
}
|
||||
return `[${this.dimensions.join(',')}]`;
|
||||
}
|
||||
};
|
||||
|
||||
dnn.Error = class extends Error {
|
||||
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'Error loading SnapML model.';
|
||||
}
|
||||
};
|
||||
|
||||
export const ModelFactory = dnn.ModelFactory;
|
||||
|
||||
@@ -0,0 +1,662 @@
|
||||
|
||||
const dot = {};
|
||||
|
||||
dot.ModelFactory = class {
|
||||
|
||||
async match(context) {
|
||||
const reader = await context.read('text', 0x10000);
|
||||
if (reader) {
|
||||
try {
|
||||
for (let i = 0; i < 64; i++) {
|
||||
const line = reader.read('\n');
|
||||
if (line === undefined) {
|
||||
break;
|
||||
}
|
||||
if (line.trim().startsWith('//') || line.trim().startsWith('#')) {
|
||||
continue;
|
||||
}
|
||||
if (line.trim().match(/^(strict)?\s*digraph/)) {
|
||||
return context.set('dot');
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async open(context) {
|
||||
const decoder = await context.read('text.decoder');
|
||||
const parser = new dot.Parser(decoder);
|
||||
const graph = parser.parse();
|
||||
if (graph.kind !== 'digraph') {
|
||||
throw new dot.Error(`Graph type '${graph.type}' is not supported.`);
|
||||
}
|
||||
return new dot.Model(graph);
|
||||
}
|
||||
};
|
||||
|
||||
dot.Model = class {
|
||||
|
||||
constructor(graph) {
|
||||
this.format = 'DOT';
|
||||
this.modules = [new dot.Graph(graph)];
|
||||
}
|
||||
};
|
||||
|
||||
dot.Graph = class {
|
||||
|
||||
constructor(graph) {
|
||||
this.name = graph.name || '';
|
||||
this.nodes = [];
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
const values = new Map();
|
||||
values.map = (name, type, tensor, metadata) => {
|
||||
if (typeof name !== 'string') {
|
||||
throw new dot.Error('Invalid value name.');
|
||||
}
|
||||
if (!values.has(name) || tensor) {
|
||||
values.set(name, new dot.Value(name, type, tensor, metadata));
|
||||
}
|
||||
return values.get(name);
|
||||
};
|
||||
const nodes = new Map();
|
||||
nodes.map = (name) => {
|
||||
if (typeof name !== 'string') {
|
||||
throw new dot.Error('Invalid node name.');
|
||||
}
|
||||
if (!nodes.has(name)) {
|
||||
const node = {
|
||||
kind: 'node',
|
||||
name: { id: name, key: name },
|
||||
type: { name },
|
||||
inputs: [],
|
||||
outputs: [],
|
||||
attributes: new Map(),
|
||||
metadata: new Map()
|
||||
};
|
||||
nodes.set(name, node);
|
||||
}
|
||||
return nodes.get(name);
|
||||
};
|
||||
for (const node of graph.statements) {
|
||||
if (node.kind === 'node') {
|
||||
node.inputs = [];
|
||||
node.outputs = [];
|
||||
node.metadata = new Map([...node.defaults, ...node.attributes]);
|
||||
node.attributes = new Map();
|
||||
delete node.defaults;
|
||||
const metadata = node.metadata;
|
||||
if (metadata.has('label')) {
|
||||
const label = metadata.get('label');
|
||||
if (label.startsWith('{') && label.endsWith('}')) {
|
||||
const lines = label.substring(1, label.length - 1).split('|');
|
||||
if (lines.length > 1 && node.name.id === lines[0] && lines[1].startsWith('op_code=')) {
|
||||
const def = lines[1].split('\\l');
|
||||
const op_code = def[0].split('=').pop();
|
||||
node.type = { name: op_code };
|
||||
if (op_code === 'call_module') {
|
||||
node.type = { name: def[1], type: 'function' };
|
||||
} else if (op_code === 'call_function') {
|
||||
const vals = lines[2].split('\\l');
|
||||
node.type = { name: vals[0] };
|
||||
} else if (op_code.startsWith('get_parameter')) {
|
||||
node.attributes.set('type', op_code.substring(13, op_code.length).trim());
|
||||
node.type = { name: 'get_parameter' };
|
||||
}
|
||||
if (lines.length > 2) {
|
||||
const attributes = lines[2].split('\\l');
|
||||
for (const attribute of attributes) {
|
||||
const parts = attribute.split(':');
|
||||
if (parts.length === 2) {
|
||||
const key = parts[0].trim();
|
||||
let value = parts[1].trim();
|
||||
if (value.startsWith('(') && value.endsWith(')')) {
|
||||
value = JSON.parse(`[${value.substring(1, value.length - 1)}]`);
|
||||
}
|
||||
node.attributes.set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
metadata.delete('label');
|
||||
} else if (lines.length === 1 && lines[0].startsWith('buffer\\l')) {
|
||||
const def = lines[0].split('\\l');
|
||||
node.type = { name: def[0] };
|
||||
if (def.length > 1) {
|
||||
node.attributes.set('type', def[1]);
|
||||
}
|
||||
metadata.delete('label');
|
||||
}
|
||||
} else {
|
||||
const match = label.match(/^name:\s*([A-Za-z][A-Za-z0-9_]*)\stype:\s*([A-Za-z][A-Za-z0-9_]*)$/);
|
||||
if (match && node.name.id === match[1]) {
|
||||
node.type = { name: match[2] };
|
||||
metadata.delete('label');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!node.type) {
|
||||
const lines = node.name.id.split('\\n');
|
||||
const match = lines[0].match(/^([A-Z][A-Za-z0-9_]*)\/([A-Z][A-Za-z0-9_]*)\s\(op#(\d+)\)$/);
|
||||
if (match) {
|
||||
node.type = { name: match[2] };
|
||||
} else {
|
||||
const match = lines[0].match(/^([A-Z][A-Za-z0-9_]*)\s\(op#(\d+)\)$/);
|
||||
if (match) {
|
||||
node.type = { name: match[1] };
|
||||
} else {
|
||||
// debugger;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (!node.type) {
|
||||
node.type = { name: node.name.id };
|
||||
}
|
||||
nodes.set(node.name.id, node);
|
||||
}
|
||||
}
|
||||
for (const edge of graph.statements) {
|
||||
if (edge.kind === 'edge') {
|
||||
edge.uses = edge.uses || [];
|
||||
const to = nodes.map(edge.to.id);
|
||||
to.inputs.push(edge);
|
||||
edge.uses.push(to);
|
||||
edge.from = nodes.map(edge.name.id);
|
||||
edge.from.outputs.push(edge);
|
||||
}
|
||||
}
|
||||
for (const [key, node] of nodes) {
|
||||
const keys = new Set(['pos', 'height', 'width', 'shape', 'label']);
|
||||
if (node.metadata.get('shape') === 'octagon' && node.metadata.keys().every((key) => keys.has(key)) &&
|
||||
node.inputs.length === 1 && node.inputs[0].uses.length === 1 && node.inputs[0].from.outputs.length === 1 && node.inputs[0].from.outputs[0].uses.length === 1 &&
|
||||
new Set(node.outputs.map((output) => output.name.id)).size === 1 && node.outputs.every((output) => output.uses.length === 1)) {
|
||||
const [from] = node.inputs[0].from.outputs;
|
||||
for (const e of node.outputs) {
|
||||
const [n] = e.uses;
|
||||
n.inputs = n.inputs.map((edge) => edge === e ? from : edge);
|
||||
}
|
||||
nodes.delete(key);
|
||||
}
|
||||
}
|
||||
for (const [key, node] of nodes) {
|
||||
if ((node.type.name === 'get_parameter' || node.type.name === 'buffer' || node.type.name === 'Constant') &&
|
||||
node.inputs.length === 0 &&
|
||||
node.outputs.length === 1 && node.outputs[0].uses.length === 1) {
|
||||
node.outputs[0].initializer = node;
|
||||
nodes.delete(key);
|
||||
}
|
||||
}
|
||||
for (const [, obj] of nodes) {
|
||||
const node = new dot.Node(obj, values);
|
||||
this.nodes.push(node);
|
||||
}
|
||||
for (const edge of graph.statements) {
|
||||
if (edge.kind === 'edge') {
|
||||
const value = values.map(edge.name.id);
|
||||
const metadata = new Map([...edge.defaults, ...edge.attributes]);
|
||||
value.metadata = Array.from(metadata).map(([key, value]) => new dot.Argument(key, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dot.Argument = class {
|
||||
|
||||
constructor(name, value, type = null) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
}
|
||||
};
|
||||
|
||||
dot.Value = class {
|
||||
|
||||
constructor(name, type, initializer, metadata) {
|
||||
this.name = name;
|
||||
this.type = !type && initializer ? initializer.type : type;
|
||||
this.initializer = initializer || null;
|
||||
this.metadata = metadata;
|
||||
}
|
||||
};
|
||||
|
||||
dot.Node = class {
|
||||
|
||||
constructor(node, values) {
|
||||
this.name = node.name.key;
|
||||
this.type = node.type;
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.attributes = [];
|
||||
this.metadata = [];
|
||||
for (let i = 0; i < node.inputs.length; i++) {
|
||||
const edge = node.inputs[i];
|
||||
const initializer = edge.initializer ? new dot.Tensor(edge.initializer) : null;
|
||||
const value = values.map(edge.name.key, null, initializer);
|
||||
const argument = new dot.Argument(i.toString(), [value]);
|
||||
this.inputs.push(argument);
|
||||
}
|
||||
for (let i = 0; i < node.outputs.length; i++) {
|
||||
const edge = node.outputs[i];
|
||||
const value = values.map(edge.name.key);
|
||||
const argument = new dot.Argument(i.toString(), [value]);
|
||||
this.outputs.push(argument);
|
||||
}
|
||||
for (const [name, value] of node.attributes) {
|
||||
const argument = new dot.Argument(name, value, 'attribute');
|
||||
this.attributes.push(argument);
|
||||
}
|
||||
for (const [name, value] of node.metadata) {
|
||||
const argument = new dot.Argument(name, value);
|
||||
this.metadata.push(argument);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dot.TensorType = class {
|
||||
|
||||
constructor(type) {
|
||||
const index = type.indexOf('[');
|
||||
const dtype = type.substring(0, index);
|
||||
this.dataType = dtype.split('.').pop();
|
||||
if (index > 0) {
|
||||
const dimensions = JSON.parse(type.substring(index, type.length));
|
||||
this.shape = new dot.TensorShape(dimensions);
|
||||
} else {
|
||||
this.shape = new dot.TensorShape([]);
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.dataType + this.shape.toString();
|
||||
}
|
||||
};
|
||||
|
||||
dot.TensorShape = class {
|
||||
|
||||
constructor(dimensions) {
|
||||
this.dimensions = dimensions;
|
||||
}
|
||||
|
||||
toString() {
|
||||
if (this.dimensions && this.dimensions.length > 0) {
|
||||
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
dot.Tensor = class {
|
||||
|
||||
constructor(stmt) {
|
||||
if (stmt.attributes.has('type')) {
|
||||
const type = stmt.attributes.get('type');
|
||||
this.type = new dot.TensorType(type);
|
||||
} else {
|
||||
this.type = new dot.TensorType('?');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dot.Parser = class {
|
||||
|
||||
constructor(decoder) {
|
||||
// https://graphviz.org/doc/info/lang.html
|
||||
this._tokenizer = new dot.Tokenizer(decoder);
|
||||
this._token = this._tokenizer.read();
|
||||
}
|
||||
|
||||
parse() {
|
||||
const graph = {};
|
||||
if (this._eat('id', 'strict')) {
|
||||
graph.strict = true;
|
||||
}
|
||||
let edgeop = '';
|
||||
if (this._match('id', 'graph')) {
|
||||
graph.kind = this._read();
|
||||
edgeop = '--';
|
||||
} else if (this._match('id', 'digraph')) {
|
||||
graph.kind = this._read();
|
||||
edgeop = '->';
|
||||
} else {
|
||||
throw new dot.Error('Invalid graph type.');
|
||||
}
|
||||
if (this._match('id')) {
|
||||
graph.name = this._read();
|
||||
}
|
||||
const defaults = {};
|
||||
defaults.graph = new Map();
|
||||
defaults.node = new Map();
|
||||
defaults.edge = new Map();
|
||||
graph.statements = this._parseBlock(defaults, edgeop, 0);
|
||||
graph.defaults = new Map(defaults.graph);
|
||||
return graph;
|
||||
}
|
||||
|
||||
_parseBlock(defaults, edgeop) {
|
||||
defaults = {
|
||||
graph: new Map(defaults.graph),
|
||||
node: new Map(defaults.node),
|
||||
edge: new Map(defaults.edge)
|
||||
};
|
||||
const list = [];
|
||||
this._read('{');
|
||||
while (!this._match('}')) {
|
||||
if (this._eat('id', 'subgraph')) {
|
||||
const stmt = {};
|
||||
stmt.kind = 'subgraph';
|
||||
if (this._match('id')) {
|
||||
stmt.name = this._read();
|
||||
}
|
||||
stmt.statements = this._parseBlock(defaults, edgeop);
|
||||
} else if (this._match('{')) {
|
||||
const stmt = {};
|
||||
const statements = this._parseBlock(defaults, edgeop);
|
||||
if (this._eat(edgeop)) {
|
||||
if (!statements.every((stmt) => stmt.kind === 'node' && stmt.attributes.size === 0)) {
|
||||
throw new dot.Error('Invalid edge group statement.');
|
||||
}
|
||||
const sources = statements.map((stmt) => stmt.name);
|
||||
list.push(...this._parseEdges(sources, edgeop, defaults.edge));
|
||||
} else {
|
||||
stmt.kind = 'subgraph';
|
||||
stmt.statements = statements;
|
||||
}
|
||||
} else if (this._match('id')) {
|
||||
const name = this._parseNodeId();
|
||||
if (this._eat('=')) { // attr
|
||||
if (this._match('id')) {
|
||||
const value = this._read();
|
||||
defaults.graph.set(name, value);
|
||||
} else {
|
||||
throw new dot.Error('Invalid attribute value.');
|
||||
}
|
||||
} else if (this._eat(edgeop)) {
|
||||
list.push(...this._parseEdges([name], edgeop, defaults.edge));
|
||||
} else {
|
||||
const attributes = this._parseAttributes();
|
||||
if (name.key === 'node' || name.key === 'edge' || name.key === 'graph') {
|
||||
for (const [key, value] of attributes) {
|
||||
defaults[name.key].set(key, value);
|
||||
}
|
||||
} else {
|
||||
list.push({ kind: 'node', name, attributes, defaults: new Map(defaults.node) });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this._match(';') || this._match(',')) {
|
||||
this._read();
|
||||
}
|
||||
}
|
||||
this._read('}');
|
||||
return list;
|
||||
}
|
||||
|
||||
_parseNodeIds() {
|
||||
const list = [];
|
||||
const open = this._eat('{');
|
||||
while (!this._match('}')) {
|
||||
const value = this._parseNodeId();
|
||||
list.push(value);
|
||||
if (this._match(',')) {
|
||||
this._read();
|
||||
continue;
|
||||
} else if (this._match(';')) {
|
||||
this._read();
|
||||
if (!open) {
|
||||
break;
|
||||
}
|
||||
} else if (!open) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (open) {
|
||||
this._read('}');
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
_parseNodeId() {
|
||||
const name = {};
|
||||
const list = [];
|
||||
name.id = this._read('id');
|
||||
list.push(name.id);
|
||||
if (this._eat(':')) {
|
||||
name.port = this._read('id');
|
||||
list.push(name.port);
|
||||
if (this._eat(':')) {
|
||||
name.compass_pt = this._read('id');
|
||||
list.push(name.compass_pt);
|
||||
}
|
||||
}
|
||||
name.key = list.join(':');
|
||||
return name;
|
||||
}
|
||||
|
||||
_parseAttributes() {
|
||||
const table = new Map();
|
||||
if (this._eat('[')) {
|
||||
while (this._match('id')) {
|
||||
const name = this._read('id');
|
||||
this._read('=');
|
||||
const value = this._read('id');
|
||||
table.set(name, value);
|
||||
if (this._match(';') || this._match(',')) {
|
||||
this._read();
|
||||
}
|
||||
}
|
||||
this._read(']');
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
_parseEdges(sources, edgeop, defaults) {
|
||||
const list = [];
|
||||
do {
|
||||
const targets = this._parseNodeIds();
|
||||
for (const name of sources) {
|
||||
for (const to of targets) {
|
||||
list.push({ kind: 'edge', name, to });
|
||||
}
|
||||
}
|
||||
sources = targets;
|
||||
} while (this._eat(edgeop));
|
||||
const attributes = this._parseAttributes();
|
||||
for (const edge of list) {
|
||||
edge.attributes = attributes;
|
||||
edge.defaults = new Map(defaults.edge);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
_match(kind, value) {
|
||||
return (this._token.kind === kind && (!value || this._token.value === value));
|
||||
}
|
||||
|
||||
_read(kind, value) {
|
||||
if (kind && this._token.kind !== kind) {
|
||||
throw new dot.Error(`Expected token of type '${kind}', but got '${this._token.kind}' ${this._tokenizer.location()}`);
|
||||
}
|
||||
if (value && this._token.value !== value) {
|
||||
throw new dot.Error(`Expected token with value '${value}', but got '${this._token.value}' ${this._tokenizer.location()}`);
|
||||
}
|
||||
const token = this._token;
|
||||
this._token = this._tokenizer.read();
|
||||
return token.value;
|
||||
}
|
||||
|
||||
_eat(kind, value) {
|
||||
if (this._match(kind, value)) {
|
||||
return this._read();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
dot.Tokenizer = class {
|
||||
|
||||
constructor(decoder) {
|
||||
this._decoder = decoder;
|
||||
this._position = 0;
|
||||
this._char = this._decoder.decode();
|
||||
}
|
||||
|
||||
_read() {
|
||||
if (this._char === undefined) {
|
||||
this._unexpected();
|
||||
}
|
||||
const char = this._char;
|
||||
this._position = this._decoder.position;
|
||||
this._char = this._decoder.decode();
|
||||
return char;
|
||||
}
|
||||
|
||||
_peek() {
|
||||
const position = this._decoder.position;
|
||||
const char = this._decoder.decode();
|
||||
this._decoder.position = position;
|
||||
return char;
|
||||
}
|
||||
|
||||
read() {
|
||||
while (this._char) {
|
||||
if (/\s/.test(this._char)) {
|
||||
this._skipWhitespace();
|
||||
continue;
|
||||
}
|
||||
if (this._char === '/' || this._char === '#') {
|
||||
this._skipComment();
|
||||
continue;
|
||||
}
|
||||
if (/[{}[\]=:;,]/.test(this._char)) {
|
||||
const value = this._read();
|
||||
return { kind: value, value };
|
||||
} else if (this._char === '-') {
|
||||
let value = this._read();
|
||||
if (this._char === '>' || this._char === '-') {
|
||||
value += this._read();
|
||||
return { kind: value, value };
|
||||
}
|
||||
throw new dot.Error(`Unexpected character '${value}' ${this.location()}`);
|
||||
} else if (/[a-zA-Z0-9_$"<]/.test(this._char)) {
|
||||
const value = this._identifier();
|
||||
return { kind: 'id', value };
|
||||
} else {
|
||||
throw new dot.Error(`Unexpected character '${this._char}' ${this.location()}`);
|
||||
}
|
||||
}
|
||||
return { type: 'eof' };
|
||||
}
|
||||
|
||||
_skipWhitespace() {
|
||||
while (this._char !== undefined && /\s/.test(this._char)) {
|
||||
this._read();
|
||||
}
|
||||
}
|
||||
|
||||
_skipComment() {
|
||||
if (this._char === '#' || (this._char === '/' && this._peek() === '/')) {
|
||||
while (this._char && this._char !== '\n') {
|
||||
this._read();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this._char === '/' && this._peek() === '*') {
|
||||
while (this._char && (this._char !== '*' || this._peek() !== '/')) {
|
||||
this._read();
|
||||
}
|
||||
this._read();
|
||||
this._read();
|
||||
return;
|
||||
}
|
||||
throw new dot.Error('Invalid comment.');
|
||||
}
|
||||
|
||||
_identifier() {
|
||||
let value = '';
|
||||
if (this._char === '"') { // double quoted string
|
||||
this._read();
|
||||
while (this._char && this._char !== '"') {
|
||||
value += this._read();
|
||||
}
|
||||
this._read('"');
|
||||
} if (this._char === '<') { // HTML String
|
||||
value += this._read();
|
||||
let depth = 0;
|
||||
while (depth > 0 || this._char !== '>') {
|
||||
const c = this._read();
|
||||
value += c;
|
||||
if (c === '<') {
|
||||
depth += 1;
|
||||
} else if (c === '>') {
|
||||
depth -= 1;
|
||||
}
|
||||
}
|
||||
value += this._read();
|
||||
} else {
|
||||
while (/[a-zA-Z0-9_$.*]/.test(this._char)) {
|
||||
value += this._read();
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
_unexpected() {
|
||||
let c = this._char;
|
||||
if (c === undefined) {
|
||||
throw new dot.Error('Unexpected end of input.');
|
||||
} else if (c === '"') {
|
||||
c = 'string';
|
||||
} else if ((c >= '0' && c <= '9') || c === '-') {
|
||||
c = 'number';
|
||||
} else {
|
||||
if (c < ' ' || c > '\x7F') {
|
||||
const name = Object.keys(this._escape).filter((key) => this._escape[key] === c);
|
||||
c = (name.length === 1) ? `\\${name}` : `\\u${(`000${c.charCodeAt(0).toString(16)}`).slice(-4)}`;
|
||||
}
|
||||
c = `token '${c}'`;
|
||||
}
|
||||
this._throw(`Unexpected ${c}`);
|
||||
}
|
||||
|
||||
_throw(message) {
|
||||
message = message.replace(/\.$/, '');
|
||||
throw new dot.Error(`${message} ${this._location()}`);
|
||||
}
|
||||
|
||||
location() {
|
||||
let line = 1;
|
||||
let column = 1;
|
||||
const position = this._decoder.position;
|
||||
this._decoder.position = 0;
|
||||
let c = '';
|
||||
do {
|
||||
if (this._decoder.position === this._position) {
|
||||
this._decoder.position = position;
|
||||
return `at ${line}:${column}.`;
|
||||
}
|
||||
c = this._decoder.decode();
|
||||
if (c === '\n') {
|
||||
line++;
|
||||
column = 1;
|
||||
} else {
|
||||
column++;
|
||||
}
|
||||
}
|
||||
while (c !== undefined);
|
||||
this._decoder.position = position;
|
||||
return `at ${line}:${column}.`;
|
||||
}
|
||||
};
|
||||
|
||||
dot.Error = class extends Error {
|
||||
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'Error loadig DOT graph';
|
||||
}
|
||||
};
|
||||
|
||||
export const ModelFactory = dot.ModelFactory;
|
||||
@@ -0,0 +1,321 @@
|
||||
[
|
||||
{
|
||||
"name": "Add",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"description": "Element-wise addition of two tensors.",
|
||||
"inputs": [
|
||||
{ "name": "A", "description": "First input tensor" },
|
||||
{ "name": "B", "description": "Second input tensor" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "C", "description": "Output tensor" }
|
||||
],
|
||||
"category": "Tensor"
|
||||
},
|
||||
{
|
||||
"name": "AveragePool",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"description": "Average pooling operation.",
|
||||
"inputs": [
|
||||
{ "name": "input", "description": "Input tensor" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output", "description": "Output tensor" }
|
||||
],
|
||||
"category": "Pool"
|
||||
},
|
||||
{
|
||||
"name": "BatchNormalization",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"description": "Batch normalization.",
|
||||
"inputs": [
|
||||
{ "name": "X", "description": "Input data tensor" },
|
||||
{ "name": "scale", "description": "Scale tensor" },
|
||||
{ "name": "B", "description": "Bias tensor" },
|
||||
{ "name": "mean", "description": "Mean tensor" },
|
||||
{ "name": "var", "description": "Variance tensor" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "Y", "description": "Output data tensor" },
|
||||
{ "name": "mean", "description": "Updated mean tensor (optional)" },
|
||||
{ "name": "var", "description": "Updated variance tensor (optional)" },
|
||||
{ "name": "saved_mean", "description": "Saved mean tensor (optional)" },
|
||||
{ "name": "saved_var", "description": "Saved variance tensor (optional)" }
|
||||
],
|
||||
"category": "Normalization"
|
||||
},
|
||||
{
|
||||
"name": "Clip",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"category": "Activation",
|
||||
"description": "Clip operator limits values to a specified range.",
|
||||
"inputs": [
|
||||
{ "name": "input", "description": "Input tensor" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output", "description": "Clipped output tensor" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Concat",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"description": "Concatenates tensors along a given axis.",
|
||||
"inputs": [
|
||||
{ "name": "inputs", "list": true, "description": "Input tensors to concatenate" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output", "description": "Concatenated output tensor" }
|
||||
],
|
||||
"category": "Tensor"
|
||||
},
|
||||
{
|
||||
"name": "Conv",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"description": "Convolution operator. Applies a convolution filter to the input.",
|
||||
"inputs": [
|
||||
{ "name": "input", "description": "Input feature map" },
|
||||
{ "name": "weight", "description": "Convolution kernel weights" },
|
||||
{ "name": "bias", "option": "optional", "description": "Bias values (optional)" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output", "description": "Output feature map" }
|
||||
],
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "Gemm",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"description": "General matrix multiplication: alpha * A * B + beta * C",
|
||||
"inputs": [
|
||||
{ "name": "A", "description": "Input tensor A" },
|
||||
{ "name": "B", "description": "Input tensor B" },
|
||||
{ "name": "C", "option": "optional", "description": "Input tensor C (optional)" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "Y", "description": "Output tensor" }
|
||||
],
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "GlobalAveragePool",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"description": "Global average pooling operation for temporal data.",
|
||||
"inputs": [
|
||||
{ "name": "input", "description": "Input tensor" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output", "description": "Output tensor" }
|
||||
],
|
||||
"category": "Pool"
|
||||
},
|
||||
{
|
||||
"name": "HardSwish",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"category": "Activation",
|
||||
"description": "Hard swish activation function.",
|
||||
"inputs": [
|
||||
{ "name": "input", "description": "Input tensor" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output", "description": "Output tensor" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "LeakyRelu",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"category": "Activation",
|
||||
"description": "Leaky Rectified Linear Unit activation function.",
|
||||
"inputs": [
|
||||
{ "name": "input", "description": "Input tensor" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output", "description": "Output tensor" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MaxPool",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"description": "Max pooling operation.",
|
||||
"inputs": [
|
||||
{ "name": "input", "description": "Input tensor" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output", "description": "Output tensor" }
|
||||
],
|
||||
"category": "Pool"
|
||||
},
|
||||
{
|
||||
"name": "Mul",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"description": "Element-wise multiplication of two tensors.",
|
||||
"inputs": [
|
||||
{ "name": "A", "description": "First input tensor" },
|
||||
{ "name": "B", "description": "Second input tensor" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "C", "description": "Output tensor" }
|
||||
],
|
||||
"category": "Tensor"
|
||||
},
|
||||
{
|
||||
"name": "Pad",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"category": "Tensor",
|
||||
"description": "Pad operator adds padding to tensor dimensions.",
|
||||
"inputs": [
|
||||
{ "name": "input", "description": "Input tensor" },
|
||||
{ "name": "pads", "description": "Padding values" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output", "description": "Padded output tensor" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Relu",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"description": "Rectified Linear Unit activation function.",
|
||||
"inputs": [
|
||||
{ "name": "input", "description": "Input tensor" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output", "description": "Output tensor" }
|
||||
],
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "RequantizeLinear",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"category": "Quantization",
|
||||
"description": "Requantize linear quantization operator.",
|
||||
"inputs": [
|
||||
{ "name": "input", "description": "Input tensor to requantize" },
|
||||
{ "name": "scale", "description": "Scale for requantization" },
|
||||
{ "name": "zero_point", "description": "Zero point for requantization" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output", "description": "Requantized output tensor" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Reshape",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"description": "Reshapes a tensor to a new shape.",
|
||||
"inputs": [
|
||||
{ "name": "data", "description": "Input tensor" },
|
||||
{ "name": "shape", "description": "New shape" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "reshaped", "description": "Reshaped output tensor" }
|
||||
],
|
||||
"category": "Shape"
|
||||
},
|
||||
{
|
||||
"name": "Resize",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"category": "Data",
|
||||
"description": "Resize operator for spatial dimensions.",
|
||||
"inputs": [
|
||||
{ "name": "input", "description": "Input tensor" },
|
||||
{ "name": "scales", "description": "Scale factors for each dimension" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output", "description": "Resized output tensor" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Sigmoid",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"description": "Sigmoid activation function.",
|
||||
"inputs": [
|
||||
{ "name": "input", "description": "Input tensor" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output", "description": "Output tensor" }
|
||||
],
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "Softmax",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"description": "Softmax activation function.",
|
||||
"inputs": [
|
||||
{ "name": "input", "description": "Input tensor" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output", "description": "Output tensor" }
|
||||
],
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "Split",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"description": "Splits a tensor into multiple tensors along a given axis.",
|
||||
"inputs": [
|
||||
{ "name": "input", "description": "Input tensor to split" },
|
||||
{ "name": "split", "description": "Optional list of split sizes or number of splits" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "outputs", "list": true, "description": "Output tensors" }
|
||||
],
|
||||
"category": "Tensor"
|
||||
},
|
||||
{
|
||||
"name": "Swish",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"category": "Activation",
|
||||
"description": "Swish activation function.",
|
||||
"inputs": [
|
||||
{ "name": "input", "description": "Input tensor" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output", "description": "Output tensor" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Tanh",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"description": "Hyperbolic tangent activation function.",
|
||||
"inputs": [
|
||||
{ "name": "input", "description": "Input tensor" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "output", "description": "Output tensor" }
|
||||
],
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "Transpose",
|
||||
"module": "espdl",
|
||||
"version": 1,
|
||||
"description": "Transposes the dimensions of a tensor.",
|
||||
"inputs": [
|
||||
{ "name": "data", "description": "Input tensor" }
|
||||
],
|
||||
"outputs": [
|
||||
{ "name": "transposed", "description": "Transposed output tensor" }
|
||||
],
|
||||
"category": "Tensor"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,329 @@
|
||||
|
||||
export const espdl = {};
|
||||
|
||||
espdl.Version = {
|
||||
_START_VERSION: 0, '0': '_START_VERSION',
|
||||
IR_VERSION_2023_12_22: 1, '1': 'IR_VERSION_2023_12_22'
|
||||
};
|
||||
|
||||
espdl.AttributeType = {
|
||||
UNDEFINED: 0, '0': 'UNDEFINED',
|
||||
FLOAT: 1, '1': 'FLOAT',
|
||||
INT: 2, '2': 'INT',
|
||||
STRING: 3, '3': 'STRING',
|
||||
TENSOR: 4, '4': 'TENSOR',
|
||||
GRAPH: 5, '5': 'GRAPH',
|
||||
FLOATS: 6, '6': 'FLOATS',
|
||||
INTS: 7, '7': 'INTS',
|
||||
STRINGS: 8, '8': 'STRINGS',
|
||||
TENSORS: 9, '9': 'TENSORS',
|
||||
GRAPHS: 10, '10': 'GRAPHS',
|
||||
TYPE_FBS: 11, '11': 'TYPE_FBS',
|
||||
TYPE_FBSS: 12, '12': 'TYPE_FBSS'
|
||||
};
|
||||
|
||||
espdl.TensorDataType = {
|
||||
UNDEFINED: 0, '0': 'UNDEFINED',
|
||||
FLOAT: 1, '1': 'FLOAT',
|
||||
UINT8: 2, '2': 'UINT8',
|
||||
INT8: 3, '3': 'INT8',
|
||||
UINT16: 4, '4': 'UINT16',
|
||||
INT16: 5, '5': 'INT16',
|
||||
INT32: 6, '6': 'INT32',
|
||||
INT64: 7, '7': 'INT64',
|
||||
STRING: 8, '8': 'STRING',
|
||||
BOOL: 9, '9': 'BOOL',
|
||||
FLOAT16: 10, '10': 'FLOAT16',
|
||||
DOUBLE: 11, '11': 'DOUBLE',
|
||||
UINT32: 12, '12': 'UINT32',
|
||||
UINT64: 13, '13': 'UINT64'
|
||||
};
|
||||
|
||||
espdl.DataLocation = {
|
||||
DEFAULT: 0, '0': 'DEFAULT',
|
||||
EXTERNAL: 1, '1': 'EXTERNAL'
|
||||
};
|
||||
|
||||
espdl.AttributeF = class AttributeF {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.AttributeF();
|
||||
$.f = reader.float32(position + 0);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.AttributeI = class AttributeI {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.AttributeI();
|
||||
$.i = reader.int64(position + 0);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.Attribute = class Attribute {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.Attribute();
|
||||
$.name = reader.string_(position, 4, null);
|
||||
$.ref_attr_name = reader.string_(position, 6, null);
|
||||
$.doc_string = reader.string_(position, 8, null);
|
||||
$.attr_type = reader.int32_(position, 10, 0);
|
||||
$.f = reader.struct(position, 12, espdl.AttributeF);
|
||||
$.i = reader.struct(position, 14, espdl.AttributeI);
|
||||
$.s = reader.array(position, 16, Uint8Array);
|
||||
$.t = reader.table(position, 18, espdl.Tensor);
|
||||
$.g = reader.table(position, 20, espdl.Graph);
|
||||
$.tp = reader.table(position, 22, espdl.TypeInfo);
|
||||
$.floats = reader.array(position, 24, Float32Array);
|
||||
$.ints = reader.int64s_(position, 26);
|
||||
$.strings = reader.strings_(position, 28);
|
||||
$.tensors = reader.tables(position, 30, espdl.Tensor);
|
||||
$.graphs = reader.tables(position, 32, espdl.Graph);
|
||||
$.type_protos = reader.tables(position, 34, espdl.TypeInfo);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.ValueInfo = class ValueInfo {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.ValueInfo();
|
||||
$.name = reader.string_(position, 4, null);
|
||||
$.value_info_type = reader.table(position, 6, espdl.TypeInfo);
|
||||
$.doc_string = reader.string_(position, 8, null);
|
||||
$.exponents = reader.int64s_(position, 10);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.Node = class Node {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.Node();
|
||||
$.input = reader.strings_(position, 4);
|
||||
$.output = reader.strings_(position, 6);
|
||||
$.name = reader.string_(position, 8, null);
|
||||
$.op_type = reader.string_(position, 10, null);
|
||||
$.domain = reader.string_(position, 12, null);
|
||||
$.attribute = reader.tables(position, 14, espdl.Attribute);
|
||||
$.doc_string = reader.string_(position, 16, null);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.Model = class Model {
|
||||
|
||||
static create(reader) {
|
||||
return espdl.Model.decode(reader, reader.root);
|
||||
}
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.Model();
|
||||
$.ir_version = reader.int32_(position, 4, 0);
|
||||
$.opset_import = reader.tables(position, 6, espdl.OperatorSetId);
|
||||
$.producer_name = reader.string_(position, 8, null);
|
||||
$.producer_version = reader.string_(position, 10, null);
|
||||
$.domain = reader.string_(position, 12, null);
|
||||
$.model_version = reader.int64_(position, 14, 0n);
|
||||
$.doc_string = reader.string_(position, 16, null);
|
||||
$.graph = reader.table(position, 18, espdl.Graph);
|
||||
$.metadata_props = reader.tables(position, 20, espdl.StringStringEntry);
|
||||
$.functions = reader.tables(position, 22, espdl.Function);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.StringStringEntry = class StringStringEntry {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.StringStringEntry();
|
||||
$.key = reader.string_(position, 4, null);
|
||||
$.value = reader.string_(position, 6, null);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.TensorAnnotation = class TensorAnnotation {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.TensorAnnotation();
|
||||
$.tensor_name = reader.string_(position, 4, null);
|
||||
$.quant_parameter_tensor_names = reader.tables(position, 6, espdl.StringStringEntry);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.Graph = class Graph {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.Graph();
|
||||
$.node = reader.tables(position, 4, espdl.Node);
|
||||
$.name = reader.string_(position, 6, null);
|
||||
$.initializer = reader.tables(position, 8, espdl.Tensor);
|
||||
$.doc_string = reader.string_(position, 10, null);
|
||||
$.input = reader.tables(position, 12, espdl.ValueInfo);
|
||||
$.output = reader.tables(position, 14, espdl.ValueInfo);
|
||||
$.value_info = reader.tables(position, 16, espdl.ValueInfo);
|
||||
$.quantization_annotation = reader.tables(position, 18, espdl.TensorAnnotation);
|
||||
$.test_inputs_value = reader.tables(position, 20, espdl.Tensor);
|
||||
$.test_outputs_value = reader.tables(position, 22, espdl.Tensor);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.AlignedBytes = class AlignedBytes {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.AlignedBytes();
|
||||
$.bytes = reader.read(position + 0, 16);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.Tensor = class Tensor {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.Tensor();
|
||||
$.dims = reader.int64s_(position, 4);
|
||||
$.data_type = reader.int32_(position, 6, 0);
|
||||
$.float_data = reader.array(position, 8, Float32Array);
|
||||
$.int32_data = reader.array(position, 10, Int32Array);
|
||||
$.string_data = reader.strings_(position, 12);
|
||||
$.int64_data = reader.int64s_(position, 14);
|
||||
$.name = reader.string_(position, 16, null);
|
||||
$.doc_string = reader.string_(position, 18, null);
|
||||
$.raw_data = reader.structs(position, 20, espdl.AlignedBytes, 16);
|
||||
$.external_data = reader.tables(position, 22, espdl.StringStringEntry);
|
||||
$.data_location = reader.int32_(position, 24, 0);
|
||||
$.double_data = reader.array(position, 26, Float64Array);
|
||||
$.uint64_data = reader.uint64s_(position, 28);
|
||||
$.exponents = reader.int64s_(position, 30);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.TensorShape = class TensorShape {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.TensorShape();
|
||||
$.dim = reader.tables(position, 4, espdl.Dimension);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.Dimension = class Dimension {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.Dimension();
|
||||
$.value = reader.table(position, 4, espdl.DimensionValue);
|
||||
$.denotation = reader.string_(position, 6, null);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.DimensionValueType = {
|
||||
UNKNOWN: 0, '0': 'UNKNOWN',
|
||||
VALUE: 1, '1': 'VALUE',
|
||||
PARAM: 2, '2': 'PARAM'
|
||||
};
|
||||
|
||||
espdl.DimensionValue = class DimensionValue {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.DimensionValue();
|
||||
$.dim_type = reader.int8_(position, 4, 0);
|
||||
$.dim_value = reader.int64_(position, 6, 0n);
|
||||
$.dim_param = reader.string_(position, 8, null);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.TensorTypeAndShape = class TensorTypeAndShape {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.TensorTypeAndShape();
|
||||
$.elem_type = reader.int32_(position, 4, 0);
|
||||
$.shape = reader.table(position, 6, espdl.TensorShape);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.SequenceType = class SequenceType {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.SequenceType();
|
||||
$.elem_type = reader.table(position, 4, espdl.TypeInfo);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.MapType = class MapType {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.MapType();
|
||||
$.key_type = reader.int32_(position, 4, 0);
|
||||
$.value_type = reader.table(position, 6, espdl.TypeInfo);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.OptionalType = class OptionalType {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.OptionalType();
|
||||
$.elem_type = reader.table(position, 4, espdl.TypeInfo);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.TypeInfoValue = class {
|
||||
|
||||
static decode(reader, position, type) {
|
||||
switch (type) {
|
||||
case 1: return espdl.TensorTypeAndShape.decode(reader, position);
|
||||
case 2: return espdl.SequenceType.decode(reader, position);
|
||||
case 3: return espdl.MapType.decode(reader, position);
|
||||
case 4: return espdl.OptionalType.decode(reader, position);
|
||||
default: return undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
espdl.TypeInfo = class TypeInfo {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.TypeInfo();
|
||||
$.value = reader.union(position, 4, espdl.TypeInfoValue);
|
||||
$.denotation = reader.string_(position, 8, null);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.OperatorSetId = class OperatorSetId {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.OperatorSetId();
|
||||
$.domain = reader.string_(position, 4, null);
|
||||
$.version = reader.int64_(position, 6, 0n);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.Function = class Function {
|
||||
|
||||
static decode(reader, position) {
|
||||
const $ = new espdl.Function();
|
||||
$.name = reader.string_(position, 4, null);
|
||||
$.input = reader.strings_(position, 6);
|
||||
$.output = reader.strings_(position, 8);
|
||||
$.attribute = reader.strings_(position, 10);
|
||||
$.attribute_proto = reader.tables(position, 12, espdl.Attribute);
|
||||
$.node = reader.tables(position, 14, espdl.Node);
|
||||
$.doc_string = reader.string_(position, 16, null);
|
||||
$.opset_import = reader.tables(position, 18, espdl.OperatorSetId);
|
||||
$.domain = reader.string_(position, 20, null);
|
||||
return $;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,404 @@
|
||||
import * as flatbuffers from './flatbuffers.js';
|
||||
|
||||
const espdl = {};
|
||||
|
||||
espdl.ModelFactory = class {
|
||||
|
||||
async match(context) {
|
||||
const identifier = context.identifier;
|
||||
const extension = identifier.lastIndexOf('.') > 0 ? identifier.split('.').pop().toLowerCase() : '';
|
||||
if (extension === 'espdl') {
|
||||
const stream = context.stream;
|
||||
if (stream && stream.length >= 16) {
|
||||
const buffer = stream.peek(16);
|
||||
const header = String.fromCharCode(...buffer.slice(0, 4));
|
||||
if (header === 'EDL2') {
|
||||
return context.set('espdl.binary', null);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async open(context) {
|
||||
espdl.schema = await context.require('./espdl-schema');
|
||||
espdl.schema = espdl.schema.espdl;
|
||||
const stream = context.stream;
|
||||
const reader = flatbuffers.BinaryReader.open(stream, 16);
|
||||
let model = null;
|
||||
try {
|
||||
model = espdl.schema.Model.create(reader);
|
||||
} catch (error) {
|
||||
const message = error && error.message ? error.message : error.toString();
|
||||
throw new espdl.Error(`File format is not espdl.Model (${message.replace(/\.$/, '')}).`);
|
||||
}
|
||||
const metadata = await espdl.Metadata.open(context);
|
||||
return new espdl.Model(metadata, model, stream);
|
||||
}
|
||||
};
|
||||
|
||||
espdl.Model = class {
|
||||
|
||||
constructor(metadata, model, stream) {
|
||||
this.format = `ESP-DL v${model.ir_version}`;
|
||||
this.description = model.doc_string || '';
|
||||
this.modules = [];
|
||||
this.metadata = [];
|
||||
if (model.metadata_props) {
|
||||
for (const prop of model.metadata_props) {
|
||||
this.metadata.push(new espdl.Argument(prop.key, prop.value));
|
||||
}
|
||||
}
|
||||
if (model.graph) {
|
||||
const graph = new espdl.Graph(metadata, model.graph, model, stream);
|
||||
this.modules.push(graph);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
espdl.Graph = class {
|
||||
|
||||
constructor(metadata, graph) {
|
||||
this.name = graph.name || '';
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.nodes = [];
|
||||
this.signatures = [];
|
||||
const context = new espdl.Context(graph);
|
||||
if (graph.node) {
|
||||
for (let i = 0; i < graph.node.length; i++) {
|
||||
const node = graph.node[i];
|
||||
const nodeObj = new espdl.Node(metadata, context, node, i.toString());
|
||||
this.nodes.push(nodeObj);
|
||||
}
|
||||
}
|
||||
if (graph.input) {
|
||||
for (let i = 0; i < graph.input.length; i++) {
|
||||
const valueInfo = graph.input[i];
|
||||
const tensor = context.initializer(valueInfo.name);
|
||||
if (!tensor) {
|
||||
const value = context.value(valueInfo.name);
|
||||
const values = value ? [value] : [];
|
||||
const argument = new espdl.Argument(valueInfo.name, values);
|
||||
this.inputs.push(argument);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (graph.output) {
|
||||
for (let i = 0; i < graph.output.length; i++) {
|
||||
const valueInfo = graph.output[i];
|
||||
const value = context.value(valueInfo.name);
|
||||
const values = value ? [value] : [];
|
||||
const argument = new espdl.Argument(valueInfo.name, values);
|
||||
this.outputs.push(argument);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
espdl.Argument = class {
|
||||
|
||||
constructor(name, value, type, visible) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.type = type || null;
|
||||
this.visible = visible !== false;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.Value = class {
|
||||
|
||||
constructor(name, type, initializer) {
|
||||
this.name = name;
|
||||
this.type = type || null;
|
||||
this.initializer = initializer || null;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.Node = class {
|
||||
|
||||
constructor(metadata, context, node, identifier) {
|
||||
this.name = node.name || '';
|
||||
this.identifier = identifier;
|
||||
this.type = null;
|
||||
if (metadata) {
|
||||
this.type = metadata.type('espdl', node.op_type);
|
||||
}
|
||||
if (!this.type) {
|
||||
this.type = { name: node.op_type };
|
||||
}
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.attributes = [];
|
||||
if (node.input) {
|
||||
for (let i = 0; i < node.input.length;) {
|
||||
const inputMeta = this.type && Array.isArray(this.type.inputs) && i < this.type.inputs.length ? this.type.inputs[i] : { name: i.toString() };
|
||||
const count = inputMeta.list ? node.input.length - i : 1;
|
||||
const list = node.input.slice(i, i + count);
|
||||
const values = list.map((inputName) => {
|
||||
if (!inputName) {
|
||||
return null;
|
||||
}
|
||||
return context.value(inputName);
|
||||
}).filter((v) => v);
|
||||
const argument = new espdl.Argument(inputMeta.name, values);
|
||||
this.inputs.push(argument);
|
||||
i += count;
|
||||
}
|
||||
}
|
||||
if (node.output) {
|
||||
for (let i = 0; i < node.output.length;) {
|
||||
const outputMeta = this.type && Array.isArray(this.type.outputs) && i < this.type.outputs.length ? this.type.outputs[i] : { name: i.toString() };
|
||||
const count = outputMeta.list ? node.output.length - i : 1;
|
||||
const list = node.output.slice(i, i + count);
|
||||
const values = list.map((outputName) => {
|
||||
if (!outputName) {
|
||||
return null;
|
||||
}
|
||||
return context.value(outputName);
|
||||
}).filter((v) => v);
|
||||
const argument = new espdl.Argument(outputMeta.name, values);
|
||||
this.outputs.push(argument);
|
||||
i += count;
|
||||
}
|
||||
}
|
||||
if (node.attribute) {
|
||||
for (const attr of node.attribute) {
|
||||
const name = attr.name || '';
|
||||
let value = null;
|
||||
let type = null;
|
||||
switch (attr.attr_type) {
|
||||
case espdl.schema.AttributeType.FLOAT:
|
||||
value = attr.f ? attr.f.f : 0;
|
||||
type = 'float32';
|
||||
break;
|
||||
case espdl.schema.AttributeType.INT:
|
||||
value = attr.i ? Number(attr.i.i) : 0;
|
||||
type = 'int64';
|
||||
break;
|
||||
case espdl.schema.AttributeType.STRING:
|
||||
value = attr.s ? new TextDecoder('utf-8').decode(attr.s) : '';
|
||||
type = 'string';
|
||||
break;
|
||||
case espdl.schema.AttributeType.TENSOR:
|
||||
value = attr.t ? new espdl.Tensor(0, attr.t) : null;
|
||||
type = 'tensor';
|
||||
break;
|
||||
case espdl.schema.AttributeType.FLOATS:
|
||||
value = attr.floats ? Array.from(attr.floats) : [];
|
||||
type = 'float32[]';
|
||||
break;
|
||||
case espdl.schema.AttributeType.INTS:
|
||||
value = attr.ints ? Array.from(attr.ints).map((i) => Number(i)) : [];
|
||||
type = 'int64[]';
|
||||
break;
|
||||
case espdl.schema.AttributeType.STRINGS:
|
||||
value = attr.strings ? attr.strings.map((s) => new TextDecoder('utf-8').decode(s)) : [];
|
||||
type = 'string[]';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const attribute = new espdl.Argument(name, value, type);
|
||||
this.attributes.push(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
espdl.Tensor = class {
|
||||
|
||||
constructor(index, tensor) {
|
||||
this.identifier = index.toString();
|
||||
this.name = tensor.name || '';
|
||||
this.type = new espdl.TensorType(tensor);
|
||||
this.category = '';
|
||||
this.encoding = this.type.dataType === 'string' ? '|' : '<';
|
||||
this.values = null;
|
||||
if (tensor.float_data && tensor.float_data.length > 0) {
|
||||
this.values = new Float32Array(tensor.float_data);
|
||||
} else if (tensor.int32_data && tensor.int32_data.length > 0) {
|
||||
this.values = new Int32Array(tensor.int32_data);
|
||||
} else if (tensor.int64_data && tensor.int64_data.length > 0) {
|
||||
this.values = new BigInt64Array(tensor.int64_data);
|
||||
} else if (tensor.string_data && tensor.string_data.length > 0) {
|
||||
this.values = tensor.string_data;
|
||||
} else if (tensor.raw_data && tensor.raw_data.length > 0) {
|
||||
const length = tensor.raw_data.length * 16;
|
||||
const data = new Uint8Array(length);
|
||||
for (let i = 0; i < tensor.raw_data.length; i++) {
|
||||
data.set(tensor.raw_data[i].bytes, i * 16);
|
||||
}
|
||||
this.values = data;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
espdl.TensorType = class {
|
||||
|
||||
constructor(tensor) {
|
||||
let dataType = '';
|
||||
if (tensor.value_info_type === undefined) {
|
||||
dataType = tensor.data_type;
|
||||
this.shape = new espdl.TensorShape(tensor.dims ? Array.from(tensor.dims).map((d) => Number(d)) : []);
|
||||
} else {
|
||||
const value = tensor.value_info_type.value;
|
||||
dataType = value ? value.elem_type : undefined;
|
||||
let shape = [];
|
||||
const dim = value && value.shape && value.shape.dim;
|
||||
if (dim && dim.length > 0) {
|
||||
shape = dim.map((d) => {
|
||||
if (d && d.value) {
|
||||
if (d.value.dim_type === 1) {
|
||||
return Number(d.value.dim_value);
|
||||
} else if (d.value.dim_type === 2) {
|
||||
return d.value.dim_param || '?';
|
||||
}
|
||||
}
|
||||
return '?';
|
||||
});
|
||||
}
|
||||
this.shape = new espdl.TensorShape(shape);
|
||||
}
|
||||
switch (dataType) {
|
||||
case espdl.schema.TensorDataType.FLOAT: this.dataType = 'float32'; break;
|
||||
case espdl.schema.TensorDataType.DOUBLE: this.dataType = 'float64'; break;
|
||||
case espdl.schema.TensorDataType.BOOL: this.dataType = 'boolean'; break;
|
||||
default: this.dataType = espdl.schema.TensorDataType[dataType] ? espdl.schema.TensorDataType[dataType].toLowerCase() : '?'; break;
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.dataType + this.shape.toString();
|
||||
}
|
||||
};
|
||||
|
||||
espdl.TensorShape = class {
|
||||
|
||||
constructor(dimensions) {
|
||||
this.dimensions = dimensions;
|
||||
}
|
||||
|
||||
toString() {
|
||||
if (!this.dimensions || this.dimensions.length === 0) {
|
||||
return '';
|
||||
}
|
||||
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.Metadata = class {
|
||||
|
||||
static async open(context) {
|
||||
if (!espdl.Metadata._metadata) {
|
||||
let data = null;
|
||||
try {
|
||||
data = await context.asset('espdl-metadata.json');
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
espdl.Metadata._metadata = new espdl.Metadata(data);
|
||||
}
|
||||
return espdl.Metadata._metadata;
|
||||
}
|
||||
|
||||
constructor(data) {
|
||||
this._types = new Map();
|
||||
if (data) {
|
||||
const types = JSON.parse(data);
|
||||
for (const type of types) {
|
||||
if (!this._types.has(type.module)) {
|
||||
this._types.set(type.module, new Map());
|
||||
}
|
||||
const types = this._types.get(type.module);
|
||||
if (!types.has(type.name)) {
|
||||
types.set(type.name, []);
|
||||
}
|
||||
types.get(type.name).push(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type(domain, name) {
|
||||
domain = domain || 'espdl';
|
||||
let current = null;
|
||||
if (this._types.has(domain)) {
|
||||
const types = this._types.get(domain);
|
||||
if (types.has(name)) {
|
||||
for (const type of types.get(name)) {
|
||||
if (!current || type.version > current.version) {
|
||||
current = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.Context = class {
|
||||
|
||||
constructor(graph) {
|
||||
this._initializers = new Map();
|
||||
this._values = new Map();
|
||||
if (graph.initializer) {
|
||||
for (let i = 0; i < graph.initializer.length; i++) {
|
||||
const tensor = graph.initializer[i];
|
||||
const name = tensor.name || '';
|
||||
if (name) {
|
||||
const initializer = new espdl.Tensor(i, tensor);
|
||||
this._initializers.set(name, initializer);
|
||||
this._values.set(name, new espdl.Value(name, initializer.type, initializer));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (graph.input) {
|
||||
for (const valueInfo of graph.input) {
|
||||
const name = valueInfo.name || '';
|
||||
if (name && !this._values.has(name)) {
|
||||
const type = valueInfo.value_info_type ? new espdl.TensorType(valueInfo) : null;
|
||||
this._values.set(name, new espdl.Value(name, type, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (graph.output) {
|
||||
for (const valueInfo of graph.output) {
|
||||
const name = valueInfo.name || '';
|
||||
if (name && !this._values.has(name)) {
|
||||
const type = valueInfo.value_info_type ? new espdl.TensorType(valueInfo) : null;
|
||||
this._values.set(name, new espdl.Value(name, type, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (graph.value_info) {
|
||||
for (const valueInfo of graph.value_info) {
|
||||
const name = valueInfo.name || '';
|
||||
if (name && !this._values.has(name)) {
|
||||
const type = valueInfo.value_info_type ? new espdl.TensorType(valueInfo) : null;
|
||||
this._values.set(name, new espdl.Value(name, type, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value(name) {
|
||||
if (!this._values.has(name)) {
|
||||
this._values.set(name, new espdl.Value(name, null, null));
|
||||
}
|
||||
return this._values.get(name);
|
||||
}
|
||||
|
||||
initializer(name) {
|
||||
return this._initializers.get(name) || null;
|
||||
}
|
||||
};
|
||||
|
||||
espdl.Error = class extends Error {
|
||||
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'Error loading ESP-DL model.';
|
||||
}
|
||||
};
|
||||
|
||||
export const ModelFactory = espdl.ModelFactory;
|
||||
@@ -0,0 +1,86 @@
|
||||
[
|
||||
{
|
||||
"name": "convolution",
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "inner_product",
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "activation",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "softmax",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "transpose",
|
||||
"category": "Transform"
|
||||
},
|
||||
{
|
||||
"name": "pool",
|
||||
"category": "Pool"
|
||||
},
|
||||
{
|
||||
"name": "instancenorm_1d",
|
||||
"category": "Normalization"
|
||||
},
|
||||
{
|
||||
"name": "batch_norm",
|
||||
"category": "Normalization"
|
||||
},
|
||||
{
|
||||
"name": "batchnorm",
|
||||
"category": "Normalization"
|
||||
},
|
||||
{
|
||||
"name": "reshape",
|
||||
"category": "Shape"
|
||||
},
|
||||
{
|
||||
"name": "dynamic_quantize",
|
||||
"category": "Quantization"
|
||||
},
|
||||
{
|
||||
"name": "dynamic_dequantize",
|
||||
"category": "Quantization"
|
||||
},
|
||||
{
|
||||
"name": "concat",
|
||||
"category": "Tensor"
|
||||
},
|
||||
{
|
||||
"name": "general_concat",
|
||||
"category": "Tensor"
|
||||
},
|
||||
{
|
||||
"name": "upsample",
|
||||
"category": "Data"
|
||||
},
|
||||
{
|
||||
"name": "relu",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "prelu",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "tanh",
|
||||
"category": "Activation"
|
||||
},
|
||||
{
|
||||
"name": "squeeze",
|
||||
"category": "Transform"
|
||||
},
|
||||
{
|
||||
"name": "rnn_arch",
|
||||
"category": "Layer"
|
||||
},
|
||||
{
|
||||
"name": "flatten",
|
||||
"category": "Shape"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,461 @@
|
||||
|
||||
const espresso = {};
|
||||
|
||||
espresso.ModelFactory = class {
|
||||
|
||||
async match(context) {
|
||||
const identifier = context.identifier.toLowerCase();
|
||||
if (identifier.endsWith('.espresso.net')) {
|
||||
const obj = await context.peek('json');
|
||||
if (obj && Array.isArray(obj.layers) && obj.format_version) {
|
||||
return context.set('espresso.net', obj);
|
||||
}
|
||||
}
|
||||
if (identifier.endsWith('.espresso.shape')) {
|
||||
const obj = await context.peek('json');
|
||||
if (obj && obj.layer_shapes) {
|
||||
return context.set('espresso.shape', obj);
|
||||
}
|
||||
}
|
||||
if (identifier.endsWith('.espresso.weights')) {
|
||||
const target = await context.read('binary');
|
||||
return context.set('espresso.weights', target);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
filter(context, match) {
|
||||
if (context.type === 'espresso.net' && (match.type === 'espresso.weights' || match.type === 'espresso.shape' || match.type === 'coreml.metadata.mlmodelc')) {
|
||||
return false;
|
||||
}
|
||||
if (context.type === 'espresso.shape' && (match.type === 'espresso.weights' || match.type === 'coreml.metadata.mlmodelc')) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async open(context) {
|
||||
const metadata = await context.metadata('espresso-metadata.json');
|
||||
switch (context.type) {
|
||||
case 'espresso.net': {
|
||||
const reader = new espresso.Reader(context.value, null, null);
|
||||
await reader.read(context);
|
||||
return new espresso.Model(metadata, reader);
|
||||
}
|
||||
case 'espresso.weights': {
|
||||
const reader = new espresso.Reader(null, context.value, null);
|
||||
await reader.read(context);
|
||||
return new espresso.Model(metadata, reader);
|
||||
}
|
||||
case 'espresso.shape': {
|
||||
const reader = new espresso.Reader(null, null, context.value);
|
||||
await reader.read(context);
|
||||
return new espresso.Model(metadata, reader);
|
||||
}
|
||||
default: {
|
||||
throw new espresso.Error(`Unsupported Core ML format '${context.type}'.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
espresso.Model = class {
|
||||
|
||||
constructor(metadata, reader) {
|
||||
this.format = reader.format;
|
||||
this.metadata = [];
|
||||
this.modules = [new espresso.Graph(metadata, reader)];
|
||||
if (reader.version) {
|
||||
this.version = reader.version;
|
||||
}
|
||||
if (reader.description) {
|
||||
this.description = reader.description;
|
||||
}
|
||||
for (const argument of reader.properties) {
|
||||
this.metadata.push(argument);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
espresso.Graph = class {
|
||||
|
||||
constructor(metadata, reader) {
|
||||
this.name = '';
|
||||
this.type = reader.type;
|
||||
for (const value of reader.values.values()) {
|
||||
const name = value.name;
|
||||
const type = value.type;
|
||||
const description = value.description;
|
||||
const initializer = value.initializer;
|
||||
if (!value.value) {
|
||||
value.value = new espresso.Value(name, type, description, initializer);
|
||||
}
|
||||
}
|
||||
this.inputs = reader.inputs.map((argument) => {
|
||||
const values = argument.value.map((value) => value.value);
|
||||
return new espresso.Argument(argument.name, values, null, argument.visible);
|
||||
});
|
||||
this.outputs = reader.outputs.map((argument) => {
|
||||
const values = argument.value.map((value) => value.value);
|
||||
return new espresso.Argument(argument.name, values, null, argument.visible);
|
||||
});
|
||||
for (const obj of reader.nodes) {
|
||||
const attributes = obj.attributes;
|
||||
switch (obj.type) {
|
||||
case 'loop':
|
||||
attributes.conditionNetwork = new espresso.Graph(attributes.conditionNetwork);
|
||||
attributes.bodyNetwork = new espresso.Graph(attributes.bodyNetwork);
|
||||
break;
|
||||
case 'branch':
|
||||
attributes.ifBranch = new espresso.Graph(attributes.ifBranch);
|
||||
attributes.elseBranch = new espresso.Graph(attributes.elseBranch);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.nodes = reader.nodes.map((obj) => new espresso.Node(metadata, obj));
|
||||
}
|
||||
};
|
||||
|
||||
espresso.Argument = class {
|
||||
|
||||
constructor(name, value, type = null, visible = true) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
this.visible = visible;
|
||||
}
|
||||
};
|
||||
|
||||
espresso.Value = class {
|
||||
|
||||
constructor(name, type, description = null, initializer = null) {
|
||||
if (typeof name !== 'string') {
|
||||
throw new espresso.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
|
||||
}
|
||||
this.name = name;
|
||||
this.type = !type && initializer ? initializer.type : type;
|
||||
this.description = description;
|
||||
this.initializer = initializer;
|
||||
this.quantization = initializer ? initializer.quantization : null;
|
||||
}
|
||||
};
|
||||
|
||||
espresso.Node = class {
|
||||
|
||||
constructor(metadata, obj) {
|
||||
if (!obj.type) {
|
||||
throw new Error('Undefined node type.');
|
||||
}
|
||||
const type = metadata.type(obj.type);
|
||||
this.type = type ? { ...type } : { name: obj.type };
|
||||
this.type.name = obj.type.split(':').pop();
|
||||
this.name = obj.name || '';
|
||||
this.description = obj.description || '';
|
||||
this.inputs = (obj.inputs || []).map((argument) => {
|
||||
const values = argument.value.map((value) => value.value);
|
||||
return new espresso.Argument(argument.name, values, null, argument.visible);
|
||||
});
|
||||
this.outputs = (obj.outputs || []).map((argument) => {
|
||||
const values = argument.value.map((value) => value.value);
|
||||
return new espresso.Argument(argument.name, values, null, argument.visible);
|
||||
});
|
||||
this.attributes = Object.entries(obj.attributes || []).map(([name, value]) => {
|
||||
const schema = metadata.attribute(obj.type, name);
|
||||
let type = null;
|
||||
let visible = true;
|
||||
if (schema) {
|
||||
type = schema.type ? schema.type : type;
|
||||
if (schema.visible === false) {
|
||||
visible = false;
|
||||
} else if (schema.default !== undefined) {
|
||||
if (Array.isArray(value)) {
|
||||
value = value.map((item) => Number(item));
|
||||
}
|
||||
if (typeof value === 'bigint') {
|
||||
value = value.toNumber();
|
||||
}
|
||||
if (JSON.stringify(schema.default) === JSON.stringify(value)) {
|
||||
visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new espresso.Argument(name, value, type, visible);
|
||||
});
|
||||
if (Array.isArray(obj.chain)) {
|
||||
this.chain = obj.chain.map((obj) => new espresso.Node(metadata, obj));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
espresso.Tensor = class {
|
||||
|
||||
constructor(type, data, quantization, category) {
|
||||
this.type = type;
|
||||
this.values = data;
|
||||
this.quantization = quantization;
|
||||
this.category = category;
|
||||
this.encoding = '<';
|
||||
}
|
||||
};
|
||||
|
||||
espresso.TensorType = class {
|
||||
|
||||
constructor(dataType, shape) {
|
||||
this.dataType = dataType;
|
||||
this.shape = shape || new espresso.TensorShape([]);
|
||||
}
|
||||
|
||||
equals(obj) {
|
||||
return obj && this.dataType === obj.dataType && this.shape && this.shape.equals(obj.shape);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.dataType + this.shape.toString();
|
||||
}
|
||||
};
|
||||
|
||||
espresso.TensorShape = class {
|
||||
|
||||
constructor(dimensions) {
|
||||
this.dimensions = dimensions.map((dim) => typeof dim === 'bigint' ? dim.toNumber() : dim);
|
||||
}
|
||||
|
||||
equals(obj) {
|
||||
return obj && Array.isArray(obj.dimensions) && Array.isArray(this.dimensions) &&
|
||||
this.dimensions.length === obj.dimensions.length &&
|
||||
obj.dimensions.every((value, index) => this.dimensions[index] === value);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return Array.isArray(this.dimensions) && this.dimensions.length > 0 ?
|
||||
`[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]` : '';
|
||||
}
|
||||
};
|
||||
|
||||
espresso.Reader = class {
|
||||
|
||||
constructor(net, weights, shape) {
|
||||
this.targets = [net, shape, weights];
|
||||
}
|
||||
|
||||
async read(context) {
|
||||
this.format = 'Espresso';
|
||||
this.properties = [];
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.nodes = [];
|
||||
let [net, shape, weights] = this.targets;
|
||||
delete this.targets;
|
||||
if (!net) {
|
||||
const name = context.identifier.replace(/\.espresso\.(net|weights|shape)$/i, '.espresso.net');
|
||||
const content = await context.fetch(name);
|
||||
net = await content.read('json');
|
||||
}
|
||||
this.shapes = new Map();
|
||||
if (!shape) {
|
||||
const name = context.identifier.replace(/\.espresso\.(net|weights|shape)$/i, '.espresso.shape');
|
||||
try {
|
||||
const content = await context.fetch(name);
|
||||
shape = await content.read('json');
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
}
|
||||
if (shape && shape.layer_shapes) {
|
||||
for (const [name, value] of Object.entries(shape.layer_shapes)) {
|
||||
const dimensions = [value.n, value.k, value.w, value.h];
|
||||
const shape = new espresso.TensorShape(dimensions);
|
||||
this.shapes.set(name, shape);
|
||||
}
|
||||
}
|
||||
this.blobs = new Map();
|
||||
if (!weights) {
|
||||
const name = net && net.storage ? net.storage : context.identifier.replace(/\.espresso\.(net|weights|shape)$/i, '.espresso.weights');
|
||||
try {
|
||||
const content = await context.fetch(name);
|
||||
weights = await content.read('binary');
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
}
|
||||
if (weights) {
|
||||
const reader = weights;
|
||||
const length = reader.uint64().toNumber();
|
||||
for (let i = 0; i < length; i++) {
|
||||
const key = reader.uint64().toNumber();
|
||||
const size = reader.uint64().toNumber();
|
||||
this.blobs.set(key, size);
|
||||
}
|
||||
for (const [key, size] of this.blobs) {
|
||||
const buffer = reader.read(size);
|
||||
this.blobs.set(key, buffer);
|
||||
}
|
||||
}
|
||||
this.values = new Map();
|
||||
if (net.format_version) {
|
||||
const major = Math.floor(net.format_version / 100);
|
||||
const minor = net.format_version % 100;
|
||||
this.format += ` v${major}.${minor}`;
|
||||
}
|
||||
if (net && Array.isArray(net.layers)) {
|
||||
for (const layer of net.layers) {
|
||||
const type = layer.type;
|
||||
const data = { ...layer };
|
||||
const top = layer.top.split(',').map((name) => this._value(name));
|
||||
const bottom = layer.bottom.split(',').map((name) => this._value(name));
|
||||
const obj = {};
|
||||
obj.type = type;
|
||||
obj.name = layer.name;
|
||||
obj.attributes = data;
|
||||
obj.inputs = [{ name: 'inputs', value: bottom }];
|
||||
obj.outputs = [{ name: 'outputs', value: top }];
|
||||
obj.chain = [];
|
||||
switch (type) {
|
||||
case 'convolution':
|
||||
case 'deconvolution': {
|
||||
this._weights(obj, data, [data.C, data.K, data.Nx, data.Ny]);
|
||||
if (data.has_biases) {
|
||||
obj.inputs.push(this._initializer('biases', data.blob_biases, 'float32', [data.C]));
|
||||
}
|
||||
delete data.has_biases;
|
||||
delete data.blob_biases;
|
||||
break;
|
||||
}
|
||||
case 'batchnorm': {
|
||||
obj.inputs.push(this._initializer('params', data.blob_batchnorm_params, 'float32', [4, data.C]));
|
||||
delete data.blob_batchnorm_params;
|
||||
break;
|
||||
}
|
||||
case 'inner_product': {
|
||||
this._weights(obj, data, [data.nC, data.nB]);
|
||||
if (data.has_biases) {
|
||||
obj.inputs.push(this._initializer('biases', data.blob_biases, 'float32', [data.nC]));
|
||||
}
|
||||
delete data.has_biases;
|
||||
delete data.blob_biases;
|
||||
break;
|
||||
}
|
||||
case 'conv3d': {
|
||||
this._weights(obj, data, null);
|
||||
if (data.has_biases) {
|
||||
obj.inputs.push(this._initializer('biases', data.blob_biases, 'float32', null));
|
||||
}
|
||||
delete data.has_biases;
|
||||
delete data.blob_biases;
|
||||
break;
|
||||
}
|
||||
case 'instancenorm_1d':
|
||||
case 'dynamic_dequantize': {
|
||||
this._weights(obj, data, null);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
const blobs = Object.keys(data).filter((key) => key.startsWith('blob_'));
|
||||
if (blobs.length > 0) {
|
||||
throw new espresso.Error(`Unknown blob '${blobs.join(',')}' for type '${type}'.`);
|
||||
}
|
||||
if (data.has_prelu) {
|
||||
obj.chain.push({ type: 'prelu' });
|
||||
}
|
||||
if (data.fused_relu || data.has_relu) {
|
||||
obj.chain.push({ type: 'relu' });
|
||||
}
|
||||
if (data.fused_tanh || data.has_tanh) {
|
||||
obj.chain.push({ type: 'tanh' });
|
||||
}
|
||||
if (data.has_batch_norm) {
|
||||
obj.chain.push({ type: 'batch_norm' });
|
||||
}
|
||||
if (data.weights) {
|
||||
for (const [name, identifier] of Object.entries(data.weights)) {
|
||||
obj.inputs.push(this._initializer(name, identifier, 'float32', null));
|
||||
}
|
||||
delete data.weights;
|
||||
}
|
||||
delete data.name;
|
||||
delete data.type;
|
||||
delete data.top;
|
||||
delete data.bottom;
|
||||
delete data.fused_tanh;
|
||||
delete data.fused_relu;
|
||||
delete data.has_prelu;
|
||||
delete data.has_relu;
|
||||
delete data.has_tanh;
|
||||
delete data.has_batch_norm;
|
||||
this.nodes.push(obj);
|
||||
}
|
||||
}
|
||||
delete this.shapes;
|
||||
delete this.blobs;
|
||||
}
|
||||
|
||||
_value(name) {
|
||||
if (!this.values.has(name)) {
|
||||
const shape = this.shapes.get(name);
|
||||
const type = shape ? new espresso.TensorType('float32', shape) : null;
|
||||
this.values.set(name, { name, type });
|
||||
}
|
||||
return this.values.get(name);
|
||||
}
|
||||
|
||||
_weights(obj, data, dimensions) {
|
||||
if (data.blob_weights !== undefined) {
|
||||
obj.inputs.push(this._initializer('weights', data.blob_weights, 'float32', dimensions));
|
||||
delete data.blob_weights;
|
||||
return;
|
||||
}
|
||||
if (data.blob_weights_f16 !== undefined) {
|
||||
obj.inputs.push(this._initializer('weights', data.blob_weights_f16, 'float16', dimensions));
|
||||
delete data.blob_weights_f16;
|
||||
return;
|
||||
}
|
||||
const keys = ['wBeta', 'wGamma', 'W_S8', 'W_int8', 'W_t_int8'];
|
||||
for (const key of keys) {
|
||||
if (data.weights && data.weights[key] !== undefined) {
|
||||
let dataType = 'float32';
|
||||
let name = key;
|
||||
if (key.endsWith('_S8')) {
|
||||
dataType = 'int8';
|
||||
name = key.replace(/_S8$/, '');
|
||||
} else if (key.endsWith('_int8')) {
|
||||
dataType = 'int8';
|
||||
name = key.replace(/_int8$/, '');
|
||||
}
|
||||
obj.inputs.push(this._initializer(name, data.weights[key], dataType, dimensions));
|
||||
delete data.weights[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_initializer(name, identifier, dataType, dimensions) {
|
||||
if (!Number.isInteger(identifier)) {
|
||||
throw new espresso.Error(`Invalid '${identifier}' blob identifier.`);
|
||||
}
|
||||
dataType = dataType || 'float32';
|
||||
const blob = this.blobs.get(identifier);
|
||||
if (!dimensions) {
|
||||
const itemsize = dataType === 'float32' ? 4 : 1;
|
||||
dimensions = blob ? [blob.length / itemsize] : ['?'];
|
||||
}
|
||||
const shape = new espresso.TensorShape(dimensions);
|
||||
const type = new espresso.TensorType(dataType, shape);
|
||||
const value = {};
|
||||
const initializer = new espresso.Tensor(type, blob, null, 'Blob');
|
||||
value.value = new espresso.Value(`${identifier}\nblob`, type, null, initializer);
|
||||
return { name, value: [value] };
|
||||
}
|
||||
};
|
||||
|
||||
espresso.Error = class extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'Error loading Espresso model.';
|
||||
}
|
||||
};
|
||||
|
||||
export const ModelFactory = espresso.ModelFactory;
|
||||
|
After Width: | Height: | Size: 34 KiB |
@@ -0,0 +1,526 @@
|
||||
|
||||
const flatbuffers = {};
|
||||
|
||||
flatbuffers.BinaryReader = class {
|
||||
|
||||
static open(data, offset) {
|
||||
offset = offset || 0;
|
||||
if (data && data.length >= (offset + 8)) {
|
||||
const position = data instanceof Uint8Array ? -1 : data.position;
|
||||
const reader = data instanceof Uint8Array ?
|
||||
new flatbuffers.BufferReader(data) :
|
||||
new flatbuffers.StreamReader(data);
|
||||
reader.root = reader.int32(offset) + offset;
|
||||
let value = false;
|
||||
if (reader.root > 0 && reader.root < reader.length) {
|
||||
const buffer = reader.read(offset + 4, 4);
|
||||
reader.identifier = buffer.every((c) => c >= 32 && c <= 128) ? String.fromCharCode(...buffer) : '';
|
||||
const vtable = reader.int32(reader.root);
|
||||
if (vtable < 0 || (vtable > 4 && vtable < 1024)) {
|
||||
const start = reader.root - vtable;
|
||||
if (start > 0 && (start + 4) < reader.length) {
|
||||
const last = reader.int16(start) + start;
|
||||
if (last < reader.length) {
|
||||
const max = reader.int16(start + 2);
|
||||
if (max > 0 && (max & 1) === 0) {
|
||||
const offsets = [];
|
||||
for (let i = start + 4; i < last; i += 2) {
|
||||
const offset = reader.int16(i);
|
||||
offsets.push(offset);
|
||||
}
|
||||
value = max > Math.max(...offsets);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (position !== -1) {
|
||||
data.seek(position);
|
||||
}
|
||||
if (value) {
|
||||
return reader;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
bool(offset) {
|
||||
return Boolean(this.int8(offset));
|
||||
}
|
||||
|
||||
bool_(position, offset, defaultValue) {
|
||||
offset = this.__offset(position, offset);
|
||||
return offset ? this.bool(position + offset) : defaultValue;
|
||||
}
|
||||
|
||||
int8(offset) {
|
||||
return this.uint8(offset) << 24 >> 24;
|
||||
}
|
||||
|
||||
int8_(position, offset, defaultValue) {
|
||||
offset = this.__offset(position, offset);
|
||||
return offset ? this.int8(position + offset) : defaultValue;
|
||||
}
|
||||
|
||||
uint8_(position, offset, defaultValue) {
|
||||
offset = this.__offset(position, offset);
|
||||
return offset ? this.uint8(position + offset) : defaultValue;
|
||||
}
|
||||
|
||||
int16_(position, offset, defaultValue) {
|
||||
offset = this.__offset(position, offset);
|
||||
return offset ? this.int16(position + offset) : defaultValue;
|
||||
}
|
||||
|
||||
uint16_(position, offset, defaultValue) {
|
||||
offset = this.__offset(position, offset);
|
||||
return offset ? this.uint16(position + offset) : defaultValue;
|
||||
}
|
||||
|
||||
int32_(position, offset, defaultValue) {
|
||||
offset = this.__offset(position, offset);
|
||||
return offset ? this.int32(position + offset) : defaultValue;
|
||||
}
|
||||
|
||||
uint32_(position, offset, defaultValue) {
|
||||
offset = this.__offset(position, offset);
|
||||
return offset ? this.uint32(position + offset) : defaultValue;
|
||||
}
|
||||
|
||||
int64_(position, offset, defaultValue) {
|
||||
offset = this.__offset(position, offset);
|
||||
return offset ? this.int64(position + offset) : defaultValue;
|
||||
}
|
||||
|
||||
uint64_(position, offset, defaultValue) {
|
||||
offset = this.__offset(position, offset);
|
||||
return offset ? this.uint64(position + offset) : defaultValue;
|
||||
}
|
||||
|
||||
float32_(position, offset, defaultValue) {
|
||||
offset = this.__offset(position, offset);
|
||||
return offset ? this.float32(position + offset) : defaultValue;
|
||||
}
|
||||
|
||||
float64_(position, offset, defaultValue) {
|
||||
offset = this.__offset(position, offset);
|
||||
return offset ? this.float64(position + offset) : defaultValue;
|
||||
}
|
||||
|
||||
string(offset, encoding) {
|
||||
offset += this.int32(offset);
|
||||
const length = this.int32(offset);
|
||||
offset += 4;
|
||||
if (encoding === 1) {
|
||||
return this.read(offset, length);
|
||||
}
|
||||
let text = '';
|
||||
for (let i = 0; i < length;) {
|
||||
let codePoint = 0;
|
||||
const a = this.uint8(offset + i++);
|
||||
if (a < 0xC0) {
|
||||
codePoint = a;
|
||||
} else {
|
||||
const b = this.uint8(offset + i++);
|
||||
if (a < 0xE0) {
|
||||
codePoint = ((a & 0x1F) << 6) | (b & 0x3F);
|
||||
} else {
|
||||
const c = this.uint8(offset + i++);
|
||||
if (a < 0xF0) {
|
||||
codePoint = ((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F);
|
||||
} else {
|
||||
const d = this.uint8(offset + i++);
|
||||
codePoint = ((a & 0x07) << 18) | ((b & 0x3F) << 12) | ((c & 0x3F) << 6) | (d & 0x3F);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Encode UTF-16
|
||||
if (codePoint < 0x10000) {
|
||||
text += String.fromCharCode(codePoint);
|
||||
} else {
|
||||
codePoint -= 0x10000;
|
||||
text += String.fromCharCode((codePoint >> 10) + 0xD800, (codePoint & ((1 << 10) - 1)) + 0xDC00);
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
string_(position, offset, defaultValue) {
|
||||
offset = this.__offset(position, offset);
|
||||
return offset ? this.string(position + offset) : defaultValue;
|
||||
}
|
||||
|
||||
bools_(position, offset) {
|
||||
offset = this.__offset(position, offset);
|
||||
if (offset) {
|
||||
const length = this.__vector_len(position + offset);
|
||||
offset = this.__vector(position + offset);
|
||||
const array = new Array(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
array[i] = this.uint8(offset + i) ? true : false;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
int64s_(position, offset) {
|
||||
offset = this.__offset(position, offset);
|
||||
if (offset) {
|
||||
const length = this.__vector_len(position + offset);
|
||||
offset = this.__vector(position + offset);
|
||||
const array = new Array(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
array[i] = this.int64(offset + (i << 3));
|
||||
}
|
||||
return array;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
uint64s_(position, offset) {
|
||||
offset = this.__offset(position, offset);
|
||||
if (offset) {
|
||||
const length = this.__vector_len(position + offset);
|
||||
offset = this.__vector(position + offset);
|
||||
const array = new Array(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
array[i] = this.uint64(offset + (i << 3));
|
||||
}
|
||||
return array;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
strings_(position, offset) {
|
||||
offset = this.__offset(position, offset);
|
||||
if (offset) {
|
||||
const length = this.__vector_len(position + offset);
|
||||
offset = this.__vector(position + offset);
|
||||
const array = new Array(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
array[i] = this.string(offset + i * 4);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
struct(position, offset, type) {
|
||||
offset = this.__offset(position, offset);
|
||||
return offset ? type.decode(this, position + offset) : null;
|
||||
}
|
||||
|
||||
table(position, offset, type) {
|
||||
offset = this.__offset(position, offset);
|
||||
return offset ? type.decode(this, this.__indirect(position + offset)) : null;
|
||||
}
|
||||
|
||||
union(position, offset, type) {
|
||||
const type_offset = this.__offset(position, offset);
|
||||
const union_type = type_offset ? this.uint8(position + type_offset) : 0;
|
||||
offset = this.__offset(position, offset + 2);
|
||||
return offset ? type.decode(this, this.__union(position + offset), union_type) : null;
|
||||
}
|
||||
|
||||
array(position, offset, type) {
|
||||
offset = this.__offset(position, offset);
|
||||
if (offset) {
|
||||
const length = this.__vector_len(position + offset);
|
||||
offset = this.__vector(position + offset);
|
||||
const buffer = this.read(offset, length * type.BYTES_PER_ELEMENT);
|
||||
return new type(buffer.buffer, buffer.byteOffset, length);
|
||||
}
|
||||
return new type(0);
|
||||
}
|
||||
|
||||
unions(/* position, offset, decode */) {
|
||||
return new flatbuffers.Error('Not implemented.');
|
||||
}
|
||||
|
||||
structs(position, offset, type, size) {
|
||||
offset = this.__offset(position, offset);
|
||||
const length = offset ? this.__vector_len(position + offset) : 0;
|
||||
const list = new Array(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
list[i] = type.decode(this, this.__vector(position + offset) + i * size);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
tables(position, offset, type) {
|
||||
offset = this.__offset(position, offset);
|
||||
const length = offset ? this.__vector_len(position + offset) : 0;
|
||||
const list = new Array(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
list[i] = type.decode(this, this.__indirect(this.__vector(position + offset) + i * 4));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
__offset(bb_pos, vtableOffset) {
|
||||
const vtable = bb_pos - this.int32(bb_pos);
|
||||
return vtableOffset < this.int16(vtable) ? this.int16(vtable + vtableOffset) : 0;
|
||||
}
|
||||
|
||||
__indirect(offset) {
|
||||
return offset + this.int32(offset);
|
||||
}
|
||||
|
||||
__vector(offset) {
|
||||
return offset + this.int32(offset) + 4;
|
||||
}
|
||||
|
||||
__vector_len(offset) {
|
||||
return this.int32(offset + this.int32(offset));
|
||||
}
|
||||
|
||||
__union(offset) {
|
||||
return offset + this.int32(offset);
|
||||
}
|
||||
};
|
||||
|
||||
flatbuffers.BufferReader = class extends flatbuffers.BinaryReader {
|
||||
|
||||
constructor(data) {
|
||||
super();
|
||||
this.length = data.length;
|
||||
this._buffer = data;
|
||||
this._view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
||||
}
|
||||
|
||||
read(offset, length) {
|
||||
return this._buffer.slice(offset, offset + length);
|
||||
}
|
||||
|
||||
uint8(offset) {
|
||||
return this._buffer[offset];
|
||||
}
|
||||
|
||||
int16(offset) {
|
||||
return this._view.getInt16(offset, true);
|
||||
}
|
||||
|
||||
uint16(offset) {
|
||||
return this._view.getUint16(offset, true);
|
||||
}
|
||||
|
||||
int32(offset) {
|
||||
return this._view.getInt32(offset, true);
|
||||
}
|
||||
|
||||
uint32(offset) {
|
||||
return this._view.getUint32(offset, true);
|
||||
}
|
||||
|
||||
int64(offset) {
|
||||
return this._view.getBigInt64(offset, true);
|
||||
}
|
||||
|
||||
uint64(offset) {
|
||||
return this._view.getBigUint64(offset, true);
|
||||
}
|
||||
|
||||
float32(offset) {
|
||||
return this._view.getFloat32(offset, true);
|
||||
}
|
||||
|
||||
float64(offset) {
|
||||
return this._view.getFloat64(offset, true);
|
||||
}
|
||||
};
|
||||
|
||||
flatbuffers.StreamReader = class extends flatbuffers.BinaryReader {
|
||||
|
||||
constructor(stream) {
|
||||
super();
|
||||
this._length = stream.length;
|
||||
this._stream = stream;
|
||||
this._size = 0x10000000;
|
||||
this._offset = 0;
|
||||
this._window = Math.min(0x1000, stream.length);
|
||||
const buffer = this._stream.peek(this._window);
|
||||
this._buffer = buffer;
|
||||
this._view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
||||
this._chunk = -1;
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this._length;
|
||||
}
|
||||
|
||||
read(offset, length) {
|
||||
const buffer = new Uint8Array(length);
|
||||
this._read(buffer, offset);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
uint8(offset) {
|
||||
const position = this._fill(offset, 1);
|
||||
return this._view.getUint8(position);
|
||||
}
|
||||
|
||||
int16(offset) {
|
||||
const position = this._fill(offset, 2);
|
||||
return this._view.getInt16(position, true);
|
||||
}
|
||||
|
||||
uint16(offset) {
|
||||
const position = this._fill(offset, 2);
|
||||
return this._view.getUint16(position, true);
|
||||
}
|
||||
|
||||
int32(offset) {
|
||||
const position = this._fill(offset, 4);
|
||||
return this._view.getInt32(position, true);
|
||||
}
|
||||
|
||||
uint32(offset) {
|
||||
const position = this._fill(offset, 4);
|
||||
return this._view.getUint32(position, true);
|
||||
}
|
||||
|
||||
int64(offset) {
|
||||
const position = this._fill(offset, 8);
|
||||
return this._view.getBigInt64(position, true);
|
||||
}
|
||||
|
||||
uint64(offset) {
|
||||
const position = this._fill(offset, 8);
|
||||
return this._view.getBigUint64(position, true);
|
||||
}
|
||||
|
||||
float32(offset) {
|
||||
const position = this._fill(offset, 4);
|
||||
return this._view.getFloat32(position, true);
|
||||
}
|
||||
|
||||
float64(offset) {
|
||||
const position = this._fill(offset, 8);
|
||||
return this._view.getFloat64(position, true);
|
||||
}
|
||||
|
||||
_fill(offset, length) {
|
||||
if (offset + length > this._length) {
|
||||
throw new Error(`Expected ${offset + length - this._length} more bytes. The file might be corrupted. Unexpected end of file.`);
|
||||
}
|
||||
if (offset < this._offset || offset + length > this._offset + this._window) {
|
||||
const remainder = offset % this. _size;
|
||||
const last = this._last;
|
||||
if (this._chunk !== -1) {
|
||||
this._last = [this._chunk, this._buffer, this._view];
|
||||
}
|
||||
if (remainder + length > this._size) {
|
||||
const buffer = new Uint8Array(length);
|
||||
this._read(buffer, offset);
|
||||
this._chunk = -1;
|
||||
this._offset = offset;
|
||||
this._window = length;
|
||||
this._buffer = buffer;
|
||||
this._view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
||||
} else {
|
||||
const chunk = Math.floor(offset / this._size);
|
||||
this._offset = chunk * this._size;
|
||||
this._window = Math.min(this._length - this._offset, this._size);
|
||||
if (last && last[0] === chunk) {
|
||||
[this._chunk, this._buffer, this._view] = last;
|
||||
} else {
|
||||
this._chunk = chunk;
|
||||
this._stream.seek(this._offset);
|
||||
const buffer = this._stream.read(this._window);
|
||||
this._buffer = buffer;
|
||||
this._view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
||||
this._stream.seek(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return offset - this._offset;
|
||||
}
|
||||
|
||||
_read(buffer, offset) {
|
||||
const length = buffer.length;
|
||||
if (offset < this._offset || offset + length > this._offset + this._window) {
|
||||
this._stream.seek(offset);
|
||||
const data = this._stream.read(length);
|
||||
buffer.set(data, 0);
|
||||
this._stream.seek(0);
|
||||
} else {
|
||||
offset -= this._offset;
|
||||
const data = this._buffer.subarray(offset, offset + length);
|
||||
buffer.set(data, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
flatbuffers.TextReader = class {
|
||||
|
||||
static open(obj) {
|
||||
return new flatbuffers.TextReader(obj);
|
||||
}
|
||||
|
||||
constructor(obj) {
|
||||
this._root = obj;
|
||||
}
|
||||
|
||||
get root() {
|
||||
return this._root;
|
||||
}
|
||||
|
||||
int64(obj, defaultValue) {
|
||||
return obj === undefined ? defaultValue : BigInt(obj);
|
||||
}
|
||||
|
||||
uint64(obj, defaultValue) {
|
||||
return obj === undefined ? defaultValue : BigInt(obj);
|
||||
}
|
||||
|
||||
value(obj, defaultValue) {
|
||||
return obj === undefined ? defaultValue : obj;
|
||||
}
|
||||
|
||||
object(obj, type) {
|
||||
return obj === undefined ? obj : type.decodeText(this, obj);
|
||||
}
|
||||
|
||||
array(obj, type) {
|
||||
type = type || Array;
|
||||
if (Array.isArray(obj)) {
|
||||
const length = obj.length;
|
||||
const target = new type(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
target[i] = obj[i];
|
||||
}
|
||||
return target;
|
||||
}
|
||||
if (obj) {
|
||||
throw new flatbuffers.Error('Inalid value array.');
|
||||
}
|
||||
return new type(0);
|
||||
}
|
||||
|
||||
objects(obj, type) {
|
||||
if (Array.isArray(obj)) {
|
||||
const target = new Array(obj.length);
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
target[i] = type.decodeText(this, obj[i]);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
if (!obj) {
|
||||
return [];
|
||||
}
|
||||
throw new flatbuffers.Error('Inalid object array.');
|
||||
}
|
||||
};
|
||||
|
||||
flatbuffers.Error = class extends Error {
|
||||
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'FlatBuffers Error';
|
||||
this.message = message;
|
||||
}
|
||||
};
|
||||
|
||||
export const BinaryReader = flatbuffers.BinaryReader;
|
||||
export const TextReader = flatbuffers.TextReader;
|
||||
@@ -0,0 +1,209 @@
|
||||
|
||||
// Experimental
|
||||
|
||||
import * as python from './python.js';
|
||||
|
||||
const flax = {};
|
||||
|
||||
flax.ModelFactory = class {
|
||||
|
||||
async match(context) {
|
||||
const stream = context.stream;
|
||||
if (stream.length > 4) {
|
||||
const buffer = stream.peek(1);
|
||||
if (buffer[0] === 0xDE || buffer[0] === 0xDF || ((buffer[0] & 0x80) === 0x80)) {
|
||||
return context.set('flax.msgpack.map');
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async open(context) {
|
||||
const stream = context.stream;
|
||||
const packed = stream.peek();
|
||||
const execution = new python.Execution();
|
||||
const msgpack = execution.__import__('msgpack');
|
||||
const numpy = execution.__import__('numpy');
|
||||
// https://github.com/google/flax/blob/main/flax/serialization.py
|
||||
const ext_hook = (code, data) => {
|
||||
switch (code) {
|
||||
case 1: { // _MsgpackExtType.ndarray
|
||||
const tuple = msgpack.unpackb(data);
|
||||
const dtype = new numpy.dtype(tuple[1]);
|
||||
dtype.byteorder = '<';
|
||||
return new numpy.ndarray(tuple[0], dtype, tuple[2]);
|
||||
}
|
||||
default: {
|
||||
throw new flax.Error(`Unsupported MessagePack extension '${code}'.`);
|
||||
}
|
||||
}
|
||||
};
|
||||
const obj = msgpack.unpackb(packed, ext_hook);
|
||||
return new flax.Model(obj);
|
||||
}
|
||||
};
|
||||
|
||||
flax.Model = class {
|
||||
|
||||
constructor(obj) {
|
||||
this.format = 'Flax';
|
||||
this.modules = [new flax.Graph(obj)];
|
||||
}
|
||||
};
|
||||
|
||||
flax.Graph = class {
|
||||
|
||||
constructor(obj) {
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
const layers = new Map();
|
||||
const layer = (path) => {
|
||||
const name = path.join('.');
|
||||
if (!layers.has(name)) {
|
||||
layers.set(name, {});
|
||||
}
|
||||
return layers.get(name);
|
||||
};
|
||||
const flatten = (path, obj) => {
|
||||
for (const [name, value] of Object.entries(obj)) {
|
||||
if (flax.Utility.isTensor(value)) {
|
||||
const obj = layer(path);
|
||||
obj[name] = value;
|
||||
} else if (Array.isArray(value)) {
|
||||
const obj = layer(path);
|
||||
obj[name] = value;
|
||||
} else if (Object(value) === value) {
|
||||
flatten(path.concat(name), value);
|
||||
} else {
|
||||
const obj = layer(path);
|
||||
obj[name] = value;
|
||||
}
|
||||
}
|
||||
};
|
||||
if (Array.isArray(obj)) {
|
||||
layer([]).value = obj;
|
||||
} else {
|
||||
flatten([], obj);
|
||||
}
|
||||
this.nodes = Array.from(layers).map(([name, value]) => new flax.Node(name, value));
|
||||
}
|
||||
};
|
||||
|
||||
flax.Argument = class {
|
||||
|
||||
constructor(name, value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
};
|
||||
|
||||
flax.Value = class {
|
||||
|
||||
constructor(name, initializer = null) {
|
||||
if (typeof name !== 'string') {
|
||||
throw new flax.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
|
||||
}
|
||||
this.name = name;
|
||||
this.type = initializer ? initializer.type : null;
|
||||
this.initializer = initializer;
|
||||
}
|
||||
};
|
||||
|
||||
flax.Node = class {
|
||||
|
||||
constructor(name, layer) {
|
||||
this.name = name;
|
||||
this.type = { name: 'Module' };
|
||||
this.attributes = [];
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
for (const [name, value] of Object.entries(layer)) {
|
||||
if (flax.Utility.isTensor(value)) {
|
||||
const tensor = new flax.Tensor(value);
|
||||
const argument = new flax.Argument(name, [new flax.Value('', tensor)]);
|
||||
this.inputs.push(argument);
|
||||
} else if (Array.isArray(value)) {
|
||||
const attribute = new flax.Argument(name, value);
|
||||
this.attributes.push(attribute);
|
||||
} else {
|
||||
const attribute = new flax.Argument(name, value);
|
||||
this.attributes.push(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
flax.TensorType = class {
|
||||
|
||||
constructor(dataType, shape) {
|
||||
this.dataType = dataType || '?';
|
||||
this.shape = shape;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.dataType + this.shape.toString();
|
||||
}
|
||||
};
|
||||
|
||||
flax.TensorShape = class {
|
||||
|
||||
constructor(dimensions) {
|
||||
this.dimensions = dimensions;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return (Array.isArray(this.dimensions) && this.dimensions.length > 0) ?
|
||||
`[${this.dimensions.join(',')}]` : '';
|
||||
}
|
||||
};
|
||||
|
||||
flax.Tensor = class {
|
||||
|
||||
constructor(array) {
|
||||
this.type = new flax.TensorType(array.dtype.__name__, new flax.TensorShape(array.shape));
|
||||
const dataType = this.type.dataType;
|
||||
this.encoding = dataType === 'string' || dataType === 'object' ? '|' : array.dtype.byteorder;
|
||||
this._data = array.tobytes();
|
||||
this._itemsize = array.dtype.itemsize;
|
||||
}
|
||||
|
||||
get values() {
|
||||
switch (this.type.dataType) {
|
||||
case 'string': {
|
||||
if (this._data instanceof Uint8Array) {
|
||||
const data = this._data;
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
const size = this.type.shape.dimensions.reduce((a, b) => a * b, 1);
|
||||
this._data = new Array(size);
|
||||
let offset = 0;
|
||||
for (let i = 0; i < size; i++) {
|
||||
const buffer = data.subarray(offset, offset + this._itemsize);
|
||||
const index = buffer.indexOf(0);
|
||||
this._data[i] = decoder.decode(index >= 0 ? buffer.subarray(0, index) : buffer);
|
||||
offset += this._itemsize;
|
||||
}
|
||||
}
|
||||
return this._data;
|
||||
}
|
||||
default:
|
||||
return this._data;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
flax.Utility = class {
|
||||
|
||||
static isTensor(obj) {
|
||||
return obj && obj.__class__ && obj.__class__.__module__ === 'numpy' && obj.__class__.__name__ === 'ndarray';
|
||||
}
|
||||
};
|
||||
|
||||
flax.Error = class extends Error {
|
||||
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'Error loading Flax model.';
|
||||
}
|
||||
};
|
||||
|
||||
export const ModelFactory = flax.ModelFactory;
|
||||
@@ -0,0 +1,196 @@
|
||||
|
||||
const flexbuffers = {};
|
||||
|
||||
flexbuffers.BinaryReader = class {
|
||||
|
||||
static open(buffer) {
|
||||
const length = buffer.length;
|
||||
if (length >= 3) {
|
||||
const byteWidth = buffer[length - 1];
|
||||
if (byteWidth <= 8) {
|
||||
const packedType = buffer[length - 2];
|
||||
return new flexbuffers.BinaryReader(buffer, length - 2 - byteWidth, byteWidth, 1 << (packedType & 3), packedType >> 2);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
constructor(buffer, offset, parentWidth, byteWidth, type) {
|
||||
this._buffer = buffer;
|
||||
this._length = buffer.length;
|
||||
this._view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
||||
this._utf8Decoder = new TextDecoder('utf-8');
|
||||
this._root = new flexbuffers.Reference(this, offset, parentWidth, byteWidth, type);
|
||||
}
|
||||
|
||||
read() {
|
||||
return this._root.read();
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this._length;
|
||||
}
|
||||
|
||||
int(offset, size) {
|
||||
switch (size) {
|
||||
case 1: return this._view.getInt8(offset);
|
||||
case 2: return this._view.getInt16(offset, true);
|
||||
case 4: return this._view.getInt32(offset, true);
|
||||
case 8: return this._view.getBigInt64(offset, true);
|
||||
default: throw new flexbuffers.Error(`Invalid int size '${size}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
uint(offset, size) {
|
||||
switch (size) {
|
||||
case 1: return this._view.getUint8(offset);
|
||||
case 2: return this._view.getUint16(offset, true);
|
||||
case 4: return this._view.getUint32(offset, true);
|
||||
case 8: return this._view.getBigUint64(offset, true);
|
||||
default: throw new flexbuffers.Error(`Invalid uint size '${size}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
float(offset, size) {
|
||||
switch (size) {
|
||||
case 4: return this._view.getFloat32(offset, true);
|
||||
case 8: return this._view.getFloat64(offset, true);
|
||||
default: throw new flexbuffers.Error(`Invalid float size '${size}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
string(offset, size) {
|
||||
let end = size === undefined ? this._buffer.indexOf(0, offset) : offset + size;
|
||||
end = end === -1 ? this._buffer.length : end;
|
||||
const bytes = this._buffer.subarray(offset, end);
|
||||
return this._utf8Decoder.decode(bytes);
|
||||
}
|
||||
|
||||
bytes(offset, size) {
|
||||
return this._buffer.slice(offset, offset + size);
|
||||
}
|
||||
};
|
||||
|
||||
flexbuffers.Reference = class {
|
||||
|
||||
constructor(reader, offset, parentWidth, byteWidth, type) {
|
||||
this._reader = reader;
|
||||
this._offset = offset;
|
||||
this._parentWidth = parentWidth;
|
||||
this._byteWidth = byteWidth;
|
||||
this._type = type;
|
||||
}
|
||||
|
||||
read() {
|
||||
switch (this._type) {
|
||||
case 0x00: // null
|
||||
return null;
|
||||
case 0x01: // int
|
||||
return this._reader.int(this._offset, this._parentWidth);
|
||||
case 0x02: // uint
|
||||
return this._reader.uint(this._offset, this._parentWidth);
|
||||
case 0x03: // float
|
||||
return this._reader.float(this._offset, this._parentWidth);
|
||||
case 0x04: { // key
|
||||
return this._reader.string(this._indirect());
|
||||
}
|
||||
case 0x05: { // string
|
||||
const offset = this._indirect();
|
||||
const size = this._reader.uint(offset - this._byteWidth, this._byteWidth);
|
||||
return this._reader.string(offset, size);
|
||||
}
|
||||
case 0x06: // indirect int
|
||||
return this._reader.int(this._indirect(), this._byteWidth);
|
||||
case 0x07: // indirect uint
|
||||
return this._reader.uint(this._indirect(), this._byteWidth);
|
||||
case 0x08: // indirect float
|
||||
return this._reader.float(this._indirect(), this._byteWidth);
|
||||
case 0x09: { // map
|
||||
const offset = this._indirect();
|
||||
const keysOffset = offset - (this._byteWidth * 3);
|
||||
const keysVectorOffset = keysOffset - this._reader.uint(keysOffset, this._byteWidth);
|
||||
const keysByteWidth = this._reader.uint(keysOffset + this._byteWidth, this._byteWidth);
|
||||
const keys = this._typedVector(keysVectorOffset, keysByteWidth, 0x04);
|
||||
const values = this._vector(offset, this._byteWidth);
|
||||
const map = {};
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
map[keys[i]] = values[i];
|
||||
}
|
||||
return map;
|
||||
}
|
||||
case 0x0a: { // vector
|
||||
return this._vector(this._indirect(), this._byteWidth);
|
||||
}
|
||||
case 0x0b: // vector int
|
||||
case 0x0c: // vector uint
|
||||
case 0x0d: // vector float
|
||||
case 0x0e: // vector key
|
||||
case 0x0f: // vector string deprecated
|
||||
case 0x24: { // vector bool
|
||||
return this._typedVector(this._indirect(), this._byteWidth, this._type - 0x0b + 0x01);
|
||||
}
|
||||
case 0x10: // vector int2
|
||||
case 0x11: // vector uint2
|
||||
case 0x12: // vector float2
|
||||
case 0x13: // vector int3
|
||||
case 0x14: // vector uint3
|
||||
case 0x15: // vector float3
|
||||
case 0x16: // vector int4
|
||||
case 0x17: // vector uint4
|
||||
case 0x18: { // vector float4
|
||||
const offset = this._indirect();
|
||||
const size = (((this._type - 0x10) / 3) >> 0) + 2;
|
||||
const type = ((this._type - 0x10) % 3) + 0x01;
|
||||
return this._typedVector(offset, this._byteWidth, type, size);
|
||||
}
|
||||
case 0x19: { // blob
|
||||
const offset = this._indirect();
|
||||
const size = this._reader.uint(offset - this._byteWidth, this._byteWidth);
|
||||
return this._reader.bytes(offset, size);
|
||||
}
|
||||
case 0x1a: { // bool
|
||||
return this._reader.uint(this._offset, this._parentWidth) !== 0;
|
||||
}
|
||||
default: {
|
||||
throw new flexbuffers.Error(`Unsupported reference type '${this._type}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_indirect() {
|
||||
return this._offset - this._reader.uint(this._offset, this._parentWidth);
|
||||
}
|
||||
|
||||
_vector(offset, byteWidth) {
|
||||
const size = this._reader.uint(offset - byteWidth, byteWidth);
|
||||
const packedTypeOffset = offset + (size * byteWidth);
|
||||
const vector = new Array(size);
|
||||
for (let i = 0; i < size; i++) {
|
||||
const packedType = this._reader.uint(packedTypeOffset + i, 1);
|
||||
const reference = new flexbuffers.Reference(this._reader, offset + (i * byteWidth), byteWidth, 1 << (packedType & 3), packedType >> 2);
|
||||
vector[i] = reference.read();
|
||||
}
|
||||
return vector;
|
||||
}
|
||||
|
||||
_typedVector(offset, byteWidth, type, size) {
|
||||
size = size === undefined ? this._reader.uint(offset - byteWidth, byteWidth) : size;
|
||||
const vector = new Array(size);
|
||||
for (let i = 0; i < size; i++) {
|
||||
const reference = new flexbuffers.Reference(this._reader, offset + (i * byteWidth), byteWidth, 1, type);
|
||||
vector[i] = reference.read();
|
||||
}
|
||||
return vector;
|
||||
}
|
||||
};
|
||||
|
||||
flexbuffers.Error = class extends Error {
|
||||
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'FlexBuffers Error';
|
||||
this.message = message;
|
||||
}
|
||||
};
|
||||
|
||||
export const BinaryReader = flexbuffers.BinaryReader;
|
||||
@@ -0,0 +1,2 @@
|
||||
[
|
||||
]
|
||||
@@ -0,0 +1,72 @@
|
||||
|
||||
// Experimental
|
||||
|
||||
const flux = {};
|
||||
|
||||
flux.ModelFactory = class {
|
||||
|
||||
async match(context) {
|
||||
const identifier = context.identifier;
|
||||
const extension = identifier.lastIndexOf('.') > 0 ? identifier.split('.').pop().toLowerCase() : '';
|
||||
const stream = context.stream;
|
||||
if (stream && extension === 'bson') {
|
||||
return context.set('flux.bson');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async open(context) {
|
||||
let root = null;
|
||||
try {
|
||||
root = await context.read('bson');
|
||||
} catch (error) {
|
||||
const message = error && error.message ? error.message : error.toString();
|
||||
throw new flux.Error(`File format is not Flux BSON (${message.replace(/\.$/, '')}).`);
|
||||
}
|
||||
/* const metadata = */ context.metadata('flux-metadata.json');
|
||||
const backref = (obj, root) => {
|
||||
if (Array.isArray(obj)) {
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
obj[i] = backref(obj[i], root);
|
||||
}
|
||||
} else if (obj === Object(obj)) {
|
||||
if (obj.tag === 'backref' && obj.ref) {
|
||||
if (!root._backrefs[obj.ref - 1n]) {
|
||||
throw new flux.Error(`Invalid backref '${obj.ref}'.`);
|
||||
}
|
||||
obj = root._backrefs[obj.ref - 1n];
|
||||
}
|
||||
for (const key of Object.keys(obj)) {
|
||||
if (obj !== root || key !== '_backrefs') {
|
||||
obj[key] = backref(obj[key], root);
|
||||
}
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
const obj = backref(root, root);
|
||||
const model = obj.model;
|
||||
if (!model) {
|
||||
throw new flux.Error('File does not contain Flux model.');
|
||||
}
|
||||
throw new flux.Error("File contains unsupported Flux data.");
|
||||
}
|
||||
};
|
||||
|
||||
flux.Model = class {
|
||||
|
||||
constructor(/* root */) {
|
||||
this.format = 'Flux';
|
||||
this.modules = [];
|
||||
}
|
||||
};
|
||||
|
||||
flux.Error = class extends Error {
|
||||
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'Flux Error';
|
||||
}
|
||||
};
|
||||
|
||||
export const ModelFactory = flux.ModelFactory;
|
||||
@@ -0,0 +1,158 @@
|
||||
|
||||
.node path { stroke: #333; fill: none; stroke-width: 1px; }
|
||||
.node line { stroke: #333; fill: none; stroke-width: 1px; }
|
||||
|
||||
.node-item path { stroke-width: 0; stroke: #000; fill: #fff; }
|
||||
.node-item text { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif, "PingFang SC"; font-size: 11px; text-rendering: geometricPrecision; user-select: none; }
|
||||
|
||||
.node-item-function path { fill: #fff; }
|
||||
.node-item-function text { fill: #000; }
|
||||
.node-item-function:hover { cursor: pointer; }
|
||||
.node-item-function:hover path { fill: #eee; }
|
||||
|
||||
.node-item-type path { fill: #333; }
|
||||
.node-item-type text { fill: #fff; }
|
||||
.node-item-type:hover { cursor: pointer; }
|
||||
.node-item-type:hover path { fill: #fff; }
|
||||
.node-item-type:hover text { fill: #000; }
|
||||
|
||||
.node-item-type-constant path { fill: #eee; }
|
||||
.node-item-type-constant text { fill: #000; }
|
||||
.node-item-type-constant:hover path { fill: #fff; }
|
||||
|
||||
.node-item-type-control path { fill: #eee; }
|
||||
.node-item-type-control text { fill: #000; }
|
||||
.node-item-type-control:hover path { fill: #fff; }
|
||||
|
||||
.node-item-type-layer path { fill: rgb(51, 85, 136); }
|
||||
.node-item-type-activation path { fill: rgb(112, 41, 33); }
|
||||
.node-item-type-pool path { fill: rgb(51, 85, 51); }
|
||||
.node-item-type-normalization path { fill: rgb(51, 85, 68); }
|
||||
.node-item-type-dropout path { fill: rgb(69, 71, 112); }
|
||||
.node-item-type-shape path { fill: rgb(108, 79, 71); }
|
||||
.node-item-type-tensor path { fill: rgb(89, 66, 59); }
|
||||
.node-item-type-transform path { fill: rgb(51, 85, 68); }
|
||||
.node-item-type-data path { fill: rgb(85, 85, 85); }
|
||||
.node-item-type-quantization path { fill: rgb(80, 40, 0); }
|
||||
.node-item-type-attention path { fill: rgb(120, 60, 0); }
|
||||
|
||||
.node-item-input path { fill: #fff; }
|
||||
.node-item-input:hover { cursor: pointer; }
|
||||
.node-item-input:hover path { fill: #fff; }
|
||||
|
||||
.node-item-constant path { fill: #eee; }
|
||||
.node-item-constant:hover { cursor: pointer; }
|
||||
.node-item-constant:hover path { fill: #fff; }
|
||||
|
||||
.node-item-undefined path { fill: #f00; }
|
||||
.node-item-undefined:hover { cursor: pointer; }
|
||||
.node-item-undefined:hover path { fill: #fff; }
|
||||
|
||||
.node-argument-list > path { fill: #fff; stroke-width: 0; stroke: #000; }
|
||||
.node-argument-list:hover { cursor: pointer; }
|
||||
.node-argument-list:hover > path { fill: #f6f6f6; }
|
||||
.node-argument > text { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif, "PingFang SC"; font-size: 9px; font-weight: normal; text-rendering: geometricPrecision; user-select: none; }
|
||||
.node-argument > rect { fill: transparent; }
|
||||
|
||||
.graph-item-input path { fill: #eee; }
|
||||
.graph-item-input:hover { cursor: pointer; }
|
||||
.graph-item-input:hover path { fill: #fff; }
|
||||
|
||||
.graph-item-output path { fill: #eee; }
|
||||
.graph-item-output:hover { cursor: pointer; }
|
||||
.graph-item-output:hover path { fill: #fff; }
|
||||
|
||||
#arrowhead { fill: #000; }
|
||||
#arrowhead-hover { fill: rgba(220, 0, 0, 0.9); }
|
||||
#arrowhead-select { fill: rgba(220, 0, 0, 0.9); }
|
||||
|
||||
.edge-paths { pointer-events: none; }
|
||||
.edge-path { stroke: #000; stroke-width: 1px; fill: none; marker-end: url("#arrowhead"); }
|
||||
.edge-paths-hit-test { pointer-events: stroke; stroke-width: 0.5em; fill: none; stroke: #000; stroke-opacity: 0.001; }
|
||||
|
||||
.select > .node.node-border { stroke: rgba(220, 0, 0, 0.9); stroke-width: 2px; }
|
||||
.select.edge-path { stroke: rgba(220, 0, 0, 0.9); stroke-width: 1px; marker-end: url("#arrowhead-select"); }
|
||||
.select.node-argument > rect { fill: transparent; stroke: rgba(220, 0, 0, 0.9); }
|
||||
|
||||
.edge-label { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif, "PingFang SC"; font-size: 10px; }
|
||||
.edge-path-control-dependency { stroke-dasharray: 3, 2; }
|
||||
.edge-path-tunnel { stroke-dasharray: 5, 3; marker-end: url("#arrowhead-tunnel"); opacity: 0.5; }
|
||||
#arrowhead-tunnel { fill: #000; }
|
||||
|
||||
.cluster rect { stroke: #000; fill: #000; fill-opacity: 0.02; stroke-opacity: 0.06; stroke-width: 1px; }
|
||||
|
||||
.node-block > .node-block-background { fill: #fff; stroke: none; stroke-width: 0; }
|
||||
.node-block .edge-path { stroke: #000; stroke-width: 1px; fill: none; }
|
||||
.node-block .select.edge-path { stroke: rgba(220, 0, 0, 0.9); marker-end: url("#arrowhead-select"); }
|
||||
|
||||
@keyframes pulse { from { stroke-dashoffset: 100px; } to { stroke-dashoffset: 0; } }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
.edge-path { stroke: #888; }
|
||||
|
||||
.node path { stroke: #242424; }
|
||||
.node line { stroke: #242424; }
|
||||
|
||||
.select > .node.node-border { stroke: rgba(192, 0, 0, 0.8); }
|
||||
.select.edge-path { stroke: rgba(192, 0, 0, 0.8); }
|
||||
.select.node-argument > rect { fill: transparent; stroke: rgba(192, 0, 0, 0.8); }
|
||||
#arrowhead { fill: #888; }
|
||||
#arrowhead-hover { fill: rgba(192, 0, 0, 0.8); }
|
||||
#arrowhead-select { fill: rgba(192, 0, 0, 0.8); }
|
||||
|
||||
.edge-label { fill: #b2b2b2; }
|
||||
|
||||
.node-item-function path { fill: #404040; }
|
||||
.node-item-function text { fill: #dfdfdfdf; }
|
||||
.node-item-function:hover { cursor: pointer; }
|
||||
.node-item-function:hover path { fill: #666666; }
|
||||
|
||||
.node-item-type path { fill: #303030; }
|
||||
.node-item-type text { fill: #dfdfdf; }
|
||||
.node-item-type:hover { cursor: pointer; }
|
||||
.node-item-type:hover path { fill: #808080; }
|
||||
.node-item-type:hover text { fill: #dfdfdf; }
|
||||
|
||||
.node-item path { stroke: #fff; }
|
||||
.node-item text { fill: #dfdfdf; }
|
||||
|
||||
.node-argument > text { fill: #b2b2b2; }
|
||||
.node-argument-list > path { fill: #2d2d2d; }
|
||||
.node-argument-list:hover > path { fill: #303030; }
|
||||
|
||||
.graph-item-input path { fill: #404040; }
|
||||
.graph-item-input:hover { cursor: pointer; }
|
||||
.graph-item-input:hover path { fill: #666666; }
|
||||
|
||||
.graph-item-output path { fill: #404040; }
|
||||
.graph-item-output:hover { cursor: pointer; }
|
||||
.graph-item-output:hover path { fill: #666666; }
|
||||
|
||||
.node-item-input path { fill: #404040; }
|
||||
.node-item-input:hover path { fill: #666666; }
|
||||
.node-item-constant path { fill: #4b4b4b; }
|
||||
.node-item-constant:hover path { fill: #666666; }
|
||||
|
||||
.node-item-type-layer path { fill: rgba(51, 85, 136, 0.7); }
|
||||
.node-item-type-activation path { fill: rgba(75, 27, 22, 0.7); }
|
||||
.node-item-type-activation path { fill: rgba(75, 27, 22, 0.7); }
|
||||
.node-item-type-pool path { fill: rgba(51, 85, 51, 0.7); }
|
||||
.node-item-type-pool path { fill: rgba(51, 85, 51, 0.7); }
|
||||
.node-item-type-normalization path { fill: rgba(51, 85, 68, 0.7); }
|
||||
.node-item-type-dropout path { fill: rgba(69, 71, 112, 0.7); }
|
||||
.node-item-type-shape path { fill: rgba(108, 79, 71, 0.7); }
|
||||
.node-item-type-tensor path { fill: rgba(89, 66, 59, 0.7); }
|
||||
.node-item-type-transform path { fill: rgba(51, 85, 68, 0.7); }
|
||||
.node-item-type-data path { fill: rgba(85, 85, 85, 0.7); }
|
||||
.node-item-type-quantization path { fill: rgb(80, 40, 0, 0.7); }
|
||||
.node-item-type-attention path { fill: rgb(100, 50, 0, 0.7); }
|
||||
.node-item-type-custom path { fill: rgb(64, 64, 64, 0.7); }
|
||||
|
||||
.node-block > .node-block-background { fill: #404040; stroke: none; }
|
||||
.node-block .edge-path { stroke: #888; }
|
||||
.node-block .select.edge-path { stroke: rgba(192, 0, 0, 0.8); marker-end: url("#arrowhead-select"); }
|
||||
|
||||
.edge-path-tunnel { stroke: #888; opacity: 0.5; }
|
||||
#arrowhead-tunnel { fill: #888; }
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
// Experimental
|
||||
|
||||
const hailo = {};
|
||||
|
||||
hailo.ModelFactory = class {
|
||||
|
||||
async match(context) {
|
||||
const container = await hailo.Container.open(context);
|
||||
if (container) {
|
||||
return context.set(container.type, container);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
filter(context, match) {
|
||||
if (context.type === 'hailo.metadata' && (match.type === 'hailo.configuration' || match.type === 'npz' || match.type === 'onnx.proto')) {
|
||||
return false;
|
||||
}
|
||||
if (context.type === 'hailo.configuration' && match.type === 'npz') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async open(context) {
|
||||
const metadata = await context.metadata('hailo-metadata.json');
|
||||
const target = context.value;
|
||||
await target.read();
|
||||
return new hailo.Model(metadata, target);
|
||||
}
|
||||
};
|
||||
|
||||
hailo.Model = class {
|
||||
|
||||
constructor(metadata, container) {
|
||||
const configuration = container.configuration;
|
||||
this.modules = [new hailo.Graph(metadata, configuration, container.weights)];
|
||||
this.name = configuration && configuration.name || "";
|
||||
this.format = container.format + (container.metadata && container.metadata.sdk_version ? ` v${container.metadata.sdk_version}` : '');
|
||||
this.metadata = [];
|
||||
if (container.metadata && container.metadata.state) {
|
||||
this.metadata.push(new hailo.Argument('state', container.metadata.state));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
hailo.Graph = class {
|
||||
|
||||
constructor(metadata, configuration, weights) {
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.nodes = [];
|
||||
const values = new Map();
|
||||
values.map = (name, type, tensor) => {
|
||||
if (name.length === 0 && tensor) {
|
||||
return new hailo.Value(name, type || null, tensor);
|
||||
}
|
||||
if (!values.has(name)) {
|
||||
values.set(name, new hailo.Value(name, type || null, tensor || null));
|
||||
} else if (tensor) {
|
||||
throw new hailo.Error(`Duplicate value '${name}'.`);
|
||||
} else if (type && !type.equals(values.get(name).type)) {
|
||||
throw new hailo.Error(`Duplicate value '${name}'.`);
|
||||
}
|
||||
return values.get(name);
|
||||
};
|
||||
const layers = Object.entries(configuration.layers || {}).map(([name, value]) => {
|
||||
value.name = name;
|
||||
return value;
|
||||
});
|
||||
const inputs = new Set();
|
||||
for (const layer of layers) {
|
||||
switch (layer.type) {
|
||||
case 'input_layer': {
|
||||
for (let i = 0; i < layer.output.length; i++) {
|
||||
const shape = Array.isArray(layer.output_shapes) && layer.output_shapes.length > 0 ? layer.output_shapes[0] : null;
|
||||
const type = shape ? new hailo.TensorType('?', new hailo.TensorShape(shape)) : null;
|
||||
const output = layer.output[i];
|
||||
if (!inputs.has(output)) {
|
||||
const name = `${layer.name}\n${output}`;
|
||||
const argument = new hailo.Argument('input', [values.map(name, type)]);
|
||||
this.inputs.push(argument);
|
||||
inputs.add(output);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'output_layer': {
|
||||
for (let i = 0; i < layer.input.length; i++) {
|
||||
const shape = Array.isArray(layer.input_shapes) && layer.input_shapes.length > 0 ? layer.input_shapes[i] : null;
|
||||
const type = shape ? new hailo.TensorType('?', new hailo.TensorShape(shape)) : null;
|
||||
const input = layer.input[i];
|
||||
const name = `${input}\n${layer.name}`;
|
||||
const argument = new hailo.Argument('output', [values.map(name, type)]);
|
||||
this.outputs.push(argument);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const node = new hailo.Node(metadata, layer, values, weights.get(layer.name));
|
||||
this.nodes.push(node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
hailo.Argument = class {
|
||||
|
||||
constructor(name, value, type = null, visible = true) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
this.visible = visible;
|
||||
}
|
||||
};
|
||||
|
||||
hailo.Value = class {
|
||||
|
||||
constructor(name, type, initializer) {
|
||||
if (typeof name !== 'string') {
|
||||
throw new hailo.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
|
||||
}
|
||||
this.name = name;
|
||||
this.type = initializer ? initializer.type : type;
|
||||
this.initializer = initializer;
|
||||
}
|
||||
};
|
||||
|
||||
hailo.Node = class {
|
||||
|
||||
constructor(metadata, layer, values, weights) {
|
||||
weights = weights || new Map();
|
||||
this.name = layer.name || '';
|
||||
this.type = metadata.type(layer.type);
|
||||
if (layer.type === 'activation') {
|
||||
const name = layer.params.activation || layer.name || '';
|
||||
this.type = { ...this.type, name };
|
||||
}
|
||||
this.inputs = layer.input.map((name, index) => {
|
||||
const shape = layer.input_shapes ? layer.input_shapes[index] : null;
|
||||
const type = shape ? new hailo.TensorType('?', new hailo.TensorShape(shape)) : null;
|
||||
name = `${name}\n${layer.name}`;
|
||||
return new hailo.Argument("input", [values.map(name, type, null)]);
|
||||
});
|
||||
const layer_params = layer.params ? Object.entries(layer.params) : [];
|
||||
const params_list = layer_params.reduce((acc, [name, value]) => {
|
||||
const schema = metadata.attribute(layer.type, name) || {};
|
||||
if (schema.visible) {
|
||||
const label = schema.label ? schema.label : name;
|
||||
if (!weights.has(label)) {
|
||||
const array = weights.get(label);
|
||||
const tensor = new hailo.Tensor(array, value);
|
||||
acc.push(new hailo.Argument(label, [values.map('', tensor.type, tensor)]));
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
const params_from_npz = Array.from(weights).filter(([, value]) => value).map(([name, value]) => {
|
||||
const tensor = new hailo.Tensor(value);
|
||||
return new hailo.Argument(name, [values.map('', tensor.type, tensor)]);
|
||||
});
|
||||
this.inputs = this.inputs.concat(params_list).concat(params_from_npz);
|
||||
this.outputs = (layer.output || []).map((name, index) => {
|
||||
const shape = layer.output_shapes ? layer.output_shapes[index] : null;
|
||||
const type = shape ? new hailo.TensorType('?', new hailo.TensorShape(shape)) : null;
|
||||
name = `${layer.name}\n${name}`;
|
||||
return new hailo.Argument("output", [values.map(name, type, null)]);
|
||||
});
|
||||
this.attributes = [];
|
||||
const attrs = Object.assign(layer.params || {}, { original_names: layer.original_names || [] });
|
||||
for (const [name, value] of Object.entries(attrs)) {
|
||||
const schema = metadata.attribute(layer.type, name);
|
||||
const type = schema && schema.type ? schema.type : '';
|
||||
const visible = name === 'original_names' || (schema && schema.visible === false) ? false : true;
|
||||
const attribute = new hailo.Argument(name, value, type, visible);
|
||||
this.attributes.push(attribute);
|
||||
}
|
||||
this.chain = [];
|
||||
if (layer && layer.params && layer.params.activation && layer.params.activation !== 'linear' && layer.type !== 'activation') {
|
||||
const activation = {
|
||||
type: layer.params.activation,
|
||||
name: layer.params.activation,
|
||||
input: [],
|
||||
output: []
|
||||
};
|
||||
const node = new hailo.Node(metadata, activation, values.map);
|
||||
this.chain.push(node);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
hailo.Tensor = class {
|
||||
|
||||
constructor(array, shape) {
|
||||
const dataType = array && array.dtype ? array.dtype.__name__ : '?';
|
||||
shape = array && array.shape ? array.shape : shape;
|
||||
this.type = new hailo.TensorType(dataType, new hailo.TensorShape(shape));
|
||||
if (array) {
|
||||
this.stride = array.strides.map((stride) => stride / array.itemsize);
|
||||
this.layout = this.type.dataType === 'string' || this.type.dataType === 'object' ? '|' : array.dtype.byteorder;
|
||||
this.values = this.type.dataType === 'string' || this.type.dataType === 'object' || this.type.dataType === 'void' ? array.tolist() : array.tobytes();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
hailo.TensorType = class {
|
||||
|
||||
constructor(dataType, shape) {
|
||||
this.dataType = dataType;
|
||||
this.shape = shape;
|
||||
}
|
||||
|
||||
equals(obj) {
|
||||
return obj && this.dataType === obj.dataType && this.shape && this.shape.equals(obj.shape);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return (this.dataType || '?') + this.shape.toString();
|
||||
}
|
||||
};
|
||||
|
||||
hailo.TensorShape = class {
|
||||
|
||||
constructor(dimensions) {
|
||||
this.dimensions = dimensions;
|
||||
}
|
||||
|
||||
equals(obj) {
|
||||
if (obj && Array.isArray(obj.dimensions) && Array.isArray(this.dimensions)) {
|
||||
if (this.dimensions.length === obj.dimensions.length) {
|
||||
return obj.dimensions.every((value, index) => this.dimensions[index] === value);
|
||||
}
|
||||
const a = this.dimensions.filter((value, index) => index === 0 || index === this.dimensions.length - 1 || value !== 1);
|
||||
const b = obj.dimensions.filter((value, index) => index === 0 || index === obj.dimensions.length - 1 || value !== 1);
|
||||
if (a.length === b.length) {
|
||||
return a.every((value, index) => b[index] === value);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
toString() {
|
||||
if (this.dimensions && this.dimensions.length > 0) {
|
||||
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
hailo.Container = class {
|
||||
|
||||
static async open(context) {
|
||||
const identifier = context.identifier;
|
||||
const basename = identifier.split('.');
|
||||
basename.pop();
|
||||
if (identifier.toLowerCase().endsWith('.hn')) {
|
||||
if (basename.length > 1 && (basename[basename.length - 1] === 'native' || basename[basename.length - 1] === 'fp')) {
|
||||
basename.pop();
|
||||
}
|
||||
const configuration = await context.peek('json');
|
||||
if (configuration && configuration.name && configuration.net_params && configuration.layers) {
|
||||
return new hailo.Container(context, 'hailo.configuration', basename.join('.'), configuration, null);
|
||||
}
|
||||
} else if (identifier.toLowerCase().endsWith('.metadata.json')) {
|
||||
basename.pop();
|
||||
const metadata = await context.peek('json');
|
||||
if (metadata && metadata.state && metadata.hn) {
|
||||
return new hailo.Container(context, 'hailo.metadata', basename.join('.'), null, metadata);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
constructor(context, type, basename, configuration, metadata) {
|
||||
this.type = type;
|
||||
this.context = context;
|
||||
this.basename = basename;
|
||||
this.configuration = configuration;
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
async _request(name, type) {
|
||||
try {
|
||||
const content = await this.context.fetch(name);
|
||||
if (content) {
|
||||
return await content.read(type);
|
||||
}
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async read() {
|
||||
this.format = 'Hailo NN';
|
||||
this.weights = new Map();
|
||||
if (!this.metadata) {
|
||||
this.metadata = await this._request(`${this.basename}.metadata.json`, 'json');
|
||||
}
|
||||
if (this.metadata) {
|
||||
this.format = 'Hailo Archive';
|
||||
this.configuration = await this._request(this.metadata.hn, 'json');
|
||||
if (!this.configuration) {
|
||||
throw new hailo.Error("Archive does not contain '.nn' configuration.");
|
||||
}
|
||||
let extension = '';
|
||||
switch (this.metadata.state) {
|
||||
case 'fp_optimized_model': extension = '.fpo.npz'; break;
|
||||
case 'quantized_model': extension = '.q.npz'; break;
|
||||
case 'compiled_model': extension = '.q.npz'; break;
|
||||
default: extension = '.npz'; break;
|
||||
}
|
||||
const entries = await this._request(this.basename + extension, 'npz');
|
||||
if (entries && entries.size > 0) {
|
||||
const inputs = new Set([
|
||||
'kernel', 'bias',
|
||||
'input_activation_bits', 'output_activation_bits',
|
||||
'weight_bits', 'bias_decomposition'
|
||||
]);
|
||||
for (const [name, value] of entries) {
|
||||
const key = name.split('.').slice(0, -1).join('.');
|
||||
const match = key.match(/.*?(?=:[0-9])/);
|
||||
if (match) {
|
||||
const path = match[0].split('/');
|
||||
if (inputs.has(path[2])) {
|
||||
const layer = `${path[0]}/${path[1]}`;
|
||||
if (!this.weights.has(layer)) {
|
||||
this.weights.set(layer, new Map());
|
||||
}
|
||||
const weights = this.weights.get(layer);
|
||||
weights.set(path[2], value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete this.context;
|
||||
delete this.basename;
|
||||
}
|
||||
};
|
||||
|
||||
hailo.Error = class extends Error {
|
||||
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'Error loading Hailo model.';
|
||||
}
|
||||
};
|
||||
|
||||
export const ModelFactory = hailo.ModelFactory;
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
|
||||
const hickle = {};
|
||||
|
||||
hickle.ModelFactory = class {
|
||||
|
||||
async match(context) {
|
||||
const group = await context.peek('hdf5');
|
||||
if (group && group.attributes && group.attributes.get('CLASS') === 'hickle') {
|
||||
return context.set('hickle', group);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async open(context) {
|
||||
return new hickle.Model(context.value);
|
||||
}
|
||||
};
|
||||
|
||||
hickle.Model = class {
|
||||
|
||||
constructor(group) {
|
||||
this.format = 'Hickle Weights';
|
||||
this.modules = [new hickle.Graph(group)];
|
||||
}
|
||||
};
|
||||
|
||||
hickle.Graph = class {
|
||||
|
||||
constructor(group) {
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
const deserialize = (group) => {
|
||||
if (group && group.attributes.has('type')) {
|
||||
const type = group.attributes.get('type');
|
||||
if (Array.isArray(type) && type.length && typeof type[0] === 'string') {
|
||||
switch (type[0]) {
|
||||
case 'hickle':
|
||||
case 'dict_item': {
|
||||
if (group.groups.size === 1) {
|
||||
return deserialize(group.groups.values().next().value);
|
||||
}
|
||||
throw new hickle.Error(`Invalid Hickle type value '${type[0]}'.`);
|
||||
}
|
||||
case 'dict': {
|
||||
const dict = new Map();
|
||||
for (const [name, obj] of group.groups) {
|
||||
const value = deserialize(obj);
|
||||
dict.set(name, value);
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
case 'ndarray': {
|
||||
return group.value;
|
||||
}
|
||||
default: {
|
||||
throw new hickle.Error(`Unsupported Hickle type '${type[0]}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new hickle.Error(`Unsupported Hickle type '${JSON.stringify(type)}'`);
|
||||
}
|
||||
throw new hickle.Error('Unsupported Hickle group.');
|
||||
};
|
||||
const obj = deserialize(group);
|
||||
const layers = new Map();
|
||||
if (obj && obj instanceof Map && Array.from(obj.values()).every((value) => value.type && value.shape)) {
|
||||
for (const [key, value] of obj) {
|
||||
const tensor = new hickle.Tensor(key, value.shape, value.type, value.littleEndian, value.type === 'string' ? value.value : value.data);
|
||||
const bits = key.split('.');
|
||||
const parameter = bits.pop();
|
||||
const layer = bits.join('.');
|
||||
if (!layers.has(layer)) {
|
||||
layers.set(layer, []);
|
||||
}
|
||||
layers.get(layer).push({ name: parameter, value: tensor });
|
||||
}
|
||||
}
|
||||
this.nodes = Array.from(layers).map(([name, value]) => new hickle.Node(name, value));
|
||||
}
|
||||
};
|
||||
|
||||
hickle.Argument = class {
|
||||
|
||||
constructor(name, value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
};
|
||||
|
||||
hickle.Value = class {
|
||||
|
||||
constructor(name, type, initializer = null) {
|
||||
if (typeof name !== 'string') {
|
||||
throw new hickle.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
|
||||
}
|
||||
this.name = name;
|
||||
this.type = !type && initializer ? initializer.type : type;
|
||||
this.initializer = initializer;
|
||||
}
|
||||
};
|
||||
|
||||
hickle.Node = class {
|
||||
|
||||
constructor(name, parameters) {
|
||||
this.type = { name: 'Weights' };
|
||||
this.name = name;
|
||||
this.inputs = parameters.map((parameter) => {
|
||||
return new hickle.Argument(parameter.name, [
|
||||
new hickle.Value(parameter.value.name, null, parameter.value)
|
||||
]);
|
||||
});
|
||||
this.outputs = [];
|
||||
this.attributes = [];
|
||||
}
|
||||
};
|
||||
|
||||
hickle.Tensor = class {
|
||||
|
||||
constructor(name, shape, type, littleEndian, data) {
|
||||
this.name = name;
|
||||
this.type = new hickle.TensorType(type, new hickle.TensorShape(shape));
|
||||
this.encoding = littleEndian ? '<' : '>';
|
||||
this._data = data;
|
||||
}
|
||||
|
||||
get values() {
|
||||
if (Array.isArray(this._data) || this._data === null) {
|
||||
return null;
|
||||
}
|
||||
if (this._data instanceof Uint8Array) {
|
||||
return this._data;
|
||||
}
|
||||
return this._data.peek();
|
||||
}
|
||||
};
|
||||
|
||||
hickle.TensorType = class {
|
||||
|
||||
constructor(dataType, shape) {
|
||||
this.dataType = dataType;
|
||||
this.shape = shape;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.dataType + this.shape.toString();
|
||||
}
|
||||
};
|
||||
|
||||
hickle.TensorShape = class {
|
||||
|
||||
constructor(dimensions) {
|
||||
this.dimensions = dimensions;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.dimensions ? (`[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`) : '';
|
||||
}
|
||||
};
|
||||
|
||||
hickle.Error = class extends Error {
|
||||
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'Error loading Hickle model.';
|
||||
}
|
||||
};
|
||||
|
||||
export const ModelFactory = hickle.ModelFactory;
|
||||
|
After Width: | Height: | Size: 57 KiB |
@@ -0,0 +1,46 @@
|
||||
|
||||
const imgdnn = {};
|
||||
|
||||
imgdnn.ModelFactory = class {
|
||||
|
||||
async match(context) {
|
||||
const stream = context.stream;
|
||||
const signature = [0x49, 0x4d, 0x47, 0x44, 0x4e, 0x4e]; // IMGDNN
|
||||
if (stream && stream.length >= signature.length && stream.peek(6).every((value, index) => value === signature[index])) {
|
||||
return 'imgdnn';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
open(/* context */) {
|
||||
throw new imgdnn.Error('Invalid file content. File contains undocumented IMGDNN data.');
|
||||
}
|
||||
};
|
||||
|
||||
imgdnn.Model = class {
|
||||
|
||||
constructor(metadata, model) {
|
||||
this.format = 'IMGDNN';
|
||||
this.modules = [new imgdnn.Graph(metadata, model)];
|
||||
}
|
||||
};
|
||||
|
||||
imgdnn.Graph = class {
|
||||
|
||||
constructor(/* metadata, model */) {
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.nodes = [];
|
||||
}
|
||||
};
|
||||
|
||||
imgdnn.Error = class extends Error {
|
||||
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'Error loading IMGDNN model.';
|
||||
}
|
||||
};
|
||||
|
||||
export const ModelFactory = imgdnn.ModelFactory;
|
||||
|
||||
@@ -0,0 +1,600 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="description" content="Visualizer for neural network, deep learning and machine learning models." />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; frame-src 'none'">
|
||||
<meta name="version" content="0.0.0">
|
||||
<meta name="date" content="">
|
||||
<title>Netron</title>
|
||||
<link rel="stylesheet" type="text/css" href="grapher.css">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
|
||||
<link rel="icon" type="image/png" href="icon.png">
|
||||
<link rel="apple-touch-icon" type="image/png" href="icon.png">
|
||||
<link rel="apple-touch-icon-precomposed" type="image/png" href="icon.png">
|
||||
<link rel="fluid-icon" type="image/png" href="icon.png">
|
||||
<script type="text/javascript" src="index.js"></script>
|
||||
<style>
|
||||
html { touch-action: none; overflow: hidden; width: 100%; height: 100%; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; text-rendering: optimizeLegibility; -webkit-text-rendering: optimizeLegibility; -moz-text-rendering: optimizeLegibility; -ms-text-rendering: optimizeLegibility; -o-text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-font-smoothing: antialiased; -ms-font-smoothing: antialiased; -o-font-smoothing: antialiased; }
|
||||
body { touch-action: none; overflow: hidden; width: 100%; height: 100%; margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif, "PingFang SC"; font-size: 12px; text-rendering: geometricPrecision; }
|
||||
button { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif, "PingFang SC"; }
|
||||
.center { position: absolute; margin: auto; top: 0; right: 0; bottom: 0; left: 0; user-select: none; -webkit-user-select: none; -moz-user-select: none; }
|
||||
.select { user-select: text; -webkit-user-select: text; -moz-user-select: text; }
|
||||
.target { display: flex; height: 100%; width: 100%; overflow: auto; outline: none; touch-action: pan-x pan-y; }
|
||||
.canvas { margin: auto; flex-shrink: 0; text-rendering: geometricPrecision; user-select: none; -webkit-user-select: none; -moz-user-select: none; }
|
||||
.open-file-dialog { display: none; }
|
||||
.default { background-color: #ffffff; }
|
||||
.default .logo { display: none; }
|
||||
.default .target { display: flex; opacity: 1; }
|
||||
.default .toolbar { display: flex; }
|
||||
.toolbar { display: flex; align-items: center; position: absolute; bottom: 10px; left: 10px; padding: 0; margin: 0; user-select: none; -webkit-user-select: none; -moz-user-select: none; }
|
||||
.toolbar button:focus { outline: 0; }
|
||||
.toolbar-button { background: None; border-radius: 6px; border: 0; margin: 0; margin-right: 1px; padding: 0; fill: None; stroke: #777; cursor: pointer; width: 32px; height: 32px; user-select: none; }
|
||||
.toolbar-path { display: flex; align-items: center; }
|
||||
.toolbar-select { background: transparent; position: relative; width: 170px; margin: 4px 4px 4px 4px; }
|
||||
.toolbar-select select { width: 100%; appearance: none; -webkit-appearance: none; -moz-appearance: none; font-family: inherit; font-size: 12px; line-height: 16px; border: 1px solid; padding: 3px 18px 3px 10px; border-radius: 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; box-sizing: border-box; }
|
||||
.toolbar-select select { background: #777; border-color: #777; color: #fff; }
|
||||
.toolbar-select-arrow { position: absolute; top: 50%; right: 8px; transform: translateY(-50%); pointer-events: none; }
|
||||
.toolbar-select-arrow { fill: #ffffff; }
|
||||
.toolbar-select select:hover { background: #000000; border-color: #000000; }
|
||||
.toolbar-select select:focus { outline: 0; }
|
||||
.toolbar-path-back-button { background: #777; border-top-left-radius: 6px; border-bottom-left-radius: 6px; border: 1px solid; border-color: #777; margin: 4px 0px 4px 4px; padding: 5px 8px 5px 8px; cursor: pointer; color: #ffffff; font-size: 12px; line-height: 12px; transition: 0.1s; }
|
||||
.toolbar-path-back-button:hover { background: #000000; border-color: #000000; }
|
||||
.toolbar-path-name-button { background: #777; border: 0px; border-color: #777; color: #ffffff; border-left: 1px; border-left-color: #ffffff; margin: 4px 0 4px 1px; padding: 6px 8px 6px 8px; cursor: pointer; width: auto; font-size: 12px; line-height: 12px; transition: 0.1s; }
|
||||
.toolbar-path-name-button:hover { background: #000000; border-color: #000000; }
|
||||
.toolbar-path-name-button:last-child { border-top-right-radius: 6px; border-bottom-right-radius: 6px; }
|
||||
.toolbar-icon .border { stroke: #fff; }
|
||||
.toolbar-icon .stroke { stroke: #808080; }
|
||||
.toolbar-icon .fill { fill: #808080; }
|
||||
.toolbar-icon:hover .stroke { stroke: #000000; }
|
||||
.toolbar-icon:hover .fill { fill: #000000; }
|
||||
.message { display: none; opacity: 0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; flex-direction: column; justify-content: flex-start; }
|
||||
.message-text { display: inline; text-align: center; width: 562px; font-size: 13px; line-height: 20px; margin-top: 50vh; padding-top: 56px; padding-bottom: 20px; margin-left: auto; margin-right: auto; user-select: text; -webkit-user-select: text; -moz-user-select: text; }
|
||||
.welcome .message-text a { text-decoration: none; color: #666666; }
|
||||
.welcome .message-text a:visited { color: inherit; }
|
||||
.welcome .message-text a:hover { color: #242424; text-decoration: underline; }
|
||||
.message-button { display: inline; text-align: center; width: 125px; margin-left: auto; margin-right: auto; }
|
||||
.logo-text { top: -57px; width: 582px; transition: 0.1s; }
|
||||
.logo-name { top: -170px; width: 582px; transition: 0.1s; }
|
||||
.logo-icon { left: 248px; top: -18px; width: 106px; height: 106px; transition: 0.1s; }
|
||||
.logo-spinner { left: 248px; top: -18px; width: 106px; height: 106px; display: none; }
|
||||
.logo-stroke { stroke: #444444; }
|
||||
.logo-fill { fill: #444444; }
|
||||
.logo-border { stroke: #555555; }
|
||||
.logo-glyph { fill: #444444; }
|
||||
.logo-button { font-size: 12px; font-weight: bold; line-height: 1.25; text-align: center; vertical-align: middle; min-width: 5em; height: 2.7em; border-radius: 1.3em; transition: 0.1s; user-select: none; -webkit-user-select: none; -moz-user-select: none; color: #444444; background-color: #ececec; border: 1px solid #444444; }
|
||||
.logo-button:hover { color: #ececec; background-color: #444444; cursor: pointer; transition: 0.2s; }
|
||||
.logo-button:focus { outline: 0; }
|
||||
.logo-message { display: none; height: 0px; }
|
||||
.logo-github { display: none; }
|
||||
.open-file-button { top: 170px; left: 0px; width: 10.5em; }
|
||||
.progress { top: 120px; height: 2px; width: 400px; }
|
||||
.progress-bar { height: 100%; width: 0%; background-color: #444444; }
|
||||
.notification .logo-name { display: none; }
|
||||
.notification .open-file-button { display: none; }
|
||||
.notification .progress { display: none; }
|
||||
.welcome body { background-color: #ececec; }
|
||||
.welcome { background-color: #ececec; color: #242424; }
|
||||
.welcome .message-text { display: none; opacity: 0; }
|
||||
.welcome .message-button { display: none; opacity: 0; }
|
||||
.welcome .target { display: none; opacity: 0; }
|
||||
.welcome .menu { background-color: #ffffff; }
|
||||
.welcome.spinner .logo-spinner { display: block; -webkit-animation: orbit 0.5s infinite linear; animation: orbit 0.5s infinite linear; cursor: wait; }
|
||||
.welcome.spinner .menu-button { display: none; }
|
||||
.welcome.notification .menu-button { display: none; }
|
||||
.notification body { background-color: #ececec; }
|
||||
.notification .message { display: flex; opacity: 1; }
|
||||
.notification .message-text { display: inline; opacity: 1; }
|
||||
.notification .message-button { display: inline; opacity: 1; }
|
||||
.alert { background-color: #ececec; color: #242424; }
|
||||
.alert .target { display: none; opacity: 0; }
|
||||
.alert .toolbar { display: none; opacity: 0; }
|
||||
.alert .menu { display: none; opacity: 0; }
|
||||
.alert .logo { display: none; opacity: 0; }
|
||||
.alert .message { display: flex; opacity: 1; }
|
||||
.alert .message-text { display: inline; opacity: 1; width: 50%; padding-top: 0px; }
|
||||
.alert .message-button { display: inline; opacity: 1; }
|
||||
.about { overflow: hidden; }
|
||||
.about .toolbar { display: none; }
|
||||
.about .logo { display: block; background-color: #ececec; color: #666666; }
|
||||
.about .logo-message { display: block; top: 132px; font-size: 14px; }
|
||||
.about .logo-github { display: block; top: 340px; width: 48px; height: 48px; }
|
||||
.about a { text-decoration: none; color: #666666; }
|
||||
.about a:visited { color: inherit; }
|
||||
.about a:hover { color: #242424; }
|
||||
.about .open-file-button { display: none; }
|
||||
.about .logo-name { display: none; }
|
||||
.about .notification { display: none; }
|
||||
.about .progress { display: none; }
|
||||
.about .menu-button { display: none; }
|
||||
.titlebar { color: #aaaaaa; display: none; height: 32px; position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 2; -webkit-app-region: drag; }
|
||||
.titlebar-visible { display: block; }
|
||||
.titlebar-content { display: block; padding: 0 142px; height: 100%; text-align: center; font-size: 14px; line-height: 32px; transition: all .1s ease-in-out; user-select: none; }
|
||||
.titlebar-content-text { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.spinner .titlebar-content { opacity: 0; }
|
||||
.active .titlebar { color: #464646; transition: all 0.05s ease-in-out; }
|
||||
.titlebar-control-box { display: none; align-items: center; flex-direction: row-reverse; height: 100%; position: absolute; top: 0; right: 0; width: 138px; }
|
||||
.titlebar-control-box-visible { display: flex; }
|
||||
.titlebar-icon { width: 1em; height: 1em; vertical-align: -0.15em; fill: currentColor; overflow: hidden; }
|
||||
.titlebar-button { display: flex; justify-content: center; align-items: center; width: 46px; height: 32px; user-select: none; -webkit-app-region: no-drag; }
|
||||
.titlebar-button:hover { color: #000000; background-color: rgba(0, 0, 0, 0.15); }
|
||||
.titlebar-button-close:hover { color: #ffffff; background-color: #b43029; }
|
||||
.menu-button { display: flex; justify-content: center; align-items: center; color: #aaaaaa; font-size: 20px; height: 32px; width: 32px; position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 2; -webkit-app-region: no-drag; -webkit-app-region: no-drag; user-select: none; }
|
||||
.menu-button:hover { color: #000000; }
|
||||
.menu { display: block; position: absolute; left: -17em; width: 17em; top: 0; height: 100%; z-index: 2; background-color: #ececec; border-right: 1px solid rgba(255, 255, 255, 0.5); padding-top: 40px; padding-bottom: 2px; margin-left: 0; margin-top: 0; overflow: hidden; transition: 0.1s; }
|
||||
.menu .menu-group { margin-bottom: 12px; }
|
||||
.menu .menu-group .menu-group-header { display: block; border: none; border-radius: 0; color: black; width: 100%; text-align: left; margin: 4px 12px 5px 12px; white-space: no-wrap; font-size: 11px; font-weight: bold; color: #bbbbbb; white-space: nowrap; }
|
||||
.menu .menu-group .menu-command { display: block; border: none; border-radius: 0; background-color: transparent; color: black; width: 100%; text-align: left; padding: 4px 12px 5px 12px; font-size: 12px; }
|
||||
.menu .menu-group .menu-command:focus { color: #ffffff; background-color: #2e6bd2; outline: none; }
|
||||
.menu .menu-group .menu-command:disabled { color: #888888; }
|
||||
.menu .menu-group .menu-command .menu-label { display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
||||
.menu .menu-group .menu-command .menu-shortcut { display: block; float: right; margin-left: 25px; color: #888888; }
|
||||
.menu .menu-group .menu-separator { border-top: 1px; border-bottom: 0; border-style: solid; border-color: #e5e5e5; margin-left: 12px; margin-right: 12px; }
|
||||
.about .titlebar-visible { opacity: 0; }
|
||||
@-webkit-keyframes orbit { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } }
|
||||
@keyframes orbit { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } }
|
||||
.welcome.spinner .logo-spinner-stroke { stroke: #ececec; }
|
||||
.welcome.spinner .logo-name { display: none; }
|
||||
.welcome.spinner .open-file-button { display: none; }
|
||||
.welcome.spinner .target { display: flex; opacity: 0; }
|
||||
.welcome .notification .logo-name { display: none; }
|
||||
.welcome .toolbar { display: none; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root { color-scheme: dark; }
|
||||
.default { background-color: #404040; }
|
||||
.target { background-color: #404040; }
|
||||
.welcome { background-color: #1e1e1e; color: #888888; }
|
||||
.alert { background-color: #1e1e1e; color: #888888; }
|
||||
.logo-stroke { stroke: #888888; }
|
||||
.logo-fill { fill: #888888; }
|
||||
.logo-border { stroke: #000000; }
|
||||
.logo-glyph { fill: #888888; }
|
||||
.logo-spinner-stroke { stroke: #ffffff; }
|
||||
.logo-button { color: #888888; background-color: #1e1e1e; border-color: #888888; }
|
||||
.logo-button:hover { color: #1e1e1e; background-color: #888888; }
|
||||
.welcome .progress-bar { background-color: #888888; }
|
||||
.welcome .menu { background-color: #2d2d2d }
|
||||
.about .logo { background-color: #1e1e1e; color: #888888; }
|
||||
.about a { color: #c6c6c6; }
|
||||
.about a:hover { color: #565656; }
|
||||
.welcome .message-text a { color: #c6c6c6; }
|
||||
.welcome .message-text a:visited { color: #c6c6c6; }
|
||||
.welcome .message-text a:hover { color: #565656; }
|
||||
.toolbar-icon .border { stroke: #333333; }
|
||||
.toolbar-icon .stroke { stroke: #aaaaaa; }
|
||||
.toolbar-icon .fill { fill: #aaaaaa; }
|
||||
.toolbar-icon:hover .stroke { stroke: #dfdfdf; }
|
||||
.toolbar-icon:hover .fill { fill: #dfdfdf; }
|
||||
.toolbar-path-back-button { background: #aaaaaa; border-color: #aaaaaa; color: #333333; }
|
||||
.toolbar-path-back-button:hover { background: #dfdfdf; border-color: #dfdfdf; }
|
||||
.toolbar-path-name-button { background: #aaaaaa ; border-color: #aaaaaa; color: #404040; }
|
||||
.toolbar-path-name-button:hover { background: #dfdfdf; border-color: #dfdfdf; }
|
||||
.toolbar-select select { background: #aaaaaa; border-color: #aaaaaa; color: #404040; }
|
||||
.toolbar-select select:hover { background: #dfdfdf; border-color: #dfdfdf; color: #404040; }
|
||||
.toolbar-select-arrow { fill: #404040; }
|
||||
.titlebar { color: #949494; }
|
||||
.welcome body { background-color: #1e1e1e; }
|
||||
.default body { background-color: #404040; }
|
||||
.active .titlebar { color: #c4c4c4; }
|
||||
.titlebar-button:hover { color: #ffffff; background-color: rgba(0, 0, 0, 0.15); }
|
||||
.titlebar-button-close:hover { color: #ffffff; background-color: #b43029; }
|
||||
.menu-button { color: #aaaaaa; }
|
||||
.menu-button:hover { color: #ffffff; }
|
||||
.menu { background-color: #2d2d2d; border-color: rgba(0, 0, 0, 0); }
|
||||
.menu .menu-group .menu-group-header { color: #666666; }
|
||||
.menu .menu-group .menu-command { color: #ffffff; }
|
||||
.menu .menu-group .menu-command:focus { color: #ffffff; background-color: #2e6bd2; }
|
||||
.menu .menu-group .menu-command:disabled { color: #888888; }
|
||||
.menu .menu-group .menu-command .shortcut { color: #888888; }
|
||||
.menu .menu-group .menu-separator { border-color: #363636; }
|
||||
}
|
||||
@media all and (max-width: 640px) {
|
||||
.logo { width: 240px; }
|
||||
.logo-text { opacity: 0; }
|
||||
.logo-name { opacity: 0; }
|
||||
.logo-icon { left: 0; width: 128px; height: 128px; }
|
||||
.logo-spinner { left: 0; width: 128px; height: 128px; }
|
||||
.logo .open-file-button { top: 204px; left: 0; }
|
||||
.message-text { padding-top: 68px; width: 320px; }
|
||||
.progress { top: 160px; height: 2px; width: 100px; }
|
||||
.about .logo { width: 100%; padding-left: 0; padding-right: 0; }
|
||||
.about .logo-message { top: 175px; font-size: 12px; }
|
||||
.about .logo-github { top: 370px; }
|
||||
}
|
||||
.sidebar { display: flex; flex-direction: column; font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif; font-size: 12px; height: 100%; right: -100%; position: fixed; transition: 0.1s; top: 0; background-color: #ececec; color: #242424; overflow: hidden; border-left: 1px solid rgba(255, 255, 255, 0.5); opacity: 0; }
|
||||
.sidebar-title { font-weight: bold; font-size: 12px; letter-spacing: 0.5px; text-transform: uppercase; height: 20px; margin: 0; padding: 20px; user-select: none; -webkit-user-select: none; -moz-user-select: none; }
|
||||
.sidebar-closebutton { padding: 8px 8px 8px 32px; text-decoration: none; font-size: 25px; color: #777777; opacity: 1.0; display: block; transition: 0.2s; position: absolute; top: 0; right: 15px; margin-left: 50px; user-select: none; -webkit-user-select: none; -moz-user-select: none; }
|
||||
.sidebar-closebutton:hover { color: #242424; }
|
||||
.sidebar-content { display: flex; flex-direction: column; flex-grow: 1; height: 0; }
|
||||
.sidebar-header { font-weight: bold; font-size: 12px; letter-spacing: 0.5px; text-transform: uppercase; height: 20px; margin: 0; margin-top: 30px; padding-top: 10px; padding-bottom: 10px; user-select: none; -webkit-user-select: none; -moz-user-select: none; }
|
||||
.sidebar-section { font-weight: bold; font-size: 11px; text-transform: uppercase; line-height: 1.25; margin-top: 16px; margin-bottom: 16px; display: block; user-select: none; -webkit-user-select: none; -moz-user-select: none; cursor: default; }
|
||||
.sidebar-object { flex-grow: 1; padding: 0px 20px 20px 20px; overflow-y: auto; }
|
||||
.sidebar-item { margin-bottom: 0px; display: block; }
|
||||
.sidebar-item-name { float: left; font-size: 11px; min-width: 95px; max-width: 95px; padding-right: 5px; padding-top: 7px; display: block; }
|
||||
.sidebar-item-name input { color: #777; font-family: inherit; font-size: inherit; color: inherit; background-color: inherit; width: 100%; text-align: right; margin: 0; padding: 0; border: 0; outline: none; text-overflow: ellipsis; }
|
||||
.sidebar-item-value-list { margin: 0; margin-left: 105px; overflow: hidden; display: block; padding: 0; }
|
||||
.sidebar-item-value { font-size: 11px; background-color: #fcfcfc; border-radius: 2px; border: 1px solid #fcfcfc; margin-top: 3px; margin-bottom: 3px; overflow: auto; }
|
||||
.sidebar-item-value-content { background-color: #f8f8f8; border: 1px solid #f8f8f8; }
|
||||
.sidebar-item-value b { font-weight: bold; }
|
||||
.sidebar-item-value code { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; overflow: auto; white-space: pre-wrap; word-wrap: break-word; }
|
||||
.sidebar-item-value pre { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; margin: 0; overflow: auto; white-space: pre; word-wrap: normal; display: block; }
|
||||
.sidebar-item-value-line { padding: 4px 6px 4px 6px; overflow-x: auto; white-space: pre; }
|
||||
.sidebar-item-value-line-wrap { white-space: pre-wrap; overflow-x: hidden; overflow-wrap: break-word; }
|
||||
.sidebar-item-value-line-break { padding: 4px 6px 4px 6px; overflow-x: auto; white-space: pre; }
|
||||
.sidebar-item-value-line-link { padding: 4px 6px 4px 6px; cursor: default; overflow-x: auto; white-space: nowrap; }
|
||||
.sidebar-item-value-line-link:hover { text-decoration: underline; }
|
||||
.sidebar-item-value-line-border { padding: 4px 6px 4px 6px; border-top: 1px solid rgba(27, 31, 35, 0.05); }
|
||||
.sidebar-item-value-line-content { white-space: pre; word-wrap: normal; overflow: auto; display: block; }
|
||||
.sidebar-item-value-expander { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; float: right; color: #aaa; cursor: pointer; user-select: none; -webkit-user-select: none; -moz-user-select: none; padding: 4px 6px 4px 4px; }
|
||||
.sidebar-item-value-expander:hover { color: #333; }
|
||||
.sidebar-item-value-button { display: flex; justify-content: center; align-items: center; font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; width: 22px; height: 22px; cursor: pointer; user-select: none; -webkit-user-select: none; -moz-user-select: none; color: #aaa; }
|
||||
.sidebar-item-value-button svg use { fill: #aaa; stroke: #aaa; }
|
||||
.sidebar-item-value-button:hover svg use { fill: #333; stroke: #333; }
|
||||
.sidebar-item-value-button-tool { float: right; padding-left: 3px; }
|
||||
.sidebar-item-value-button-context { float: right; }
|
||||
.sidebar-item-selector {
|
||||
font-family: inherit; font-size: 12px;
|
||||
background-color: #fcfcfc; border: #fcfcfc; color: #333;
|
||||
border-radius: 2px; width: 100%; height: 23px; padding: 3px 12px 3px 7px;
|
||||
margin-top: 3px; margin-bottom: 3px; outline: none;
|
||||
box-sizing: border-box; -moz-box-sizing: border-box;
|
||||
appearance: none; -webkit-appearance: none; -moz-appearance: none;
|
||||
background-image: linear-gradient(45deg, transparent 50%, #333 50%), linear-gradient(135deg, #333 50%, transparent 50%);
|
||||
background-position: calc(100% - 12px) calc(10px), calc(100% - 7px) calc(10px);
|
||||
background-size: 5px 5px, 5px 5px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.sidebar-separator { margin-bottom: 20px; }
|
||||
.sidebar-find-search { display: flex; align-items: center; background: #fff; border-radius: 16px; margin: 0px 20px 8px 20px; padding: 0px 8px 0px 8px; }
|
||||
.sidebar-find-query { width: 100vw; background: none; font-family: inherit; font-size: 13px; padding: 8px 8px 8px 8px; border: 0; outline: 0; }
|
||||
.sidebar-find-toggle { margin-left: auto; margin-right: 2px; width: 16px; height: 16px; cursor: pointer; }
|
||||
.sidebar-find-toggle input[type="checkbox"] { display: none; }
|
||||
.sidebar-find-toggle input[type="checkbox"]:not(:checked) + svg { fill: #ccc; stroke: #ccc; }
|
||||
.sidebar-find-toggle input[type="checkbox"]:checked + svg { fill: #555; stroke: #555; }
|
||||
.sidebar-find-toggle-icon { stroke: #555; fill: #555; width: 16px; height: 16px; }
|
||||
.sidebar-find-content { flex-grow: 1; padding: 0px 20px 20px 20px; overflow-y: auto; list-style-type: none; margin: 0; outline: 0; }
|
||||
.sidebar-find-content li *:first-child { margin-right: 2px; }
|
||||
.sidebar-find-content li { color: #666; font-size: 13px; height: 22px; line-height: 22px; padding: 0 12px 0 12px; outline: 0; white-space: nowrap; border-radius: 3px; user-select: none; -webkit-user-select: none; -moz-user-select: none; }
|
||||
.sidebar-find-content li.focus { background: #e5e5e5; color: #000; }
|
||||
.sidebar-find-content-icon { stroke: #555; fill: #555; float: left; width: 16px; height: 16px; padding: 3px; pointer-events: none; }
|
||||
.sidebar-documentation { flex-grow: 1; padding: 0px 20px 20px 20px; overflow-y: auto; font-size: 13px; line-height: 1.5; margin: 0; }
|
||||
.sidebar-documentation h1 { font-weight: bold; font-size: 13px; line-height: 1.25; border-bottom: 1px solid #e8e8e8; padding-bottom: 0.3em; margin-top: 0; margin-bottom: 16px; }
|
||||
.sidebar-documentation h2 { font-weight: bold; font-size: 13px; line-height: 1.25; margin-top: 20px; margin-bottom: 16px; text-transform: uppercase; border: 0; }
|
||||
.sidebar-documentation h3 { font-weight: bold; font-size: 11px; line-height: 1.25; }
|
||||
.sidebar-documentation p { margin-top: 4px; margin-bottom: 4px; margin-left: 0px; }
|
||||
.sidebar-documentation a { color: #237; }
|
||||
.sidebar-documentation code { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 12px; background-color: rgba(27, 31, 35, 0.05); padding: 0.2em 0.4em; margin: 0; border-radius: 3px; }
|
||||
.sidebar-documentation pre { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 12px; padding: 16px; overflow: auto; line-height: 1.45; background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; }
|
||||
.sidebar-documentation pre code { font-size: 13px; padding: 16px; line-height: 1.45; background-color: transparent; padding: 0; border-radius: 0; }
|
||||
.sidebar-documentation tt { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-weight: bold; font-size: 90%; background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; padding: 0.2em 0.4em; margin: 0; }
|
||||
.sidebar-documentation dl dt { font-size: 13px; font-weight: bold; padding: 0; margin-top: 16px; margin-left: 0px; }
|
||||
.sidebar-documentation dd { padding: 0 16px; margin-left: 0; margin-bottom: 16px; }
|
||||
.sidebar-documentation ul { margin-top: 6px; margin-bottom: 6px; padding-left: 20px; }
|
||||
.sidebar-documentation blockquote { margin-left: 15px; margin-right: 15px; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.sidebar html { color: #dfdfdf; }
|
||||
.sidebar { background-color: #2d2d2d; color: #dfdfdf; border-left: 1px solid rgba(0, 0, 0, 0); }
|
||||
.sidebar-closebutton { padding: 8px 8px 8px 32px; text-decoration: none; font-size: 25px; color: #777777; opacity: 1.0; display: block; transition: 0.2s; position: absolute; top: 0; right: 15px; margin-left: 50px; user-select: none; -webkit-user-select: none; -moz-user-select: none; }
|
||||
.sidebar-closebutton:hover { color: #ffffff; }
|
||||
.sidebar-item-value { background-color: #383838; border-color: #383838; }
|
||||
.sidebar-item-value-content { background-color: #3e3e3e; border-color: #3e3e3e; }
|
||||
.sidebar-item-value-line-border { border-color: rgba(0, 0, 0, 0.09); }
|
||||
.sidebar-item-selector { background-color: #383838; border: #383838; color: #dfdfdf; background-image: linear-gradient(45deg, transparent 50%, #aaa 50%), linear-gradient(135deg, #aaa 50%, transparent 50%); }
|
||||
.sidebar-item-disable-select { user-select: none; -webkit-user-select: none; -moz-user-select: none; }
|
||||
.sidebar-header { border-bottom-color: #2d2d2d; color: #dfdfdf; }
|
||||
.sidebar-documentation h1 { border-bottom: 1px solid #424242; color: #dfdfdf; }
|
||||
.sidebar-documentation h2 { color: #dfdfdf; }
|
||||
.sidebar-documentation p { color: #aaaaaa; }
|
||||
.sidebar-documentation a { color: #6688aa; }
|
||||
.sidebar-documentation tt { background-color:#1e1e1e; }
|
||||
.sidebar-documentation code { background-color: #1e1e1e; }
|
||||
.sidebar-documentation pre { background-color: #1e1e1e; }
|
||||
.sidebar-find-search { background: #383838; color: #dfdfdf; border-color: #424242; }
|
||||
.sidebar-find-toggle input[type="checkbox"]:not(:checked) + svg { fill: #555; stroke: #555; }
|
||||
.sidebar-find-toggle input[type="checkbox"]:checked + svg { fill: #aaa; stroke: #aaa; }
|
||||
.sidebar-find-content li { color: #aaaaaa; }
|
||||
.sidebar-find-content li.focus { background: #383838; color: #dfdfdf; }
|
||||
.sidebar-find-content-icon { stroke: #888888; fill: #888888; }
|
||||
.sidebar-item-value-expander { color: #888; }
|
||||
.sidebar-item-value-expander:hover { color: #e5e5e5; }
|
||||
.sidebar-item-value-button { color: #888; }
|
||||
.sidebar-item-value-button svg use { fill: #888; stroke: #888; }
|
||||
.sidebar-item-value-button:hover svg use { fill: #e5e5e5; stroke: #e5e5e5; }
|
||||
}
|
||||
@media screen and (prefers-reduced-motion: reduce) {
|
||||
.menu { transition: none; }
|
||||
.sidebar { transition: none; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="welcome spinner">
|
||||
<div id="target" class="target" tabindex="0">
|
||||
</div>
|
||||
<div id="sidebar" class="sidebar">
|
||||
<h1 id="sidebar-title" class="sidebar-title"></h1>
|
||||
<a id="sidebar-closebutton" class="sidebar-closebutton" href="javascript:void(0)" draggable="false">×</a>
|
||||
<div id="sidebar-content" class="sidebar-content"></div>
|
||||
<svg width="0" height="0" display="none">
|
||||
<defs>
|
||||
<symbol id="sidebar-icon-node" viewBox="0 0 20 20">
|
||||
<circle cx="10" cy="10" r="6" stroke-width="2" fill="none"/>
|
||||
</symbol>
|
||||
<symbol id="sidebar-icon-connection" viewBox="0 0 20 20">
|
||||
<line x1="4" y1="10" x2="15" y2="10" stroke-width="2" />
|
||||
<polyline points="11,6 15,10 11,14" stroke-width="2" />
|
||||
</symbol>
|
||||
<symbol id="sidebar-icon-weight" viewBox="0 0 20 20">
|
||||
<circle cx="5" cy="5" r="1" />
|
||||
<circle cx="10" cy="5" r="1" />
|
||||
<circle cx="15" cy="5" r="1" />
|
||||
<circle cx="5" cy="10" r="1" />
|
||||
<circle cx="10" cy="10" r="1" />
|
||||
<circle cx="15" cy="10" r="1" />
|
||||
<circle cx="5" cy="15" r="1" />
|
||||
<circle cx="10" cy="15" r="1" />
|
||||
<circle cx="15" cy="15" r="1" />
|
||||
</symbol>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="toolbar" class="toolbar">
|
||||
<button id="zoom-in-button" class="toolbar-button" title="Zoom In">
|
||||
<svg class="toolbar-icon" viewbox="0 0 100 100">
|
||||
<circle class="border" cx="50" cy="50" r="35" stroke-width="8" stroke="#fff"></circle>
|
||||
<line class="border" x1="50" y1="38" x2="50" y2="62" stroke-width="8" stroke-linecap="round" stroke="#fff"></line>
|
||||
<line class="border" x1="38" y1="50" x2="62" y2="50" stroke-width="8" stroke-linecap="round" stroke="#fff"></line>
|
||||
<line class="border" x1="78" y1="78" x2="82" y2="82" stroke-width="12" stroke-linecap="square" stroke="#fff"></line>
|
||||
<circle class="stroke" cx="50" cy="50" r="35" stroke-width="4"></circle>
|
||||
<line class="stroke" x1="50" y1="38" x2="50" y2="62" stroke-width="4" stroke-linecap="round"></line>
|
||||
<line class="stroke" x1="38" y1="50" x2="62" y2="50" stroke-width="4" stroke-linecap="round"></line>
|
||||
<line class="stroke" x1="78" y1="78" x2="82" y2="82" stroke-width="8" stroke-linecap="square"></line>
|
||||
</svg>
|
||||
</button>
|
||||
<button id="zoom-out-button" class="toolbar-button" title="Zoom Out">
|
||||
<svg class="toolbar-icon" viewbox="0 0 100 100">
|
||||
<circle class="border" cx="50" cy="50" r="35" stroke-width="8" stroke="#fff"></circle>
|
||||
<line class="border" x1="38" y1="50" x2="62" y2="50" stroke-width="8" stroke-linecap="round" stroke="#fff"></line>
|
||||
<line class="border" x1="78" y1="78" x2="82" y2="82" stroke-width="12" stroke-linecap="square" stroke="#fff"></line>
|
||||
<circle class="stroke" cx="50" cy="50" r="35" stroke-width="4"></circle>
|
||||
<line class="stroke" x1="38" y1="50" x2="62" y2="50" stroke-width="4" stroke-linecap="round"></line>
|
||||
<line class="stroke" x1="78" y1="78" x2="82" y2="82" stroke-width="8" stroke-linecap="square"></line>
|
||||
</svg>
|
||||
</button>
|
||||
<button id="sidebar-model-button" class="toolbar-button" title="Model Properties">
|
||||
<svg class="toolbar-icon" viewbox="0 0 100 100">
|
||||
<rect class="border" x="12" y="12" width="76" height="76" rx="16" ry="16" stroke-width="8"></rect>
|
||||
<line class="border" x1="28" y1="37" x2="32" y2="37" stroke-width="8" stroke-linecap="round" stroke="#fff"></line>
|
||||
<line class="border" x1="28" y1="50" x2="32" y2="50" stroke-width="8" stroke-linecap="round" stroke="#fff"></line>
|
||||
<line class="border" x1="28" y1="63" x2="32" y2="63" stroke-width="8" stroke-linecap="round" stroke="#fff"></line>
|
||||
<line class="border" x1="40" y1="37" x2="70" y2="37" stroke-width="8" stroke-linecap="round" stroke="#fff"></line>
|
||||
<line class="border" x1="40" y1="50" x2="70" y2="50" stroke-width="8" stroke-linecap="round" stroke="#fff"></line>
|
||||
<line class="border" x1="40" y1="63" x2="70" y2="63" stroke-width="8" stroke-linecap="round" stroke="#fff"></line>
|
||||
<rect class="stroke" x="12" y="12" width="76" height="76" rx="16" ry="16" stroke-width="4"></rect>
|
||||
<line class="stroke" x1="28" y1="37" x2="32" y2="37" stroke-width="4" stroke-linecap="round"></line>
|
||||
<line class="stroke" x1="28" y1="50" x2="32" y2="50" stroke-width="4" stroke-linecap="round"></line>
|
||||
<line class="stroke" x1="28" y1="63" x2="32" y2="63" stroke-width="4" stroke-linecap="round"></line>
|
||||
<line class="stroke" x1="40" y1="37" x2="70" y2="37" stroke-width="4" stroke-linecap="round"></line>
|
||||
<line class="stroke" x1="40" y1="50" x2="70" y2="50" stroke-width="4" stroke-linecap="round"></line>
|
||||
<line class="stroke" x1="40" y1="63" x2="70" y2="63" stroke-width="4" stroke-linecap="round"></line>
|
||||
</svg>
|
||||
</button>
|
||||
<button id="sidebar-target-button" class="toolbar-button" title="Target Properties">
|
||||
<svg class="toolbar-icon" viewBox="0 0 100 100">
|
||||
<rect class="border" x="12" y="12" width="76" height="76" rx="16" ry="16" stroke-width="8"></rect>
|
||||
<circle class="border" cx="50" cy="50" r="18" stroke-width="8"></circle>
|
||||
<circle class="border" cx="50" cy="50" r="3" stroke-width="8"></circle>
|
||||
<rect class="stroke" x="12" y="12" width="76" height="76" rx="16" ry="16" stroke-width="4"></rect>
|
||||
<circle class="stroke" cx="50" cy="50" r="18" stroke-width="4"></circle>
|
||||
<circle class="stroke fill" cx="50" cy="50" r="3" stroke-width="4"></circle>
|
||||
</svg>
|
||||
</button>
|
||||
<div id="toolbar-navigator" class="toolbar-select">
|
||||
<select id="toolbar-target-selector">
|
||||
<option value="Graphs" disabled="true">— Graphs ——</option>
|
||||
<option value="add">add</option>
|
||||
<option value="subtract">subtract</option>
|
||||
<option value="multiply">multiply</option>
|
||||
<option value="Functions" disabled="true">— Functions ——</option>
|
||||
<option value="line">foo_bar.foo</option>
|
||||
</select>
|
||||
<svg class="toolbar-select-arrow" viewBox="0 0 10 6" width="10" height="6">
|
||||
<path d="M0 0L5 6L10 0Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div id="toolbar-path" class="toolbar-path">
|
||||
<button id="toolbar-path-back-button" class="toolbar-path-back-button" title="Back">
|
||||
❮
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="logo" class="center logo">
|
||||
<a href="https://github.com/lutzroeder/netron" target="blank_">
|
||||
<svg class="center logo-text" viewbox="0 0 5120 1024">
|
||||
<g transform="scale(9) translate(-44,-15)">
|
||||
<g transform="matrix(100,0,0,100,60.9965,126)">
|
||||
<path class="logo-glyph" d="M0.089,0L0.089,-0.745L0.595,-0.147L0.595,-0.715L0.656,-0.715L0.656,0.021L0.15,-0.578L0.15,0L0.089,0Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(100,0,0,100,164.341,126)">
|
||||
<path class="logo-glyph" d="M0.089,0L0.089,-0.715L0.443,-0.715L0.443,-0.654L0.154,-0.654L0.154,-0.43L0.443,-0.43L0.443,-0.369L0.154,-0.369L0.154,-0.061L0.443,-0.061L0.443,0L0.089,0Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(100,0,0,100,244.491,126)">
|
||||
<path class="logo-glyph" d="M0.216,0L0.216,-0.654L0.019,-0.654L0.019,-0.715L0.478,-0.715L0.478,-0.654L0.281,-0.654L0.281,0L0.216,0Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(100,0,0,100,323.031,126)">
|
||||
<path class="logo-glyph" d="M0.154,-0.658L0.154,-0.394L0.219,-0.394C0.28,-0.394 0.322,-0.404 0.346,-0.423C0.37,-0.442 0.382,-0.475 0.382,-0.522C0.382,-0.571 0.369,-0.606 0.345,-0.627C0.32,-0.648 0.278,-0.658 0.219,-0.658L0.154,-0.658ZM0.523,0L0.444,0L0.193,-0.341L0.154,-0.341L0.154,0L0.089,0L0.089,-0.715L0.22,-0.715C0.298,-0.715 0.356,-0.699 0.394,-0.667C0.433,-0.634 0.452,-0.585 0.452,-0.52C0.452,-0.464 0.436,-0.421 0.403,-0.389C0.37,-0.357 0.324,-0.341 0.266,-0.341L0.523,0Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(100,0,0,100,520.979,126)">
|
||||
<path class="logo-glyph" d="M0.089,0L0.089,-0.745L0.595,-0.147L0.595,-0.715L0.656,-0.715L0.656,0.021L0.15,-0.578L0.15,0L0.089,0Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<svg class="center logo-icon" viewbox="0 0 1024 1024">
|
||||
<circle class="logo-stroke" cx="512" cy="512" r="431" fill="none" stroke-width="32"></circle>
|
||||
<circle class="logo-border" cx="512" cy="512" r="450" fill="none" stroke-width="6"></circle>
|
||||
<circle class="logo-border" cx="512" cy="512" r="412" fill="none" stroke-width="6"></circle>
|
||||
<line class="logo-stroke" x1="296" y1="392" x2="540" y2="280" stroke-width="12"></line>
|
||||
<line class="logo-stroke" x1="296" y1="632" x2="540" y2="280" stroke-width="12"></line>
|
||||
<line class="logo-stroke" x1="296" y1="392" x2="540" y2="435" stroke-width="12"></line>
|
||||
<line class="logo-stroke" x1="296" y1="632" x2="540" y2="435" stroke-width="12"></line>
|
||||
<line class="logo-stroke" x1="296" y1="392" x2="540" y2="590" stroke-width="12"></line>
|
||||
<line class="logo-stroke" x1="296" y1="632" x2="540" y2="590" stroke-width="12"></line>
|
||||
<line class="logo-stroke" x1="296" y1="392" x2="540" y2="744" stroke-width="12"></line>
|
||||
<line class="logo-stroke" x1="296" y1="632" x2="540" y2="744" stroke-width="12"></line>
|
||||
<line class="logo-stroke" x1="540" y1="280" x2="785" y2="512" stroke-width="12"></line>
|
||||
<line class="logo-stroke" x1="540" y1="590" x2="785" y2="512" stroke-width="12"></line>
|
||||
<line class="logo-stroke" x1="540" y1="435" x2="785" y2="512" stroke-width="12"></line>
|
||||
<line class="logo-stroke" x1="540" y1="744" x2="785" y2="512" stroke-width="12"></line>
|
||||
<g transform="translate(296, 392)">
|
||||
<circle class="logo-fill" cx="0" cy="0" r="51"></circle>
|
||||
<circle class="logo-border" cx="0" cy="0" r="51" fill="none" stroke-width="6"></circle>
|
||||
</g>
|
||||
<g transform="translate(296, 632)">
|
||||
<circle class="logo-fill" cx="0" cy="0" r="51"></circle>
|
||||
<circle class="logo-border" cx="0" cy="0" r="51" fill="none" stroke-width="6"></circle>
|
||||
</g>
|
||||
<g transform="translate(540, 280)">
|
||||
<circle class="logo-fill" cx="0" cy="0" r="51"></circle>
|
||||
<circle class="logo-border" cx="0" cy="0" r="51" fill="none" stroke-width="6"></circle>
|
||||
</g>
|
||||
<g transform="translate(540, 435)">
|
||||
<circle class="logo-fill" cx="0" cy="0" r="51"></circle>
|
||||
<circle class="logo-border" cx="0" cy="0" r="51" fill="none" stroke-width="6"></circle>
|
||||
</g>
|
||||
<g transform="translate(540, 590)">
|
||||
<circle class="logo-fill" cx="0" cy="0" r="51"></circle>
|
||||
<circle class="logo-border" cx="0" cy="0" r="51" fill="none" stroke-width="6"></circle>
|
||||
</g>
|
||||
<g transform="translate(540, 744)">
|
||||
<circle class="logo-fill" cx="0" cy="0" r="51"></circle>
|
||||
<circle class="logo-border" cx="0" cy="0" r="51" fill="none" stroke-width="6"></circle>
|
||||
</g>
|
||||
<g transform="translate(785, 512)">
|
||||
<circle class="logo-fill" cx="0" cy="0" r="51"></circle>
|
||||
<circle class="logo-border" cx="0" cy="0" r="51" fill="none" stroke-width="6"></circle>
|
||||
</g>
|
||||
</svg>
|
||||
<svg id="logo-spinner" class="center logo-spinner" viewbox="0 0 1024 1024">
|
||||
<g transform="translate(512, 512)" style="opacity: 1">
|
||||
<path class="logo-spinner-stroke" d="M-431,0 A-431,-431 0 0,1 0,-431" stroke-width="24" fill="None"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="https://www.lutzroeder.com" target="blank_">
|
||||
<svg class="center logo-name" viewbox="0 0 5120 300">
|
||||
<g transform="scale(5.8) translate(20, 0)">
|
||||
<g transform="matrix(30,0,0,30,18.9123,38)">
|
||||
<path class="logo-glyph" d="M0.089,-0L0.089,-0.715L0.154,-0.715L0.154,-0.061L0.399,-0.061L0.399,-0L0.089,-0Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(30,0,0,30,46.7613,38)">
|
||||
<path class="logo-glyph" d="M0.086,-0.715L0.15,-0.715L0.15,-0.248C0.15,-0.177 0.166,-0.125 0.198,-0.091C0.23,-0.056 0.28,-0.039 0.346,-0.039C0.412,-0.039 0.46,-0.056 0.493,-0.091C0.525,-0.125 0.541,-0.177 0.541,-0.248L0.541,-0.715L0.606,-0.715L0.606,-0.269C0.606,-0.172 0.584,-0.1 0.542,-0.052C0.499,-0.005 0.433,0.019 0.346,0.019C0.259,0.019 0.193,-0.005 0.15,-0.052C0.107,-0.1 0.086,-0.172 0.086,-0.269L0.086,-0.715Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(30,0,0,30,83.5133,38)">
|
||||
<path class="logo-glyph" d="M0.216,-0L0.216,-0.654L0.019,-0.654L0.019,-0.715L0.478,-0.715L0.478,-0.654L0.281,-0.654L0.281,-0L0.216,-0Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(30,0,0,30,114.421,38)">
|
||||
<path class="logo-glyph" d="M0.012,-0L0.437,-0.656L0.074,-0.656L0.074,-0.715L0.548,-0.715L0.125,-0.06L0.505,-0.06L0.505,-0L0.012,-0Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(30,0,0,30,171.777,38)">
|
||||
<path class="logo-glyph" d="M0.154,-0.658L0.154,-0.394L0.219,-0.394C0.28,-0.394 0.322,-0.404 0.346,-0.423C0.37,-0.442 0.382,-0.475 0.382,-0.522C0.382,-0.571 0.369,-0.606 0.345,-0.627C0.32,-0.648 0.278,-0.658 0.219,-0.658L0.154,-0.658ZM0.523,-0L0.444,-0L0.193,-0.341L0.154,-0.341L0.154,-0L0.089,-0L0.089,-0.715L0.22,-0.715C0.298,-0.715 0.356,-0.699 0.394,-0.667C0.433,-0.634 0.452,-0.585 0.452,-0.52C0.452,-0.464 0.436,-0.421 0.403,-0.389C0.37,-0.357 0.324,-0.341 0.266,-0.341L0.523,-0Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(30,0,0,30,203.607,38)">
|
||||
<path class="logo-glyph" d="M0.437,-0.039C0.479,-0.039 0.519,-0.047 0.557,-0.063C0.595,-0.078 0.629,-0.101 0.659,-0.131C0.689,-0.161 0.712,-0.196 0.727,-0.234C0.743,-0.273 0.751,-0.313 0.751,-0.356C0.751,-0.399 0.743,-0.44 0.728,-0.478C0.712,-0.516 0.689,-0.55 0.659,-0.581C0.63,-0.611 0.596,-0.634 0.558,-0.649C0.52,-0.665 0.48,-0.673 0.437,-0.673C0.395,-0.673 0.355,-0.665 0.317,-0.649C0.28,-0.634 0.246,-0.611 0.216,-0.581C0.186,-0.55 0.163,-0.516 0.147,-0.478C0.132,-0.44 0.124,-0.399 0.124,-0.356C0.124,-0.313 0.132,-0.272 0.147,-0.234C0.163,-0.196 0.186,-0.161 0.216,-0.131C0.246,-0.101 0.279,-0.078 0.316,-0.062C0.354,-0.047 0.394,-0.039 0.437,-0.039ZM0.82,-0.356C0.82,-0.306 0.81,-0.258 0.791,-0.212C0.772,-0.167 0.744,-0.126 0.708,-0.091C0.671,-0.055 0.63,-0.028 0.583,-0.009C0.537,0.01 0.488,0.019 0.437,0.019C0.386,0.019 0.337,0.01 0.291,-0.009C0.245,-0.028 0.203,-0.055 0.167,-0.091C0.131,-0.127 0.103,-0.168 0.084,-0.213C0.065,-0.258 0.055,-0.306 0.055,-0.356C0.055,-0.407 0.065,-0.455 0.084,-0.501C0.103,-0.546 0.131,-0.587 0.167,-0.623C0.203,-0.659 0.244,-0.685 0.29,-0.704C0.335,-0.722 0.385,-0.731 0.437,-0.731C0.49,-0.731 0.539,-0.722 0.585,-0.703C0.631,-0.685 0.672,-0.658 0.708,-0.623C0.744,-0.587 0.772,-0.546 0.791,-0.501C0.81,-0.455 0.82,-0.407 0.82,-0.356Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(30,0,0,30,245.853,38)">
|
||||
<path class="logo-glyph" d="M0.089,-0L0.089,-0.715L0.443,-0.715L0.443,-0.654L0.154,-0.654L0.154,-0.43L0.443,-0.43L0.443,-0.369L0.154,-0.369L0.154,-0.061L0.443,-0.061L0.443,-0L0.089,-0Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(30,0,0,30,277.243,38)">
|
||||
<path class="logo-glyph" d="M0.154,-0.056L0.245,-0.056C0.319,-0.056 0.371,-0.06 0.402,-0.069C0.433,-0.077 0.459,-0.091 0.481,-0.111C0.511,-0.139 0.534,-0.174 0.549,-0.215C0.564,-0.257 0.572,-0.305 0.572,-0.358C0.572,-0.413 0.564,-0.461 0.549,-0.502C0.533,-0.544 0.51,-0.578 0.48,-0.605C0.457,-0.625 0.429,-0.64 0.396,-0.648C0.364,-0.657 0.306,-0.661 0.224,-0.661L0.154,-0.661L0.154,-0.056ZM0.089,-0L0.089,-0.715L0.2,-0.715C0.299,-0.715 0.37,-0.71 0.412,-0.7C0.453,-0.69 0.489,-0.674 0.519,-0.65C0.559,-0.618 0.589,-0.578 0.61,-0.528C0.631,-0.478 0.641,-0.421 0.641,-0.357C0.641,-0.293 0.631,-0.236 0.61,-0.186C0.589,-0.136 0.559,-0.096 0.52,-0.066C0.489,-0.042 0.454,-0.025 0.414,-0.015C0.374,-0.005 0.31,-0 0.222,-0L0.089,-0Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(30,0,0,30,314.142,38)">
|
||||
<path class="logo-glyph" d="M0.089,-0L0.089,-0.715L0.443,-0.715L0.443,-0.654L0.154,-0.654L0.154,-0.43L0.443,-0.43L0.443,-0.369L0.154,-0.369L0.154,-0.061L0.443,-0.061L0.443,-0L0.089,-0Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(30,0,0,30,345.532,38)">
|
||||
<path class="logo-glyph" d="M0.154,-0.658L0.154,-0.394L0.219,-0.394C0.28,-0.394 0.322,-0.404 0.346,-0.423C0.37,-0.442 0.382,-0.475 0.382,-0.522C0.382,-0.571 0.369,-0.606 0.345,-0.627C0.32,-0.648 0.278,-0.658 0.219,-0.658L0.154,-0.658ZM0.523,-0L0.444,-0L0.193,-0.341L0.154,-0.341L0.154,-0L0.089,-0L0.089,-0.715L0.22,-0.715C0.298,-0.715 0.356,-0.699 0.394,-0.667C0.433,-0.634 0.452,-0.585 0.452,-0.52C0.452,-0.464 0.436,-0.421 0.403,-0.389C0.37,-0.357 0.324,-0.341 0.266,-0.341L0.523,-0Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(30,0,0,30,376.911,38)">
|
||||
<path class="logo-glyph" d="M0.06,-0.468L0.155,-0.731L0.228,-0.699L0.1,-0.452L0.06,-0.468Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(30,0,0,30,401.549,38)">
|
||||
<path class="logo-glyph" d="M0.034,-0.12L0.09,-0.15C0.1,-0.115 0.118,-0.087 0.144,-0.068C0.169,-0.049 0.2,-0.039 0.236,-0.039C0.281,-0.039 0.316,-0.052 0.342,-0.079C0.367,-0.106 0.38,-0.143 0.38,-0.19C0.38,-0.224 0.371,-0.253 0.354,-0.276C0.337,-0.299 0.3,-0.325 0.244,-0.355C0.172,-0.393 0.124,-0.427 0.101,-0.456C0.077,-0.485 0.065,-0.519 0.065,-0.56C0.065,-0.611 0.082,-0.652 0.116,-0.684C0.151,-0.716 0.195,-0.732 0.25,-0.732C0.286,-0.732 0.317,-0.724 0.344,-0.709C0.37,-0.694 0.392,-0.671 0.408,-0.641L0.358,-0.611C0.347,-0.631 0.333,-0.647 0.314,-0.658C0.295,-0.668 0.272,-0.674 0.246,-0.674C0.211,-0.674 0.183,-0.663 0.162,-0.643C0.141,-0.622 0.131,-0.594 0.131,-0.559C0.131,-0.509 0.172,-0.462 0.255,-0.419C0.27,-0.411 0.281,-0.405 0.289,-0.401C0.35,-0.367 0.391,-0.336 0.411,-0.306C0.432,-0.276 0.442,-0.237 0.442,-0.19C0.442,-0.126 0.423,-0.075 0.386,-0.037C0.348,0 0.297,0.019 0.233,0.019C0.186,0.019 0.146,0.007 0.113,-0.016C0.079,-0.039 0.053,-0.074 0.034,-0.12Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="center logo-message">
|
||||
<div style="height: 30px; text-align: center;">Version <span id="version" class="select">{version}</span></div>
|
||||
<div style="height: 30px; text-align: center;">Copyright © <a href="https://www.lutzroeder.com" target="blank_">Lutz Roeder</a></div>
|
||||
</div>
|
||||
<a id="logo-github" class="center logo-github" href="https://github.com/lutzroeder/netron" target="blank_">
|
||||
<svg viewbox="0 0 438.549 438.549">
|
||||
<path class="logo-fill" d="M409.132,114.573c-19.608-33.596-46.205-60.194-79.798-79.8C295.736,15.166,259.057,5.365,219.271,5.365
|
||||
c-39.781,0-76.472,9.804-110.063,29.408c-33.596,19.605-60.192,46.204-79.8,79.8C9.803,148.168,0,184.854,0,224.63
|
||||
c0,47.78,13.94,90.745,41.827,128.906c27.884,38.164,63.906,64.572,108.063,79.227c5.14,0.954,8.945,0.283,11.419-1.996
|
||||
c2.475-2.282,3.711-5.14,3.711-8.562c0-0.571-0.049-5.708-0.144-15.417c-0.098-9.709-0.144-18.179-0.144-25.406l-6.567,1.136
|
||||
c-4.187,0.767-9.469,1.092-15.846,1c-6.374-0.089-12.991-0.757-19.842-1.999c-6.854-1.231-13.229-4.086-19.13-8.559
|
||||
c-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559
|
||||
c-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-0.951-2.568-2.098-3.711-3.429c-1.142-1.331-1.997-2.663-2.568-3.997
|
||||
c-0.572-1.335-0.098-2.43,1.427-3.289c1.525-0.859,4.281-1.276,8.28-1.276l5.708,0.853c3.807,0.763,8.516,3.042,14.133,6.851
|
||||
c5.614,3.806,10.229,8.754,13.846,14.842c4.38,7.806,9.657,13.754,15.846,17.847c6.184,4.093,12.419,6.136,18.699,6.136
|
||||
c6.28,0,11.704-0.476,16.274-1.423c4.565-0.952,8.848-2.383,12.847-4.285c1.713-12.758,6.377-22.559,13.988-29.41
|
||||
c-10.848-1.14-20.601-2.857-29.264-5.14c-8.658-2.286-17.605-5.996-26.835-11.14c-9.235-5.137-16.896-11.516-22.985-19.126
|
||||
c-6.09-7.614-11.088-17.61-14.987-29.979c-3.901-12.374-5.852-26.648-5.852-42.826c0-23.035,7.52-42.637,22.557-58.817
|
||||
c-7.044-17.318-6.379-36.732,1.997-58.24c5.52-1.715,13.706-0.428,24.554,3.853c10.85,4.283,18.794,7.952,23.84,10.994
|
||||
c5.046,3.041,9.089,5.618,12.135,7.708c17.705-4.947,35.976-7.421,54.818-7.421s37.117,2.474,54.823,7.421l10.849-6.849
|
||||
c7.419-4.57,16.18-8.758,26.262-12.565c10.088-3.805,17.802-4.853,23.134-3.138c8.562,21.509,9.325,40.922,2.279,58.24
|
||||
c15.036,16.18,22.559,35.787,22.559,58.817c0,16.178-1.958,30.497-5.853,42.966c-3.9,12.471-8.941,22.457-15.125,29.979
|
||||
c-6.191,7.521-13.901,13.85-23.131,18.986c-9.232,5.14-18.182,8.85-26.84,11.136c-8.662,2.286-18.415,4.004-29.263,5.146
|
||||
c9.894,8.562,14.842,22.077,14.842,40.539v60.237c0,3.422,1.19,6.279,3.572,8.562c2.379,2.279,6.136,2.95,11.276,1.995
|
||||
c44.163-14.653,80.185-41.062,108.068-79.226c27.88-38.161,41.825-81.126,41.825-128.906
|
||||
C438.536,184.851,428.728,148.168,409.132,114.573z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<button id="open-file-button" class="center logo-button open-file-button" tabindex="0">Open Model…</button>
|
||||
<div class="center progress">
|
||||
<div id="progress-bar" class="progress-bar"></div>
|
||||
</div>
|
||||
<input type="file" id="open-file-dialog" class="open-file-dialog" multiple="false" accept="">
|
||||
<!-- 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: bold; color: rgba(0, 0, 0, 0.01); user-select: none;">.</div>
|
||||
<div style="font-weight: bold; color: rgba(0, 0, 0, 0.01); user-select: none;">.</div>
|
||||
</div>
|
||||
<div id="message" class="message">
|
||||
<div id="message-text" class="message-text"></div>
|
||||
<button id="message-button" class="logo-button message-button" tabindex="0">OK</button>
|
||||
</div>
|
||||
<div id="titlebar" class="titlebar">
|
||||
<svg style="position: absolute; width: 0px; height: 0px; overflow: hidden;" aria-hidden="true">
|
||||
<symbol id="icon-arrow-right" viewBox="0 0 1024 1024">
|
||||
<path d="M698.75712 565.02272l-191.488 225.4848a81.73568 81.73568 0 0 1-62.48448 28.89728 81.89952 81.89952 0 0 1-62.40256-134.94272l146.432-172.4416-146.432-172.4416a81.92 81.92 0 0 1 124.88704-106.06592l191.488 225.4848a81.87904 81.87904 0 0 1 0 106.02496z"></path>
|
||||
</symbol>
|
||||
</svg>
|
||||
<div id="titlebar-content" class="titlebar-content">
|
||||
<span id="titlebar-content-text" class="titlebar-content-text"></span>
|
||||
</div>
|
||||
<div id="titlebar-control-box" class="titlebar-control-box">
|
||||
<div id="titlebar-close" class="titlebar-button titlebar-button-close" title="Close">
|
||||
<svg class="titlebar-icon" aria-hidden="true">
|
||||
<path d="M 0,0 0,0.7 4.3,5 0,9.3 0,10 0.7,10 5,5.7 9.3,10 10,10 10,9.3 5.7,5 10,0.7 10,0 9.3,0 5,4.3 0.7,0 Z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="titlebar-toggle" class="titlebar-button" title="Maximize">
|
||||
<svg id="titlebar-maximize" class="titlebar-icon" aria-hidden="true" style="position: absolute;">
|
||||
<path d="M 0,0 0,10 10,10 10,0 Z M 1,1 9,1 9,9 1,9 Z"></path>
|
||||
</svg>
|
||||
<svg id="titlebar-restore" class="titlebar-icon" aria-hidden="true" style="position: absolute;">
|
||||
<path d="m 2,1e-5 0,2 -2,0 0,8 8,0 0,-2 2,0 0,-8 z m 1,1 6,0 0,6 -1,0 0,-5 -5,0 z m -2,2 6,0 0,6 -6,0 z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="titlebar-minimize" class="titlebar-button" title="Minimize">
|
||||
<svg class="titlebar-icon" aria-hidden="true">
|
||||
<path d="M 0,5 10,5 10,6 0,6 Z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="menu" class="menu"></div>
|
||||
<div id="menu-button" class="menu-button">≡</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,118 @@
|
||||
window.exports = {};
|
||||
|
||||
window.exports.require = function(id, callback) {
|
||||
if (!/^[a-zA-Z0-9_-]+$/.test(id)) {
|
||||
throw new Error("Invalid module '" + id + "'.");
|
||||
}
|
||||
var base = window.location.href || '';
|
||||
base = base.split('?')[0].split('#')[0];
|
||||
const index = base.lastIndexOf('/');
|
||||
base = index > 0 ? base.substring(0, index + 1) : base;
|
||||
base = base.lastIndexOf('/') === base.length - 1 ? base : base + '/';
|
||||
var url = base + id + '.js';
|
||||
var document = window.document;
|
||||
var scripts = document.head.getElementsByTagName('script');
|
||||
for (var i = 0; i < scripts.length; i++) {
|
||||
if (url === scripts[i].getAttribute('src')) {
|
||||
throw new Error("Duplicate import of '" + url + "'.");
|
||||
}
|
||||
}
|
||||
var script = document.createElement('script');
|
||||
script.setAttribute('id', id);
|
||||
script.setAttribute('type', 'module');
|
||||
/* eslint-disable no-use-before-define */
|
||||
var loadHandler = function() {
|
||||
script.removeEventListener('load', loadHandler);
|
||||
script.removeEventListener('error', errorHandler);
|
||||
callback();
|
||||
};
|
||||
var errorHandler = function(e) {
|
||||
script.removeEventListener('load', loadHandler);
|
||||
script.removeEventListener('error', errorHandler);
|
||||
callback(null, new Error("The script '" + e.target.src + "' failed to load."));
|
||||
};
|
||||
/* eslint-enable no-use-before-define */
|
||||
script.addEventListener('load', loadHandler, false);
|
||||
script.addEventListener('error', errorHandler, false);
|
||||
script.setAttribute('src', url);
|
||||
document.head.appendChild(script);
|
||||
};
|
||||
|
||||
window.exports.preload = function(callback) {
|
||||
var modules = [
|
||||
['view'],
|
||||
['json', 'xml', 'protobuf', 'hdf5', 'grapher', 'browser'],
|
||||
['base', 'text', 'flatbuffers', 'flexbuffers', 'zip', 'tar', 'python']
|
||||
];
|
||||
var next = function() {
|
||||
if (modules.length === 0) {
|
||||
callback();
|
||||
} else {
|
||||
var ids = modules.pop();
|
||||
/* eslint-disable no-loop-func */
|
||||
var resolved = ids.length;
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
window.exports.require(ids[i], function(module, error) {
|
||||
if (error) {
|
||||
callback(null, error);
|
||||
} else {
|
||||
resolved--;
|
||||
if (resolved === 0) {
|
||||
next();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
/* eslint-enable no-loop-func */
|
||||
}
|
||||
};
|
||||
next();
|
||||
};
|
||||
|
||||
window.exports.terminate = function(message) {
|
||||
var document = window.document;
|
||||
document.getElementById('message-text').innerText = message;
|
||||
var button = document.getElementById('message-button');
|
||||
button.style.display = 'none';
|
||||
button.onclick = null;
|
||||
document.body.setAttribute('class', 'welcome message');
|
||||
if (window.__view__) {
|
||||
/* eslint-disable no-unused-vars */
|
||||
try {
|
||||
window.__view__.show('welcome message');
|
||||
} catch (error) {
|
||||
// continue regardless of error
|
||||
}
|
||||
/* eslint-enable no-unused-vars */
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('error', function (event) {
|
||||
var error = event instanceof window.ErrorEvent && event.error && event.error instanceof Error ? event.error : new Error(event && event.message ? event.message : JSON.stringify(event));
|
||||
window.exports.terminate(error.message);
|
||||
});
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
if (typeof Symbol !== 'function' || typeof Symbol.asyncIterator !== 'symbol' ||
|
||||
typeof BigInt !== 'function' || typeof BigInt.asIntN !== 'function' || typeof BigInt.asUintN !== 'function' || typeof DataView.prototype.getBigInt64 !== 'function') {
|
||||
throw new Error('Please update your browser to use this application.');
|
||||
}
|
||||
var ua = window.navigator.userAgent;
|
||||
var chrome = ua.match(/Chrom(e|ium)\/([0-9]+)\./);
|
||||
var safari = ua.match(/Version\/(\d+)\.(\d+).*Safari/);
|
||||
var firefox = ua.match(/Firefox\/([0-9]+)\./);
|
||||
if ((Array.isArray(chrome) && parseInt(chrome[2], 10) < 86) ||
|
||||
(Array.isArray(safari) && (parseInt(safari[1], 10) < 16 || (parseInt(safari[1], 10) === 16 && parseInt(safari[2], 10) < 4))) ||
|
||||
(Array.isArray(firefox) && parseInt(firefox[1], 10) < 114)) {
|
||||
throw new Error('Please update your browser to use this application.');
|
||||
}
|
||||
window.exports.preload(function(value, error) {
|
||||
if (error) {
|
||||
window.exports.terminate(error.message);
|
||||
} else {
|
||||
var host = new window.exports.browser.Host();
|
||||
window.__view__ = new window.exports.view.View(host);
|
||||
window.__view__.start();
|
||||
}
|
||||
});
|
||||
});
|
||||