Compare commits
41 Commits
v0.1.0
...
d1e3a1760a
| Author | SHA1 | Date | |
|---|---|---|---|
| d1e3a1760a | |||
| 23a2772468 | |||
| 671813958b | |||
| 66b135d21a | |||
| e1102d0baf | |||
| 184ff0301f | |||
| 59f79fcfa8 | |||
| 01e8b73677 | |||
| cf4e9f7c3a | |||
| 619211e827 | |||
| 75c54d6740 | |||
| 7ad1f80a0c | |||
| ef6ca1e3e6 | |||
| e71429dce9 | |||
| f1ad49e255 | |||
| 9dca8012e3 | |||
| e03920255e | |||
| 49ce2450a0 | |||
| 1d7d8ca0d8 | |||
| c805be725d | |||
| b4864aa5ff | |||
| c599e0bd04 | |||
| 19dd8ac908 | |||
| 70153494bf | |||
| e7a8c43a74 | |||
| 484dff54c7 | |||
| 056d835b7c | |||
| fb81ebed83 | |||
| 34b5816eae | |||
| 693078768e | |||
| d17dfd062a | |||
| 5e6f118a13 | |||
| d104ba82c5 | |||
| e2edbce407 | |||
| 433e57ba93 | |||
| 0cc9ad385f | |||
| 3da3b9c1bb | |||
| ff3a13826a | |||
| 6e6fc4e7a5 | |||
| fd3123cf17 | |||
| ed048d0e83 |
1
.gitignore
vendored
@@ -33,6 +33,7 @@ node_modules/
|
||||
# Misc
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
build_notes.md
|
||||
|
||||
# Cordova
|
||||
cordova/platforms/
|
||||
|
||||
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 48 KiB |
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<widget id="edu.midwestern.alvinn" version="0.1.0-a" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||
<widget id="edu.midwestern.alvinn" version="0.1.2" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<name>ALVINN</name>
|
||||
<description>Anatomy Lab Visual Identification Neural Network.</description>
|
||||
<author email="jgeorg@midwestern.edu" href="https://midwestern.edu">
|
||||
@@ -11,20 +11,19 @@
|
||||
|
||||
<allow-navigation href="*" />
|
||||
|
||||
<access origin="*" />
|
||||
|
||||
<platform name="android">
|
||||
<edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application">
|
||||
<application android:usesCleartextTraffic="true" />
|
||||
</edit-config>
|
||||
<preference name="StatusBarOverlaysWebView" value="false" />
|
||||
<preference name="android-minSdkVersion" value="22" />
|
||||
<preference name="SplashMaintainAspectRatio" value="true" />
|
||||
<splash density="land-hdpi" src="res/screen/android/drawable-hdpi/screen.png" />
|
||||
<splash density="land-mdpi" src="res/screen/android/drawable-mdpi/screen.png" />
|
||||
<splash density="land-xhdpi" src="res/screen/android/drawable-xhdpi/screen.png" />
|
||||
<splash density="land-xxhdpi" src="res/screen/android/drawable-xxhdpi/screen.png" />
|
||||
<splash density="land-xxxhdpi" src="res/screen/android/drawable-xxxhdpi/screen.png" />
|
||||
<splash density="port-hdpi" src="res/screen/android/drawable-hdpi/screen.png" />
|
||||
<splash density="port-mdpi" src="res/screen/android/drawable-mdpi/screen.png" />
|
||||
<splash density="port-xhdpi" src="res/screen/android/drawable-xhdpi/screen.png" />
|
||||
<splash density="port-xxhdpi" src="res/screen/android/drawable-xxhdpi/screen.png" />
|
||||
<splash density="port-xxxhdpi" src="res/screen/android/drawable-xxxhdpi/screen.png" />
|
||||
<preference name="AndroidWindowSplashScreenAnimatedIcon" value="res/screen/android/splashscreen.xml" />
|
||||
<preference name="AndroidWindowSplashScreenBackground" value="#0f206c" />
|
||||
<preference name="AndroidInsecureFileModeEnabled" value="true" />
|
||||
<preference name="hostname" value="localhost" />
|
||||
<icon density="ldpi" src="res/icon/android/mipmap-ldpi/ic_launcher.png" />
|
||||
<icon density="mdpi" src="res/icon/android/mipmap-mdpi/ic_launcher.png" />
|
||||
<icon density="hdpi" src="res/icon/android/mipmap-hdpi/ic_launcher.png" />
|
||||
|
||||
1683
cordova/package-lock.json
generated
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "edu.midwestern.alvinn",
|
||||
"displayName": "ALVINN",
|
||||
"version": "0.1.0-a",
|
||||
"version": "0.1.0-b",
|
||||
"description": "Anatomy Lab Visual Identification Neural Network.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
@@ -14,19 +14,23 @@
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"cordova-android": "^12.0.1",
|
||||
"cordova-browser": "^7.0.0",
|
||||
"cordova-ios": "^7.0.1",
|
||||
"cordova-plugin-camera": "^7.0.0",
|
||||
"cordova-plugin-keyboard": "^1.2.0",
|
||||
"cordova-plugin-splashscreen": "^6.0.2",
|
||||
"cordova-plugin-statusbar": "^4.0.0"
|
||||
},
|
||||
"cordova": {
|
||||
"plugins": {
|
||||
"cordova-plugin-statusbar": {},
|
||||
"cordova-plugin-keyboard": {},
|
||||
"cordova-plugin-splashscreen": {}
|
||||
"cordova-plugin-camera": {
|
||||
"ANDROIDX_CORE_VERSION": "1.6.+"
|
||||
}
|
||||
},
|
||||
"platforms": [
|
||||
"ios",
|
||||
"browser",
|
||||
"android"
|
||||
]
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 36 KiB |
19
cordova/res/screen/android/splashscreen.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:pathData="m54,25.98 l-8.34,20.77c0,0 -9.99,4.08 -11.08,6.7 0,1.34 6.61,4.56 6.61,4.56L34.24,75.73H25.3L49.22,18.33h9.61L82.7,75.73H73.89L66.78,58.01c0,0 6.66,-3.22 6.66,-4.56 -1.11,-2.67 -11.12,-6.7 -11.12,-6.7z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="m49.21,46.17c-2.35,0.97 -13.28,5.91 -13.28,7.24 0,1.33 8.58,5.74 13.28,7.24 -2.62,-1.47 -4.25,-4.24 -4.25,-7.24 0,-3 1.62,-5.77 4.25,-7.24zM58.8,46.17c2.62,1.48 4.24,4.24 4.24,7.24 -0,3 -1.63,5.77 -4.25,7.24 2.36,-0.97 13.29,-5.91 13.29,-7.24 0,-1.33 -8.57,-5.74 -13.27,-7.24z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="m54,45.9c-1.16,0 -2.48,0.26 -3.86,0.69a7.85,7.82 0,0 0,-4 6.81,7.85 7.82,0 0,0 4,6.81c1.38,0.43 2.7,0.69 3.86,0.69 1.16,0 2.48,-0.26 3.86,-0.69a7.85,7.82 0,0 0,4 -6.81,7.85 7.82,0 0,0 -3.98,-6.8 7.85,7.82 0,0 0,-0 -0c-1.38,-0.43 -2.71,-0.69 -3.87,-0.69z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M50.81,53.41a3.19,3.19 0,1 0,6.38 0a3.19,3.19 0,1 0,-6.38 0z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
||||
@@ -25,7 +25,7 @@
|
||||
},
|
||||
"theming": {
|
||||
"customColor": true,
|
||||
"color": "#002f65",
|
||||
"color": "#385cad",
|
||||
"darkMode": false,
|
||||
"iconFonts": true
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "alvinn",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"version": "0.1.1",
|
||||
"description": "ALVINN",
|
||||
"repository": "",
|
||||
"license": "UNLICENSED",
|
||||
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="#002F65" height="24" viewBox="0 -960 960 960" width="24"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm40-80h480L570-480 450-320l-90-120-120 160Zm-40 80v-560 560Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 323 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="#002F65" height="24" viewBox="0 -960 960 960" width="24"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h480q33 0 56.5 23.5T720-720v180l160-160v440L720-420v180q0 33-23.5 56.5T640-160H160Zm0-80h480v-480H160v480Zm0 0v-480 480Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 300 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="#002F65" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-320q75 0 127.5-52.5T660-500q0-75-52.5-127.5T480-680q-75 0-127.5 52.5T300-500q0 75 52.5 127.5T480-320Zm0-72q-45 0-76.5-31.5T372-500q0-45 31.5-76.5T480-608q45 0 76.5 31.5T588-500q0 45-31.5 76.5T480-392Zm0 192q-146 0-266-81.5T40-500q54-137 174-218.5T480-800q146 0 266 81.5T920-500q-54 137-174 218.5T480-200Zm0-300Zm0 220q113 0 207.5-59.5T832-500q-50-101-144.5-160.5T480-720q-113 0-207.5 59.5T128-500q50 101 144.5 160.5T480-280Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 550 B |
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg width="100" height="100" version="1.1" viewBox="0 0 26.458333 26.458333" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="#002f65" stroke-width=".264583px">
|
||||
<path d="m25.402178 7.8631343c-0.487907-0.3670601-0.811572-0.7261214-1.573424-1.106523-0.006122-0.1598737 0.053853-0.2411643-0.072374-0.5438299-0.239221-0.3572156-1.352454-0.987126-2.19723-0.8590224-1.567124 0.9252583-1.879175 1.9380345-3.311246 2.9148849-0.987966 0.103956-2.015535 0.3206455-3.091153 0.6741123-10.556415-1.8721062-8.2481554 5.9196998-14.460584 1.7189868 0 0-0.24989902 0.06545-0.28777276 0.170279-0.0360567 0.0998 0.10708587 0.299783 0.10708587 0.299783 2.0948939 1.933517 4.742145 1.471155 6.6624536-0.07316 0.096935 0.768305 0.3887649 1.92789 0.8180324 3.363404-0.035692 1.245357-1.2923422 2.350278-1.3169003 2.801484-0.013354 0.24535 0.5120291 3.6149 0.7015429 3.650219l0.7793046 0.145235c0.8989154 0.167526 0.7195768-0.420583 0.3224789-0.780361-0.2085791-0.188976-0.3404558-0.252396-0.3637846-0.441707-0.3810495-3.092169 2.1284358-4.423261 2.4023638-6.742929 2.453391 0.120243 3.974486 1.282365 6.721539 1.403033 0.136906 1.035362-0.177341 4.099457-0.120257 4.484465 0.04824 0.325337 0.511082 0.918401 0.497537 1.876854-3e-3 0.211416 0.410117 0.159484 0.619918 0.185743 0.799059 0.09999 1.033405-0.329373 0.42557-0.75884-0.132327-0.0935-0.456134-0.264276-0.476806-0.424973-0.251045-1.951541 1.103782-4.917365 1.103782-4.917365 0.355435-0.554509 0.707693-1.135262 1.002776-2.188396 0.160636-0.543413 0.157772-1.012576 0.119972-1.465872 1.541867-1.5721797 1.123352-2.3466703 2.548492-2.7336036 0.65786 0.059985 1.147615 0.1738285 1.444935 0.3493259 0.420933-0.188852 0.760222-0.5096057 0.993749-1.001227z" fill="none"/>
|
||||
<path d="m15.156751 8.9427562c-10.556415-1.8721062-8.2481554 5.9196998-14.460584 1.7189868 0 0-0.24989902 0.06545-0.28777276 0.170279-0.0360567 0.0998 0.10708587 0.299783 0.10708587 0.299783 2.0948939 1.933517 4.742145 1.471155 6.6624536-0.07316 0.048468 0.384152 0.1456587 0.866125 0.2843915 1.431499 0.7210773 0.130029 2.5390772 0.501293 3.0586462 0.563846 0.613348 0.03006 1.528237 0.20676 2.05877 0.334503 0.563462-1.044613 0.536275-0.982536 2.57701-4.4457368z" fill="#002f65" fill-rule="evenodd"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.3 KiB |
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg width="100" height="100" version="1.1" viewBox="0 0 26.458333 26.458333" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="#002f65" stroke-width=".264583px">
|
||||
<path d="m25.402178 7.8631343c-0.487907-0.3670601-0.811572-0.7261214-1.573424-1.106523-0.006122-0.1598737 0.053853-0.2411643-0.072374-0.5438299-0.239221-0.3572156-1.352454-0.987126-2.19723-0.8590224-1.567124 0.9252583-1.879175 1.9380345-3.311246 2.9148849-0.987966 0.103956-2.015535 0.3206455-3.091153 0.6741123-10.556415-1.8721062-8.2481554 5.9196998-14.460584 1.7189868 0 0-0.24989902 0.06545-0.28777276 0.170279-0.0360567 0.0998 0.10708587 0.299783 0.10708587 0.299783 2.0948939 1.933517 4.742145 1.471155 6.6624536-0.07316 0.096935 0.768305 0.3887649 1.92789 0.8180324 3.363404-0.035692 1.245357-1.2923422 2.350278-1.3169003 2.801484-0.013354 0.24535 0.5120291 3.6149 0.7015429 3.650219l0.7793046 0.145235c0.8989154 0.167526 0.7195768-0.420583 0.3224789-0.780361-0.2085791-0.188976-0.3404558-0.252396-0.3637846-0.441707-0.3810495-3.092169 2.1284358-4.423261 2.4023638-6.742929 2.453391 0.120243 3.974486 1.282365 6.721539 1.403033 0.136906 1.035362-0.177341 4.099457-0.120257 4.484465 0.04824 0.325337 0.511082 0.918401 0.497537 1.876854-3e-3 0.211416 0.410117 0.159484 0.619918 0.185743 0.799059 0.09999 1.033405-0.329373 0.42557-0.75884-0.132327-0.0935-0.456134-0.264276-0.476806-0.424973-0.251045-1.951541 1.103782-4.917365 1.103782-4.917365 0.355435-0.554509 0.707693-1.135262 1.002776-2.188396 0.160636-0.543413 0.157772-1.012576 0.119972-1.465872 1.541867-1.5721797 1.123352-2.3466703 2.548492-2.7336036 0.65786 0.059985 1.147615 0.1738285 1.444935 0.3493259 0.420933-0.188852 0.760222-0.5096057 0.993749-1.001227z" fill="none" stop-color="#000000" style="-inkscape-stroke:none;font-variation-settings:normal"/>
|
||||
<path d="m25.402178 7.8631343c-0.487907-0.3670601-0.811572-0.7261214-1.573424-1.106523-0.006122-0.1598737 0.053853-0.2411643-0.072374-0.5438299-0.239221-0.3572156-1.352454-0.987126-2.19723-0.8590224-1.567124 0.9252583-1.879175 1.9380345-3.311246 2.9148849 0.566485 0.8398567 1.254642 1.7575311 2.167098 2.9799951 1.541867-1.5721797 1.123352-2.3466703 2.548492-2.7336036 0.65786 0.059985 1.147615 0.1738285 1.444935 0.3493259 0.420933-0.188852 0.760222-0.5096057 0.993749-1.001227z" fill="#002f65" fill-rule="evenodd"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.4 KiB |
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg width="100" height="100" version="1.1" viewBox="0 0 26.458333 26.458333" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="#002f65" stroke-width=".264583px">
|
||||
<path d="m25.402178 7.8631343c-0.487907-0.3670601-0.811572-0.7261214-1.573424-1.106523-0.006122-0.1598737 0.053853-0.2411643-0.072374-0.5438299-0.239221-0.3572156-1.352454-0.987126-2.19723-0.8590224-1.567124 0.9252583-1.879175 1.9380345-3.311246 2.9148849-0.987966 0.103956-2.015535 0.3206455-3.091153 0.6741123-10.556415-1.8721062-8.2481554 5.9196998-14.460584 1.7189868 0 0-0.24989902 0.06545-0.28777276 0.170279-0.0360567 0.0998 0.10708587 0.299783 0.10708587 0.299783 2.0948939 1.933517 4.742145 1.471155 6.6624536-0.07316 0.096935 0.768305 0.3887649 1.92789 0.8180324 3.363404-0.035692 1.245357-1.2923422 2.350278-1.3169003 2.801484-0.013354 0.24535 0.5120291 3.6149 0.7015429 3.650219l0.7793046 0.145235c0.8989154 0.167526 0.7195768-0.420583 0.3224789-0.780361-0.2085791-0.188976-0.3404558-0.252396-0.3637846-0.441707-0.3810495-3.092169 2.1284358-4.423261 2.4023638-6.742929 2.453391 0.120243 3.974486 1.282365 6.721539 1.403033 0.136906 1.035362-0.177341 4.099457-0.120257 4.484465 0.04824 0.325337 0.511082 0.918401 0.497537 1.876854-3e-3 0.211416 0.410117 0.159484 0.619918 0.185743 0.799059 0.09999 1.033405-0.329373 0.42557-0.75884-0.132327-0.0935-0.456134-0.264276-0.476806-0.424973-0.251045-1.951541 1.103782-4.917365 1.103782-4.917365 0.355435-0.554509 0.707693-1.135262 1.002776-2.188396 0.160636-0.543413 0.157772-1.012576 0.119972-1.465872 1.541867-1.5721797 1.123352-2.3466703 2.548492-2.7336036 0.65786 0.059985 1.147615 0.1738285 1.444935 0.3493259 0.420933-0.188852 0.760222-0.5096057 0.993749-1.001227z" fill="none" stop-color="#000000" style="-inkscape-stroke:none;font-variation-settings:normal"/>
|
||||
<path d="m17.24251 14.457023c0.136906 1.035362-0.177341 4.099457-0.120257 4.484465 0.04824 0.325337 0.511082 0.918401 0.497537 1.876854-3e-3 0.211416 0.410117 0.159484 0.619918 0.185743 0.799059 0.09999 1.033405-0.329373 0.42557-0.75884-0.132327-0.0935-0.456134-0.264276-0.476806-0.424973-0.251045-1.951541 1.103782-4.917365 1.103782-4.917365 0.355435-0.554509 0.707693-1.135262 1.002776-2.188396 0.160636-0.543413 0.157772-1.012576 0.119972-1.465872-3.100189-4.8581326-4.866767-0.394712-3.172492 3.208384z" fill="#002f65" fill-rule="evenodd" stop-color="#000000" style="-inkscape-stroke:none;font-variation-settings:normal"/>
|
||||
<path d="m7.1779333 11.058645c0.096935 0.768305 0.3887649 1.92789 0.8180324 3.363404-0.035692 1.245357-1.2923422 2.350278-1.3169003 2.801484-0.013354 0.24535 0.5120291 3.6149 0.7015429 3.650219l0.7793046 0.145235c0.8989154 0.167526 0.7195768-0.420583 0.3224789-0.780361-0.2085791-0.188976-0.3404558-0.252396-0.3637846-0.441707-0.3810495-3.092169 2.1284358-4.423261 2.4023638-6.742929 2.1562-5.4517681-2.8350883-3.4878487-3.3430377-1.995345z" fill="#002f65" fill-rule="evenodd" stop-color="#000000" style="-inkscape-stroke:none;font-variation-settings:normal"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.0 KiB |
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg width="100" height="100" version="1.1" viewBox="0 0 26.458333 26.458333" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="#002F65" stroke-width=".264583px">
|
||||
<path d="m25.402178 7.8631343c-0.487907-0.3670601-0.811572-0.7261214-1.573424-1.106523-0.006122-0.1598737 0.053853-0.2411643-0.072374-0.5438299-0.239221-0.3572156-1.352454-0.987126-2.19723-0.8590224-1.567124 0.9252583-1.879175 1.9380345-3.311246 2.9148849-0.987966 0.103956-2.015535 0.3206455-3.091153 0.6741123-10.556415-1.8721062-8.2481554 5.9196998-14.460584 1.7189868 0 0-0.24989902 0.06545-0.28777276 0.170279-0.0360567 0.0998 0.10708587 0.299783 0.10708587 0.299783 2.0948939 1.933517 4.742145 1.471155 6.6624536-0.07316 0.096935 0.768305 0.3887649 1.92789 0.8180324 3.363404-0.035692 1.245357-1.2923422 2.350278-1.3169003 2.801484-0.013354 0.24535 0.5120291 3.6149 0.7015429 3.650219l0.7793046 0.145235c0.8989154 0.167526 0.7195768-0.420583 0.3224789-0.780361-0.2085791-0.188976-0.3404558-0.252396-0.3637846-0.441707-0.3810495-3.092169 2.1284358-4.423261 2.4023638-6.742929 2.453391 0.120243 3.974486 1.282365 6.721539 1.403033 0.136906 1.035362-0.177341 4.099457-0.120257 4.484465 0.04824 0.325337 0.511082 0.918401 0.497537 1.876854-3e-3 0.211416 0.410117 0.159484 0.619918 0.185743 0.799059 0.09999 1.033405-0.329373 0.42557-0.75884-0.132327-0.0935-0.456134-0.264276-0.476806-0.424973-0.251045-1.951541 1.103782-4.917365 1.103782-4.917365 0.355435-0.554509 0.707693-1.135262 1.002776-2.188396 0.160636-0.543413 0.157772-1.012576 0.119972-1.465872 1.541867-1.5721797 1.123352-2.3466703 2.548492-2.7336036 0.65786 0.059985 1.147615 0.1738285 1.444935 0.3493259 0.420933-0.188852 0.760222-0.5096057 0.993749-1.001227z" fill="none" />
|
||||
<path d="m18.247904 8.2686439c-0.987966 0.103956-3.091153 0.6741123-3.091153 0.6741123-1.652395 2.7995828-2.197177 3.7434008-2.550516 4.3811848 0 0 3.039112 0.665488 4.636275 1.133082-3e-3 0.01385 2.049744 0.445884 2.049744 0.445884s0.707693-1.135262 1.002776-2.188396c0.160636-0.543413 0.157772-1.012576 0.119972-1.465872-0.291029-0.377705-1.38593-1.9038754-2.167098-2.9799951z" fill-rule="evenodd" fill="#002F65" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.2 KiB |
@@ -1,8 +1,7 @@
|
||||
<template>
|
||||
<f7-app v-bind="f7params">
|
||||
|
||||
<!-- Left panel with cover effect-->
|
||||
<f7-panel left cover dark>
|
||||
<f7-panel left cover>
|
||||
<f7-view>
|
||||
<f7-page>
|
||||
<f7-navbar title="ALVINN"></f7-navbar>
|
||||
@@ -10,33 +9,47 @@
|
||||
<f7-list-item link="/settings/" view=".view-main" panel-close=".panel-left">Settings</f7-list-item>
|
||||
<f7-list-item link="/about/">About ALVINN</f7-list-item>
|
||||
</f7-list>
|
||||
<f7-toolbar class="panel-bar" position="bottom">
|
||||
<span>version 0.1.1</span>
|
||||
</f7-toolbar>
|
||||
</f7-page>
|
||||
</f7-view>
|
||||
</f7-panel>
|
||||
|
||||
<!-- Your main view, should have "view-main" class -->
|
||||
<f7-view main class="safe-areas" url="/"></f7-view>
|
||||
|
||||
<!-- Popup -->
|
||||
<f7-popover id="region-popover">
|
||||
<f7-segmented raised style="flex-wrap: wrap; flex-direction: column;">
|
||||
<f7-button style="height: auto; width: auto;" href="/detect/thorax/" popover-close="#region-popover">
|
||||
<img src="../assets/regions/thorax.svg" />
|
||||
</f7-button>
|
||||
<f7-button style="height: auto; width: auto;" href="/detect/abdomen/" popover-close="#region-popover">
|
||||
<img src="../assets/regions/abdpel.svg" />
|
||||
</f7-button>
|
||||
<f7-button style="height: auto; width: auto;" href="/detect/limbs/" popover-close="#region-popover">
|
||||
<img src="../assets/regions/limb.svg" />
|
||||
</f7-button>
|
||||
<f7-button style="height: auto; width: auto;" href="/detect/head/" popover-close="#region-popover">
|
||||
<img src="../assets/regions/headneck.svg" />
|
||||
</f7-button>
|
||||
</f7-segmented>
|
||||
</f7-popover>
|
||||
<f7-view main class="safe-areas" url="/">
|
||||
<f7-popup :opened="showDisclaimer"
|
||||
style="height: auto; text-align: center;"
|
||||
:close-by-backdrop-click="false"
|
||||
:close-on-escape="false"
|
||||
:swipe-to-close="false"
|
||||
>
|
||||
<f7-block-title large>
|
||||
IMPORTANT
|
||||
</f7-block-title>
|
||||
<f7-block>
|
||||
<h3>
|
||||
ALVINN is for educational purposes only. It may not be used for medical diagnosis, intervention, or treatment.
|
||||
</h3>
|
||||
<div style="display: flex; justify-content: space-around; flex-direction: row; align-items: center;">
|
||||
<span style="height: min-content;">
|
||||
<f7-checkbox v-model:checked="rememberAgreement"/> Don't show again
|
||||
</span>
|
||||
<f7-button text="I agree" fill @click="setAgreement" />
|
||||
</div>
|
||||
</f7-block>
|
||||
</f7-popup>
|
||||
</f7-view>
|
||||
|
||||
</f7-app>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.panel-bar > .toolbar-inner {
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { f7, f7ready } from 'framework7-vue';
|
||||
@@ -47,18 +60,76 @@
|
||||
import store from '../js/store';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
rememberAgreement: false,
|
||||
siteAgreement: false,
|
||||
showDisclaimer: true
|
||||
}
|
||||
},
|
||||
created () {
|
||||
var loadSiteSettings = localStorage.getItem('siteSettings')
|
||||
if (loadSiteSettings) {
|
||||
var loadedSettings = JSON.parse(loadSiteSettings)
|
||||
this.siteAgreement = loadedSettings.siteAgreement
|
||||
this.rememberAgreement = loadedSettings.rememberAgreement
|
||||
}
|
||||
if (this.siteAgreement && this.rememberAgreement) {
|
||||
this.showDisclaimer = false
|
||||
store().agree()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setAgreement () {
|
||||
this.siteAgreement = true
|
||||
store().agree()
|
||||
let newSettings = {
|
||||
siteAgreement: this.siteAgreement,
|
||||
rememberAgreement: this.rememberAgreement
|
||||
}
|
||||
let saveSiteSettings = new Promise(
|
||||
(saved,failed) => {
|
||||
try {
|
||||
localStorage.setItem('siteSettings',JSON.stringify(newSettings))
|
||||
saved()
|
||||
} catch {
|
||||
failed()
|
||||
}
|
||||
}
|
||||
)
|
||||
saveSiteSettings.then(
|
||||
() => {
|
||||
this.showDisclaimer = false
|
||||
},
|
||||
() => {
|
||||
var toast = f7.toast.create({
|
||||
text: 'ERROR: No settings saved',
|
||||
closeTimeout: 2000
|
||||
})
|
||||
toast.open()
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const device = getDevice();
|
||||
// Framework7 Parameters
|
||||
var loadThemeSettings = localStorage.getItem('themeSettings')
|
||||
if (loadThemeSettings) var themeSettings = JSON.parse(loadThemeSettings)
|
||||
try {
|
||||
if (themeSettings.darkMode.toString()) var darkTheme = themeSettings.darkMode
|
||||
} catch {
|
||||
var darkTheme = 'auto'
|
||||
}
|
||||
//provide('isAgreed',siteAgreement)
|
||||
const f7params = {
|
||||
name: 'ALVINN', // App name
|
||||
theme: 'auto', // Automatic theme detection
|
||||
darkMode: darkTheme,
|
||||
colors: {
|
||||
primary: '#002f65',
|
||||
},
|
||||
|
||||
// App store
|
||||
store: store,
|
||||
// App routes
|
||||
routes: routes,
|
||||
|
||||
@@ -99,7 +170,7 @@
|
||||
});
|
||||
|
||||
return {
|
||||
f7params
|
||||
f7params,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
32
src/components/region-icon.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<svg width="100%" height="100%" version="1.1" viewBox="0 0 26.458333 26.458333" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" :fill="fillColor" >
|
||||
<path d="m25.402178 7.8631343c-0.487907-0.3670601-0.811572-0.7261214-1.573424-1.106523-0.006122-0.1598737 0.053853-0.2411643-0.072374-0.5438299-0.239221-0.3572156-1.352454-0.987126-2.19723-0.8590224-1.567124 0.9252583-1.879175 1.9380345-3.311246 2.9148849-0.987966 0.103956-2.015535 0.3206455-3.091153 0.6741123-10.556415-1.8721062-8.2481554 5.9196998-14.460584 1.7189868 0 0-0.24989902 0.06545-0.28777276 0.170279-0.0360567 0.0998 0.10708587 0.299783 0.10708587 0.299783 2.0948939 1.933517 4.742145 1.74421 6.6624536-0.07316 0.096935 0.768305 0.3887649 1.92789 0.8180324 3.363404-0.035692 1.245357-1.2923422 2.350278-1.3169003 2.801484-0.013354 0.24535 0.5120291 3.6149 0.7015429 3.650219l0.7793046 0.145235c0.8989154 0.167526 0.7195768-0.420583 0.3224789-0.780361-0.2085791-0.188976-0.3404558-0.252396-0.3637846-0.441707-0.3810495-3.092169 2.1284358-4.423261 2.4023638-6.742929 2.453391 0.120243 3.974486 1.282365 6.721539 1.403033 0.136906 1.035362-0.177341 4.099457-0.120257 4.484465 0.04824 0.325337 0.511082 0.918401 0.497537 1.876854-3e-3 0.211416 0.410117 0.159484 0.619918 0.185743 0.799059 0.09999 1.033405-0.329373 0.42557-0.75884-0.132327-0.0935-0.456134-0.264276-0.476806-0.424973-0.251045-1.951541 1.103782-4.917365 1.103782-4.917365 0.355435-0.554509 0.707693-1.135262 1.002776-2.188396 0.160636-0.543413 0.157772-1.012576 0.119972-1.465872 1.541867-1.5721797 1.123352-2.3466703 2.548492-2.7336036 0.65786 0.059985 1.147615 0.1738285 1.444935 0.3493259 0.420933-0.188852 0.760222-0.5096057 0.993749-1.001227z" style="opacity: .25;"/>
|
||||
<path v-if="region == 0" d="m 18.247904,8.2686439 c -0.987966,0.103956 -3.091153,0.6741123 -3.091153,0.6741123 -1.652395,2.7995828 -2.226698,3.8098238 -2.580037,4.4476078 0,0 2.617397,0.984666 4.665796,1.066659 -0.003,0.01385 2.049744,0.445884 2.049744,0.445884 0,0 0.707693,-1.135262 1.002776,-2.188396 0.160636,-0.543413 0.157772,-1.012576 0.119972,-1.465872 -0.291029,-0.377705 -1.38593,-1.9038754 -2.167098,-2.9799951 z" fill-rule="evenodd" :fill="fillColor" />
|
||||
<path v-else-if="region ==1" d="m15.156751 8.9427562c-10.556415-1.8721062-8.2481554 5.9196998-14.460584 1.7189868 0 0-0.24989902 0.06545-0.28777276 0.170279-0.0360567 0.0998 0.10708587 0.299783 0.10708587 0.299783 2.0948939 1.933517 4.742145 1.74421 6.6624536-0.07316 0.048468 0.384152 0.1456587 0.866125 0.2843915 1.431499 0.7210773 0.130029 2.5390772 0.501293 3.0586462 0.563846 0.613348 0.03006 1.528237 0.20676 2.05877 0.334503 0.563462-1.044613 0.536275-0.982536 2.57701-4.4457368z" :fill="fillColor" fill-rule="evenodd"/>
|
||||
<g v-else-if="region == 2" :fill="fillColor" fill-rule="evenodd">
|
||||
<path d="m17.24251 14.457023c0.136906 1.035362-0.177341 4.099457-0.120257 4.484465 0.04824 0.325337 0.511082 0.918401 0.497537 1.876854-3e-3 0.211416 0.410117 0.159484 0.619918 0.185743 0.799059 0.09999 1.033405-0.329373 0.42557-0.75884-0.132327-0.0935-0.456134-0.264276-0.476806-0.424973-0.251045-1.951541 1.103782-4.917365 1.103782-4.917365 0.355435-0.554509 0.707693-1.135262 1.002776-2.188396 0.160636-0.543413 0.157772-1.012576 0.119972-1.465872-3.100189-4.8581326-4.866767-0.394712-3.172492 3.208384z" />
|
||||
<path d="m7.1779333 11.058645c0.096935 0.768305 0.3887649 1.92789 0.8180324 3.363404-0.035692 1.245357-1.2923422 2.350278-1.3169003 2.801484-0.013354 0.24535 0.5120291 3.6149 0.7015429 3.650219l0.7793046 0.145235c0.8989154 0.167526 0.7195768-0.420583 0.3224789-0.780361-0.2085791-0.188976-0.3404558-0.252396-0.3637846-0.441707-0.3810495-3.092169 2.1284358-4.423261 2.4023638-6.742929 2.1562-5.4517681-2.8350883-3.4878487-3.3430377-1.995345z" />
|
||||
</g>
|
||||
<path v-else-if="region == 3" d="m25.402178 7.8631343c-0.487907-0.3670601-0.811572-0.7261214-1.573424-1.106523-0.006122-0.1598737 0.053853-0.2411643-0.072374-0.5438299-0.239221-0.3572156-1.352454-0.987126-2.19723-0.8590224-1.567124 0.9252583-1.879175 1.9380345-3.311246 2.9148849 0.566485 0.8398567 1.254642 1.7575311 2.167098 2.9799951 1.541867-1.5721797 1.123352-2.3466703 2.548492-2.7336036 0.65786 0.059985 1.147615 0.1738285 1.444935 0.3493259 0.420933-0.188852 0.760222-0.5096057 0.993749-1.001227z" :fill="fillColor" fill-rule="evenodd"/>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "RegionIcon",
|
||||
props: {
|
||||
region: {
|
||||
type: Number,
|
||||
validator(value) {
|
||||
return value >= 0 && value <= 3
|
||||
}
|
||||
},
|
||||
fillColor: {
|
||||
type: String,
|
||||
default: "var(--avn-theme-color)"
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
44
src/components/svg-icon.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" :fill="fillColor" height="100%" viewBox="0 -960 960 960" width="100%">
|
||||
<path v-if="icon == 'image'" d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm40-80h480L570-480 450-320l-90-120-120 160Zm-40 80v-560 560Z"/>
|
||||
<path v-else-if="icon == 'videocam'" d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h480q33 0 56.5 23.5T720-720v180l160-160v440L720-420v180q0 33-23.5 56.5T640-160H160Zm0-80h480v-480H160v480Zm0 0v-480 480Z"/>
|
||||
<path v-else-if="icon == 'visibility'" d="M480-320q75 0 127.5-52.5T660-500q0-75-52.5-127.5T480-680q-75 0-127.5 52.5T300-500q0 75 52.5 127.5T480-320Zm0-72q-45 0-76.5-31.5T372-500q0-45 31.5-76.5T480-608q45 0 76.5 31.5T588-500q0 45-31.5 76.5T480-392Zm0 192q-146 0-266-81.5T40-500q54-137 174-218.5T480-800q146 0 266 81.5T920-500q-54 137-174 218.5T480-200Zm0-300Zm0 220q113 0 207.5-59.5T832-500q-50-101-144.5-160.5T480-720q-113 0-207.5 59.5T128-500q50 101 144.5 160.5T480-280Z"/>
|
||||
<path v-else-if="icon == 'photo_library'" d="M360-400h400L622-580l-92 120-62-80-108 140Zm-40 160q-33 0-56.5-23.5T240-320v-480q0-33 23.5-56.5T320-880h480q33 0 56.5 23.5T880-800v480q0 33-23.5 56.5T800-240H320Zm0-80h480v-480H320v480ZM160-80q-33 0-56.5-23.5T80-160v-560h80v560h560v80H160Zm160-720v480-480Z"/>
|
||||
<path v-else-if="icon == 'no_photography'" d="m880-195-80-80v-405H638l-73-80H395l-38 42-57-57 60-65h240l74 80h126q33 0 56.5 23.5T880-680v485Zm-720 75q-33 0-56.5-23.5T80-200v-480q0-33 23.5-56.5T160-760h41l80 80H160v480h601l80 80H160Zm466-215q-25 34-62.5 54.5T480-260q-75 0-127.5-52.5T300-440q0-46 20.5-83.5T375-586l58 58q-24 13-38.5 36T380-440q0 42 29 71t71 29q29 0 52-14.5t36-38.5l58 58Zm-18-233q25 24 38.5 57t13.5 71v12q0 6-1 12L456-619q6-1 12-1h12q38 0 71 13.5t57 38.5ZM819-28 27-820l57-57L876-85l-57 57ZM407-440Zm171-57Z"/>
|
||||
<path v-else-if="icon == 'photo_camera'" d="M480-260q75 0 127.5-52.5T660-440q0-75-52.5-127.5T480-620q-75 0-127.5 52.5T300-440q0 75 52.5 127.5T480-260Zm0-80q-42 0-71-29t-29-71q0-42 29-71t71-29q42 0 71 29t29 71q0 42-29 71t-71 29ZM160-120q-33 0-56.5-23.5T80-200v-480q0-33 23.5-56.5T160-760h126l74-80h240l74 80h126q33 0 56.5 23.5T880-680v480q0 33-23.5 56.5T800-120H160Zm0-80h640v-480H638l-73-80H395l-73 80H160v480Zm320-240Z"/>
|
||||
<path v-else-if="icon == 'cloud_upload'" d="M260-160q-91 0-155.5-63T40-377q0-78 47-139t123-78q25-92 100-149t170-57q117 0 198.5 81.5T760-520q69 8 114.5 59.5T920-340q0 75-52.5 127.5T740-160H520q-33 0-56.5-23.5T440-240v-206l-64 62-56-56 160-160 160 160-56 56-64-62v206h220q42 0 71-29t29-71q0-42-29-71t-71-29h-60v-80q0-83-58.5-141.5T480-720q-83 0-141.5 58.5T280-520h-20q-58 0-99 41t-41 99q0 58 41 99t99 41h100v80H260Zm220-280Z"/>
|
||||
<path v-else-if="icon == 'cloud_done'" d="m414-280 226-226-58-58-169 169-84-84-57 57 142 142ZM260-160q-91 0-155.5-63T40-377q0-78 47-139t123-78q25-92 100-149t170-57q117 0 198.5 81.5T760-520q69 8 114.5 59.5T920-340q0 75-52.5 127.5T740-160H260Zm0-80h480q42 0 71-29t29-71q0-42-29-71t-71-29h-60v-80q0-83-58.5-141.5T480-720q-83 0-141.5 58.5T280-520h-20q-58 0-99 41t-41 99q0 58 41 99t99 41Zm220-240Z"/>
|
||||
<path v-else-if="icon == 'check_list'" d="M655-200 513-342l56-56 85 85 170-170 56 57-225 226Zm0-320L513-662l56-56 85 85 170-170 56 57-225 226ZM80-280v-80h360v80H80Zm0-320v-80h360v80H80Z"/>
|
||||
<path v-else-if="icon == 'refresh_search'" d="M822-142 592-372q-32 26-71 39t-81 13q-42 0-80-12.5T290-368l58-58q20 12 43 19t49 7q75 0 127.5-52.5T620-580q0-75-52.5-127.5T440-760q-69 0-119.5 46.5T262-598l50-50 56 56-148 148L72-592l56-56 54 52q6-103 80-173.5T440-840q109 0 184.5 75.5T700-580q0 42-13 82t-39 70l230 230-56 56Z"/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "SvgIcon",
|
||||
props: {
|
||||
icon: {
|
||||
type: String,
|
||||
validator(value) {
|
||||
const iconList = [
|
||||
'image',
|
||||
'videocam',
|
||||
'visibility',
|
||||
'photo_library',
|
||||
'no_photography',
|
||||
'photo_camera',
|
||||
'cloud_upload',
|
||||
'cloud_done',
|
||||
'check_list',
|
||||
'refresh_search'
|
||||
]
|
||||
return iconList.includes(value)
|
||||
}
|
||||
},
|
||||
fillColor: {
|
||||
type: String,
|
||||
default: "var(--avn-theme-color)"
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -4,16 +4,45 @@
|
||||
}
|
||||
|
||||
/* Your app custom styles here */
|
||||
.view-main {
|
||||
--f7-theme-color: #002F65;
|
||||
html {
|
||||
--avn-theme-color: #0f206c;
|
||||
--avn-button-bg-color: #d9d8d6;
|
||||
--avn-structure-box-color: yellow;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--avn-theme-color: #d9c79e;
|
||||
--avn-button-bg-color: #0f206c;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Open Sans", sans-serif;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
--f7-navbar-text-color: #002F65;
|
||||
--f7-navbar-link-color: #002F65;
|
||||
--f7-navbar-text-color: var(--avn-theme-color);
|
||||
--f7-navbar-link-color: var(--avn-theme-color);
|
||||
}
|
||||
|
||||
.navbar-bg {
|
||||
--f7-navbar-bg-color: #BDBCAF;
|
||||
}
|
||||
--f7-navbar-bg-color: #d9c79e;
|
||||
}
|
||||
|
||||
.dark .navbar-bg {
|
||||
--f7-navbar-bg-color: #0f206c;
|
||||
}
|
||||
|
||||
/* MWU Colors
|
||||
Rich blue: #385cad
|
||||
Dark blue: #0f206c
|
||||
Light gold: #d9c79e
|
||||
Dark gold: #bd9a5f
|
||||
|
||||
Secondary:
|
||||
White: #ffffff
|
||||
Black: #414042
|
||||
Dark Gray: #c8c8c8
|
||||
Light Gray: #d9d8d6
|
||||
Green: #009490
|
||||
Purple: #83276b
|
||||
*/
|
||||
@@ -23,6 +23,7 @@
|
||||
<link rel="apple-touch-icon" href="icons/apple-touch-icon.png">
|
||||
<link rel="icon" href="icons/favicon.png">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link href='https://fonts.googleapis.com/css?family=Open Sans' rel='stylesheet'>
|
||||
<% } %>
|
||||
<!-- built styles file will be auto injected -->
|
||||
</head>
|
||||
|
||||
@@ -4,7 +4,6 @@ import AboutPage from '../pages/about.vue';
|
||||
import SettingsPage from '../pages/settings.vue';
|
||||
import DetectPage from '../pages/detect.vue';
|
||||
|
||||
import DynamicRoutePage from '../pages/dynamic-route.vue';
|
||||
import NotFoundPage from '../pages/404.vue';
|
||||
|
||||
var routes = [
|
||||
@@ -24,12 +23,6 @@ var routes = [
|
||||
path: '/settings/',
|
||||
component: SettingsPage,
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
path: '/dynamic-route/blog/:blogId/post/:postId/',
|
||||
component: DynamicRoutePage,
|
||||
},
|
||||
{
|
||||
path: '(.*)',
|
||||
component: NotFoundPage,
|
||||
|
||||
@@ -1,35 +1,14 @@
|
||||
import { reactive, computed } from 'vue';
|
||||
|
||||
import { createStore } from 'framework7/lite';
|
||||
const state = reactive({
|
||||
disclaimerAgreement: false
|
||||
})
|
||||
|
||||
const store = createStore({
|
||||
state: {
|
||||
products: [
|
||||
{
|
||||
id: '1',
|
||||
title: 'Apple iPhone 8',
|
||||
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nisi tempora similique reiciendis, error nesciunt vero, blanditiis pariatur dolor, minima sed sapiente rerum, dolorem corrupti hic modi praesentium unde saepe perspiciatis.'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: 'Apple iPhone 8 Plus',
|
||||
description: 'Velit odit autem modi saepe ratione totam minus, aperiam, labore quia provident temporibus quasi est ut aliquid blanditiis beatae suscipit odio vel! Nostrum porro sunt sint eveniet maiores, dolorem itaque!'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: 'Apple iPhone X',
|
||||
description: 'Expedita sequi perferendis quod illum pariatur aliquam, alias laboriosam! Vero blanditiis placeat, mollitia necessitatibus reprehenderit. Labore dolores amet quos, accusamus earum asperiores officiis assumenda optio architecto quia neque, quae eum.'
|
||||
},
|
||||
]
|
||||
},
|
||||
getters: {
|
||||
products({ state }) {
|
||||
return state.products;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
addProduct({ state }, product) {
|
||||
state.products = [...state.products, product];
|
||||
},
|
||||
},
|
||||
const agree = () => {
|
||||
state.disclaimerAgreement = true
|
||||
}
|
||||
|
||||
export default () => ({
|
||||
isAgreed: computed(() => state.disclaimerAgreement),
|
||||
agree
|
||||
})
|
||||
export default store;
|
||||
|
||||
@@ -1,177 +1,539 @@
|
||||
<template>
|
||||
<f7-page name="detect">
|
||||
<!-- Top Navbar -->
|
||||
<f7-navbar :sliding="false" back-link="Back">
|
||||
<f7-nav-title sliding>{{ regions[activeRegion] }}</f7-nav-title>
|
||||
</f7-navbar>
|
||||
<f7-block style="display: flex; justify-content: flex-start; flex-direction: column; align-items: stretch; height: calc(100% - var(--f7-navbar-height) - var(--f7-safe-area-top) - var(--f7-safe-area-bottom)); box-sizing: border-box; width: 100vw;">
|
||||
<img :src="imageView" id="im-display" ref="image_src" style="flex: 1 1 0%; object-fit: contain; max-width: 100vw; min-height: 0;" />
|
||||
<div ref="structure_box" style="border: solid 3px yellow; position: absolute; display: none;" />
|
||||
<f7-segmented class="image-menu" raised style="margin: 5px; width: 50%; max-width: 400px; min-width: 192px; flex: 0 0 auto;">
|
||||
<f7-button popover-open="#region-popover">
|
||||
<img :src="imageRegion" style="height: 100%;" />
|
||||
</f7-button>
|
||||
<f7-button @click="setData" :class="(imageLoaded) ? '' : 'disabled'">
|
||||
<img src="../assets/icons/visibility.svg" style="height: 100%;" />
|
||||
</f7-button>
|
||||
<f7-button @click="selectImage">
|
||||
<img src="../assets/icons/image.svg" style="height: 100%;" />
|
||||
</f7-button>
|
||||
<f7-button @click="setData">
|
||||
<img src="../assets/icons/videocam.svg" style="height: 100%;" />
|
||||
</f7-button>
|
||||
</f7-segmented>
|
||||
<input type="file" ref="image_chooser" @change="getImage()" accept="image/*" style="display: none;"/>
|
||||
<div v-if="resultData && resultData.detections" class="chip-results" style="flex: 0 0 auto; align-self: center;">
|
||||
<f7-chip v-for="(result, idx) in resultData.detections" :class="(idx == selectedChip) ? 'selected-chip' : ''" :text="result.label" media=" " :tooltip="result.confidence.toFixed(1)" :media-bg-color="chipColor(result.confidence)" deleteable @click="selectChip(idx)" @delete="deleteChip(idx)" />
|
||||
</div>
|
||||
</f7-block>
|
||||
</f7-page>
|
||||
<f7-page name="detect" :id="detectorName + '-detect-page'">
|
||||
<!-- Top Navbar -->
|
||||
<f7-navbar :sliding="false" back-link="true" back-link-url="/" back-link-force>
|
||||
<f7-nav-title sliding>{{ regions[activeRegion] }}</f7-nav-title>
|
||||
</f7-navbar>
|
||||
<f7-block class="detect-grid">
|
||||
<div class="image-container">
|
||||
<canvas id="im-draw" ref="image_cvs" :style="`display: ${imageLoaded ? 'block' : 'none'}; flex: 1 1 0%; object-fit: contain; max-width: 100%; max-height: 100%; min-width: 0; min-height: 0; background-size: contain; background-position: center; background-repeat: no-repeat`" />
|
||||
<SvgIcon v-if="!imageView" icon="image" fill-color="var(--avn-theme-color)" @click="selectImage" />
|
||||
</div>
|
||||
<div v-if="(resultData && resultData.detections) || detecting" class="chip-results" style="grid-area: result-view; flex: 0 0 auto; align-self: center;">
|
||||
<f7-chip v-for="result in showResults.filter( r => { return r.aboveThreshold && r.isSearched && !r.isDeleted })"
|
||||
:class="(result.resultIndex == selectedChip) ? 'selected-chip' : ''"
|
||||
:text="result.label"
|
||||
media=" "
|
||||
:tooltip="result.confidence.toFixed(1)"
|
||||
deleteable
|
||||
@click="selectChip(result.resultIndex)"
|
||||
@delete="deleteChip(result.resultIndex)"
|
||||
:style="chipGradient(result.confidence)"
|
||||
/>
|
||||
<span v-if="numResults == 0 && !detecting" style="height: var(--f7-chip-height); font-size: calc(var(--f7-chip-height) - 4px); font-weight: bolder; margin: 2px;">No results.</span>
|
||||
<f7-preloader v-if="detecting" size="32" style="color: var(--avn-theme-color);" />
|
||||
</div>
|
||||
<div v-if="showDetectSettings" class="detect-inputs" style="grid-area: detect-settings;">
|
||||
<f7-range class="level-slide-horz" :min="0" :max="100" :step="1" @range:change="onLevelChange" v-model:value="detectorLevel" type="range" style="flex: 1 1 50px"/>
|
||||
<f7-range class="level-slide-vert" vertical :min="0" :max="100" :step="1" @range:change="onLevelChange" v-model:value="detectorLevel" type="range" style="flex: 1 1 50px"/>
|
||||
<f7-button @click="() => detectPanel = !detectPanel" :panel-open="!detectPanel && `#${detectorName}-settings`" :panel-close="detectPanel && `#${detectorName}-settings`">
|
||||
<SvgIcon icon="check_list"/>
|
||||
</f7-button>
|
||||
<f7-button @click="setData">
|
||||
<SvgIcon icon="refresh_search"/>
|
||||
</f7-button>
|
||||
</div>
|
||||
<f7-segmented class="image-menu" raised>
|
||||
<f7-button popover-open="#region-popover">
|
||||
<RegionIcon :region="activeRegion" />
|
||||
</f7-button>
|
||||
<f7-button popover-open="#capture-popover">
|
||||
<SvgIcon icon="image"/>
|
||||
</f7-button>
|
||||
<f7-button @click="() => showDetectSettings = !showDetectSettings" :class="(imageLoaded) ? '' : 'disabled'">
|
||||
<SvgIcon icon="visibility"/>
|
||||
</f7-button>
|
||||
<f7-button :class="(numResults && uploadDirty && viewedAll) ? '' : 'disabled'" @click="submitData">
|
||||
<SvgIcon :icon="(uploadUid) ? 'cloud_done' : 'cloud_upload'"/>
|
||||
</f7-button>
|
||||
</f7-segmented>
|
||||
<input type="file" ref="image_chooser" @change="getImage()" accept="image/*" capture="environment" style="display: none;"/>
|
||||
</f7-block>
|
||||
|
||||
<f7-panel :id="detectorName + '-settings'" right cover :backdrop="false" :container-el="`#${detectorName}-detect-page`">
|
||||
<f7-page>
|
||||
<f7-navbar title="Detection Settings"></f7-navbar>
|
||||
<f7-list>
|
||||
<f7-list-button title="Close List" @click="() => detectPanel = false" :panel-close="`#${detectorName}-settings`"></f7-list-button>
|
||||
<f7-list-item checkbox checked checkbox-icon="end" title="All/none" @change="selectAll"></f7-list-item>
|
||||
<f7-list-item v-for="structure in detectorLabels" :key="structure.name" checkbox checkbox-icon="end" v-model:checked="structure.detect" :title="structure.name"></f7-list-item>
|
||||
</f7-list>
|
||||
</f7-page>
|
||||
</f7-panel>
|
||||
|
||||
<f7-popover id="region-popover" class="popover-button-menu">
|
||||
<f7-segmented raised class="segment-button-menu">
|
||||
<f7-button style="height: auto; width: auto;" href="/detect/thorax/" popover-close="#region-popover">
|
||||
<RegionIcon :region="0" />
|
||||
</f7-button>
|
||||
<f7-button style="height: auto; width: auto;" href="/detect/abdomen/" popover-close="#region-popover">
|
||||
<RegionIcon :region="1" />
|
||||
</f7-button>
|
||||
<f7-button style="height: auto; width: auto;" href="/detect/limbs/" popover-close="#region-popover">
|
||||
<RegionIcon :region="2" />
|
||||
</f7-button>
|
||||
<f7-button class="disabled" style="height: auto; width: auto;" href="/detect/head/" popover-close="#region-popover">
|
||||
<RegionIcon :region="3" />
|
||||
</f7-button>
|
||||
</f7-segmented>
|
||||
</f7-popover>
|
||||
|
||||
<f7-popover id="capture-popover" class="popover-button-menu">
|
||||
<f7-segmented raised class="segment-button-menu">
|
||||
<f7-button style="height: auto; width: auto;" popover-close="#capture-popover" class="disabled" @click="videoStream">
|
||||
<SvgIcon icon="videocam"/>
|
||||
</f7-button>
|
||||
<f7-button style="height: auto; width: auto;" popover-close="#capture-popover" @click="selectImage('camera')">
|
||||
<SvgIcon icon="photo_camera" />
|
||||
</f7-button>
|
||||
<f7-button style="height: auto; width: auto;" popover-close="#capture-popover" @click="selectImage('file')">
|
||||
<SvgIcon icon="photo_library" />
|
||||
</f7-button>
|
||||
</f7-segmented>
|
||||
</f7-popover>
|
||||
|
||||
</f7-page>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.detect-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr auto auto min-content;
|
||||
grid-template-areas:
|
||||
"image-view"
|
||||
"result-view"
|
||||
"detect-settings"
|
||||
"menu-view";
|
||||
justify-items: center;
|
||||
height: calc(100% - var(--f7-navbar-height) - var(--f7-safe-area-top) - var(--f7-safe-area-bottom));
|
||||
max-height: calc(100% - var(--f7-navbar-height) - var(--f7-safe-area-top) - var(--f7-safe-area-bottom));
|
||||
}
|
||||
|
||||
.image-container {
|
||||
grid-area: image-view;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.popover-button-menu {
|
||||
max-width: 90vw;
|
||||
max-height: 90vh;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.segment-button-menu {
|
||||
flex-wrap: nowrap;
|
||||
flex-direction: column;
|
||||
max-height: 100%;
|
||||
min-height: 0px;
|
||||
}
|
||||
|
||||
.chip-media {
|
||||
background-color: var(--chip-media-background) !important;
|
||||
}
|
||||
|
||||
.chip-results {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
padding: 10px;
|
||||
--f7-chip-border-radius: 16px;
|
||||
--f7-chip-media-size: 32px;
|
||||
--f7-chip-font-weight: normal;
|
||||
}
|
||||
|
||||
.chip-results .chip {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.selected-chip {
|
||||
font-weight: 500;
|
||||
box-shadow: 4px 4px 1px var(--avn-theme-color);
|
||||
transform: translate(-2px, -2px);
|
||||
}
|
||||
|
||||
.detect-inputs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 5px;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
min-width: 192px;
|
||||
}
|
||||
|
||||
.level-slide-vert {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.image-menu {
|
||||
grid-area: menu-view;
|
||||
margin: 5px;
|
||||
/*width: 90%;*/
|
||||
max-width: 400px;
|
||||
min-width: 192px;
|
||||
}
|
||||
|
||||
.image-menu .button {
|
||||
aspect-ratio: 1;
|
||||
height: auto;
|
||||
padding: 5px;
|
||||
flex: 1 1 0%;
|
||||
}
|
||||
|
||||
.image-menu > .button > svg {
|
||||
aspect-ratio: 1;
|
||||
height: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.segment-button-menu .button {
|
||||
padding: 8px;
|
||||
aspect-ratio: 1;
|
||||
width: auto;
|
||||
flex: 1 1 0%;
|
||||
max-height: 100px;
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
.detect-grid {
|
||||
grid-template-columns: minmax(0,1fr) max-content auto auto;
|
||||
grid-template-rows: calc(100vh - var(--f7-navbar-height) - var(--f7-safe-area-top) - var(--f7-safe-area-bottom) - 64px);
|
||||
grid-template-areas:
|
||||
"image-view result-view detect-settings menu-view";
|
||||
justify-items: stretch;
|
||||
align-items: stretch;
|
||||
height: calc(100% - var(--f7-navbar-height) - var(--f7-safe-area-top) - var(--f7-safe-area-bottom));
|
||||
max-height: calc(100% - var(--f7-navbar-height) - var(--f7-safe-area-top) - var(--f7-safe-area-bottom));
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.chip-results {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
--f7-chip-border-radius: 16px;
|
||||
--f7-chip-media-size: 32px;
|
||||
--f7-chip-font-weight: normal;
|
||||
flex-direction: column;
|
||||
max-height: 100%;
|
||||
justify-self: start;
|
||||
flex-wrap: nowrap;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.detect-inputs {
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
max-width: 72px;
|
||||
}
|
||||
|
||||
.chip-results .chip {
|
||||
padding-left: 8px;
|
||||
.level-slide-horz {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.selected-chip {
|
||||
font-weight: 500;
|
||||
box-shadow: 4px 4px 1px var(--f7-theme-color);
|
||||
transform: translate(-2px, -2px);
|
||||
.level-slide-vert {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
.image-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.image-menu {
|
||||
flex-direction: column;
|
||||
aspect-ratio: .25;
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.image-menu .button {
|
||||
aspect-ratio: 1;
|
||||
height: auto;
|
||||
padding: 5px;
|
||||
aspect-ratio: 1;
|
||||
width: auto;
|
||||
height: 100%;
|
||||
flex: 1 1 0%;
|
||||
border-bottom: 1px solid var(--f7-segmented-raised-divider-color);
|
||||
border-bottom-left-radius: 0px !important;
|
||||
}
|
||||
|
||||
.segment-button-menu {
|
||||
flex-direction: row;
|
||||
max-height: 100%;
|
||||
min-height: 0px;
|
||||
}
|
||||
|
||||
.segment-button-menu .button {
|
||||
height: auto;
|
||||
flex: 1 1 0%;
|
||||
max-height: 100px;
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
.button > svg {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { f7 } from 'framework7-vue'
|
||||
import { f7 } from 'framework7-vue'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
f7route: Object,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
regions: ['Thorax','Abdomen/Pelvis','Limbs','Head and Neck'],
|
||||
resultData: {},
|
||||
selectedChip: -1,
|
||||
activeRegion: 4,
|
||||
imageRegion: '',
|
||||
imageLoaded: false,
|
||||
imageView: '../assets/icons/image.svg',
|
||||
reader: new FileReader(),
|
||||
detectorName: ''
|
||||
}
|
||||
},
|
||||
created () {
|
||||
switch (this.f7route.params.region) {
|
||||
case 'thorax':
|
||||
this.activeRegion = 0
|
||||
this.imageRegion = '../assets/regions/thorax.svg'
|
||||
this.detectorName = 'thorax'
|
||||
break;
|
||||
case 'abdomen':
|
||||
this.activeRegion = 1
|
||||
this.imageRegion = '../assets/regions/abdpel.svg'
|
||||
this.detectorName = 'combined'
|
||||
break;
|
||||
case 'limbs':
|
||||
this.activeRegion = 2
|
||||
this.imageRegion = '../assets/regions/limb.svg'
|
||||
this.detectorName = 'defaultNew'
|
||||
break;
|
||||
case 'head':
|
||||
this.activeRegion = 3
|
||||
this.imageRegion = '../assets/regions/headneck.svg'
|
||||
break;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
chipColor (confVal) {
|
||||
if (confVal >= 90) return 'green'
|
||||
if (confVal >= 70) return 'lime'
|
||||
return 'yellow'
|
||||
},
|
||||
setData () {
|
||||
var self = this
|
||||
var loadServerSettings = localStorage.getItem('serverSettings')
|
||||
if (loadServerSettings) var serverSettings = JSON.parse(loadServerSettings)
|
||||
if (serverSettings && serverSettings.use) {
|
||||
var modelURL = `http://${serverSettings.address}:${serverSettings.port}/detect`
|
||||
var xhr = new XMLHttpRequest()
|
||||
xhr.open("POST", modelURL)
|
||||
xhr.setRequestHeader('Content-Type', 'application/json')
|
||||
xhr.onload = function () {
|
||||
if (this.status !== 200) {
|
||||
//this.response.text().then(function(message){alert(message)})
|
||||
console.log(this.response)
|
||||
return;
|
||||
}
|
||||
self.resultData = JSON.parse(this.response)
|
||||
}
|
||||
|
||||
var doodsData = {
|
||||
"detector_name": this.detectorName,
|
||||
"detect": {
|
||||
"*":50
|
||||
}
|
||||
}
|
||||
doodsData.data = this.reader.result.split(',')[1]
|
||||
import RegionIcon from '../components/region-icon.vue'
|
||||
import SvgIcon from '../components/svg-icon.vue'
|
||||
|
||||
xhr.send(JSON.stringify(doodsData))
|
||||
} else {
|
||||
f7.dialog.alert('Using built-in model')
|
||||
}
|
||||
},
|
||||
selectImage () {
|
||||
var loadResult = this.$refs.image_chooser.click()
|
||||
},
|
||||
selectChip ( iChip ) {
|
||||
var imgWidth
|
||||
var imgHeight
|
||||
import submitMixin from './submit-mixin'
|
||||
|
||||
this.selectedChip = iChip
|
||||
const box = this.$refs.structure_box
|
||||
const img = this.$refs.image_src
|
||||
var imgAspect = img.naturalWidth / img.naturalHeight
|
||||
var rendAspect = img.offsetWidth / img.offsetHeight
|
||||
if (imgAspect >= rendAspect) {
|
||||
imgWidth = img.offsetWidth
|
||||
imgHeight = img.offsetWidth / imgAspect
|
||||
} else {
|
||||
imgWidth = img.offsetHeight * imgAspect
|
||||
imgHeight = img.offsetHeight
|
||||
}
|
||||
box.style.display = "block"
|
||||
box.style.left = `${(img.offsetWidth - imgWidth) / 2 + this.resultData.detections[iChip].left * imgWidth}px`
|
||||
box.style.top = `${(img.offsetHeight - imgHeight) / 2 + this.resultData.detections[iChip].top * imgHeight}px`
|
||||
box.style.width = `${(this.resultData.detections[iChip].right - this.resultData.detections[iChip].left) * imgWidth}px`
|
||||
box.style.height = `${(this.resultData.detections[iChip].bottom - this.resultData.detections[iChip].top) * imgHeight}px`
|
||||
},
|
||||
deleteChip ( iChip ) {
|
||||
f7.dialog.confirm(`${this.resultData.detections[iChip].label} is identified with ${this.resultData.detections[iChip].confidence.toFixed(1)}% confidence. Are you sure you want to delete it?`, () => {
|
||||
this.resultData.detections.splice(iChip, 1)
|
||||
});
|
||||
},
|
||||
getImage () {
|
||||
const example = this.$refs.image_chooser.files[0];
|
||||
this.imageView = URL.createObjectURL(example);
|
||||
this.reader.readAsDataURL(example)
|
||||
this.imageLoaded = true;
|
||||
}
|
||||
export default {
|
||||
mixins: [submitMixin],
|
||||
props: {
|
||||
f7route: Object,
|
||||
},
|
||||
components: {
|
||||
RegionIcon,
|
||||
SvgIcon
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
regions: ['Thorax','Abdomen/Pelvis','Limbs','Head and Neck'],
|
||||
resultData: {},
|
||||
selectedChip: -1,
|
||||
activeRegion: 4,
|
||||
imageLoaded: false,
|
||||
imageView: null,
|
||||
imageLoadMode: "environment",
|
||||
detecting: false,
|
||||
detectPanel: false,
|
||||
showDetectSettings: false,
|
||||
detectorName: '',
|
||||
detectorLevel: 50,
|
||||
detectorLabels: [],
|
||||
serverSettings: {},
|
||||
isCordova: !!window.cordova,
|
||||
uploadUid: null,
|
||||
uploadDirty: false
|
||||
}
|
||||
},
|
||||
created () {
|
||||
switch (this.f7route.params.region) {
|
||||
case 'thorax':
|
||||
this.activeRegion = 0
|
||||
this.detectorName = 'thorax'
|
||||
break;
|
||||
case 'abdomen':
|
||||
this.activeRegion = 1
|
||||
this.detectorName = 'combined'
|
||||
break;
|
||||
case 'limbs':
|
||||
this.activeRegion = 2
|
||||
this.detectorName = 'defaultNew'
|
||||
break;
|
||||
case 'head':
|
||||
this.activeRegion = 3
|
||||
break;
|
||||
}
|
||||
var loadServerSettings = localStorage.getItem('serverSettings')
|
||||
if (loadServerSettings) this.serverSettings = JSON.parse(loadServerSettings)
|
||||
var self = this
|
||||
if (this.serverSettings && this.serverSettings.use) {
|
||||
var modelURL = `http://${this.serverSettings.address}:${this.serverSettings.port}/detectors`
|
||||
var xhr = new XMLHttpRequest()
|
||||
xhr.open("GET", modelURL)
|
||||
xhr.setRequestHeader('Content-Type', 'application/json')
|
||||
xhr.onload = function () {
|
||||
if (this.status !== 200) {
|
||||
//this.response.text().then(function(message){alert(message)})
|
||||
console.log(xhr.response)
|
||||
return;
|
||||
}
|
||||
var detectors = JSON.parse(xhr.response).detectors
|
||||
var findLabel = detectors
|
||||
.find( d => { return d.name == self.detectorName } )?.labels
|
||||
.filter( l => { return l != "" } ).sort()
|
||||
.map( l => { return {'name': l, 'detect': true} } )
|
||||
self.detectorLabels = findLabel || []
|
||||
}
|
||||
xhr.send()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showResults () {
|
||||
var filteredResults = this.resultData.detections
|
||||
if (!filteredResults) return []
|
||||
var allSelect = this.detectorLabels.every( s => { return s.detect } )
|
||||
var selectedLabels = this.detectorLabels
|
||||
.filter( l => { return l.detect })
|
||||
.map( l => { return l.name })
|
||||
filteredResults.forEach( (d, i) => {
|
||||
filteredResults[i].resultIndex = i
|
||||
filteredResults[i].aboveThreshold = d.confidence >= this.detectorLevel
|
||||
filteredResults[i].isSearched = allSelect || selectedLabels.includes(d.label)
|
||||
})
|
||||
return filteredResults
|
||||
},
|
||||
numResults () {
|
||||
return this.showResults.filter( r => { return r.aboveThreshold && r.isSearched && !r.isDeleted }).length
|
||||
},
|
||||
viewedAll () {
|
||||
if (this.resultData && this.resultData.detections) {
|
||||
return this.resultData.detections
|
||||
.filter( s => { return s.confidence >= this.detectorLevel})
|
||||
.every( s => { return s.beenViewed })
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
chipGradient (confVal) {
|
||||
return `--chip-media-background: hsl(${confVal / 100 * 120}deg 100% 50%)`
|
||||
},
|
||||
setData () {
|
||||
var self = this
|
||||
if (this.serverSettings && this.serverSettings.use) {
|
||||
var modelURL = `http://${this.serverSettings.address}:${this.serverSettings.port}/detect`
|
||||
var xhr = new XMLHttpRequest()
|
||||
xhr.open("POST", modelURL)
|
||||
xhr.setRequestHeader('Content-Type', 'application/json')
|
||||
xhr.onload = function () {
|
||||
if (this.status !== 200) {
|
||||
//this.response.text().then(function(message){alert(message)})
|
||||
console.log(xhr.response)
|
||||
self.detecting = false
|
||||
return;
|
||||
}
|
||||
self.resultData = JSON.parse(xhr.response)
|
||||
self.uploadDirty = true
|
||||
self.detecting = false
|
||||
}
|
||||
|
||||
var doodsData = {
|
||||
"detector_name": this.detectorName,
|
||||
"detect": {
|
||||
"*": 1
|
||||
},
|
||||
"data": this.imageView.src.split(',')[1]
|
||||
}
|
||||
|
||||
xhr.send(JSON.stringify(doodsData))
|
||||
} else {
|
||||
//TODO
|
||||
f7.dialog.alert('Using built-in model')
|
||||
}
|
||||
},
|
||||
selectAll (ev) {
|
||||
if (ev.target.checked) {
|
||||
this.detectorLabels.forEach( s => s.detect = true )
|
||||
} else {
|
||||
this.detectorLabels.forEach( s => s.detect = false )
|
||||
}
|
||||
},
|
||||
selectImage (mode) {
|
||||
this.imageLoadMode = mode
|
||||
if (mode == "camera") {
|
||||
this.$refs.image_chooser.setAttribute("capture","environment")
|
||||
} else {
|
||||
this.$refs.image_chooser.removeAttribute("capture")
|
||||
}
|
||||
if (this.isCordova && mode == "camera") {
|
||||
navigator.camera.getPicture(this.getImage, this.onFail, { quality: 50, destinationType: Camera.DestinationType.DATA_URL, correctOrientation: true });
|
||||
} else {
|
||||
var loadResult = this.$refs.image_chooser.click()
|
||||
}
|
||||
},
|
||||
onFail (message) {
|
||||
alert(`Camera fail: ${message}`)
|
||||
},
|
||||
selectChip ( iChip ) {
|
||||
const [imCanvas, imageCtx] = this.resetView()
|
||||
|
||||
if (this.selectedChip == iChip) {
|
||||
this.selectedChip = -1
|
||||
return
|
||||
}
|
||||
|
||||
var imgWidth
|
||||
var imgHeight
|
||||
this.selectedChip = iChip
|
||||
var imgAspect = this.imageView.width / this.imageView.height
|
||||
var rendAspect = imCanvas.width / imCanvas.height
|
||||
if (imgAspect >= rendAspect) {
|
||||
imgWidth = imCanvas.width
|
||||
imgHeight = imCanvas.width / imgAspect
|
||||
} else {
|
||||
imgWidth = imCanvas.height * imgAspect
|
||||
imgHeight = imCanvas.height
|
||||
}
|
||||
var boxLeft = (imCanvas.width - imgWidth) / 2 + this.resultData.detections[iChip].left * imgWidth
|
||||
var boxTop = (imCanvas.height - imgHeight) / 2 + this.resultData.detections[iChip].top * imgHeight
|
||||
var boxWidth = (Math.min(this.resultData.detections[iChip].right, 1) - Math.max(this.resultData.detections[iChip].left, 0)) * imgWidth
|
||||
var boxHeight = (Math.min(this.resultData.detections[iChip].bottom, 1) - Math.max(this.resultData.detections[iChip].top, 0)) * imgHeight
|
||||
imageCtx.strokeRect(boxLeft,boxTop,boxWidth,boxHeight)
|
||||
this.resultData.detections[iChip].beenViewed = true
|
||||
},
|
||||
deleteChip ( iChip ) {
|
||||
f7.dialog.confirm(`${this.resultData.detections[iChip].label} is identified with ${this.resultData.detections[iChip].confidence.toFixed(1)}% confidence. Are you sure you want to delete it?`, () => {
|
||||
this.resetView()
|
||||
this.resultData.detections.splice(iChip, 1)
|
||||
this.selectedChip = -1
|
||||
this.uploadDirty = true
|
||||
});
|
||||
},
|
||||
resetView () {
|
||||
const imCanvas = this.$refs.image_cvs
|
||||
const imageCtx = imCanvas.getContext("2d")
|
||||
imCanvas.width = imCanvas.clientWidth
|
||||
imCanvas.height = imCanvas.clientHeight
|
||||
imageCtx.clearRect(0,0,imCanvas.width,imCanvas.height)
|
||||
imageCtx.strokeStyle = 'yellow'
|
||||
imageCtx.lineWidth = 3
|
||||
return [imCanvas, imageCtx]
|
||||
},
|
||||
getImage (searchImage) {
|
||||
let loadImage =new Promise(resolve => {
|
||||
if (this.isCordova && this.imageLoadMode == "camera") {
|
||||
resolve('data:image/jpg;base64,' + searchImage)
|
||||
} else {
|
||||
const searchImage = this.$refs.image_chooser.files[0]
|
||||
var reader = new FileReader()
|
||||
reader.addEventListener("loadend", () => {
|
||||
this.detecting = true
|
||||
resolve(reader.result)
|
||||
})
|
||||
reader.readAsDataURL(searchImage)
|
||||
}
|
||||
})
|
||||
loadImage.then((imgData) => {
|
||||
this.imageLoaded = true
|
||||
this.resultData = {}
|
||||
this.imageView = new Image()
|
||||
this.imageView.src = imgData
|
||||
return(this.imageView.decode())
|
||||
}).then( () => {
|
||||
const [imCanvas, _] = this.resetView()
|
||||
imCanvas.style['background-image'] = `url(${this.imageView.src})`
|
||||
this.setData()
|
||||
}).catch((e) => {
|
||||
console.log(e.message)
|
||||
f7.dialog.alert(`Error loading image: ${e.message}`)
|
||||
})
|
||||
},
|
||||
videoStream() {
|
||||
//TODO
|
||||
return null
|
||||
},
|
||||
async submitData () {
|
||||
var uploadData = this.showResults
|
||||
.filter( d => { return d.aboveThreshold && d.isSearched && !d.isDeleted })
|
||||
.map( r => { return {"top": r.top, "left": r.left, "bottom": r.bottom, "right": r.right, "label": r.label}})
|
||||
this.uploadUid = await this.uploadData(this.imageView.src.split(',')[1],uploadData,this.uploadUid)
|
||||
if (this.uploadUid) { this.uploadDirty = false }
|
||||
},
|
||||
onLevelChange(value) {
|
||||
this.detectorLevel = value
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,40 +0,0 @@
|
||||
<template>
|
||||
<f7-page>
|
||||
<f7-navbar title="Dynamic Route" back-link="Back"></f7-navbar>
|
||||
<f7-block strong inset>
|
||||
<ul>
|
||||
<li><b>Url:</b> {{ f7route.url }}</li>
|
||||
<li><b>Path:</b> {{ f7route.path }}</li>
|
||||
<li><b>Hash:</b> {{ f7route.hash }}</li>
|
||||
<li>
|
||||
<b>Params:</b>
|
||||
<ul>
|
||||
<li v-for="(value, key) in f7route.params" :key="key">
|
||||
<b>{{ key }}:</b> {{ value }}
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<b>Query:</b>
|
||||
<ul>
|
||||
<li v-for="(value, key) in f7route.query" :key="key">
|
||||
<b>{{ key }}:</b> {{ value }}
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><b>Route:</b> {{ f7route.route.path }}</li>
|
||||
</ul>
|
||||
</f7-block>
|
||||
<f7-block strong inset>
|
||||
<f7-link @click="f7router.back()">Go back via Router API</f7-link>
|
||||
</f7-block>
|
||||
</f7-page>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
f7route: Object,
|
||||
f7router: Object,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -8,24 +8,98 @@
|
||||
<f7-nav-title sliding>A.L.V.I.N.N.</f7-nav-title>
|
||||
</f7-navbar>
|
||||
<!-- Page content-->
|
||||
<div style="display: flex; flex-direction: column; justify-content: center; align-items: center; width: 100%;">
|
||||
<h2>Anatomy Lab Visual Identification Neural Network</h2>
|
||||
<h4>Veterinary Anatomy Edition</h4>
|
||||
<p>Select a region to begin</p>
|
||||
<f7-segmented raised style="flex-wrap: wrap;">
|
||||
<f7-button style="height: auto; width: auto;" href="/detect/thorax/">
|
||||
<img src="../assets/regions/thorax.svg" />
|
||||
<div style="display: grid; grid-template-columns: 100%; grid-template-rows: min-content min-content min-content 1fr; align-content: stretch; gap: 15px; min-height: 0px; max-height: calc(100vh - (var(--f7-navbar-height) + var(--f7-safe-area-top))); height: calc(100vh - (var(--f7-navbar-height) + var(--f7-safe-area-top)));">
|
||||
<h2 style="text-align: center; padding-left: 30px; padding-right: 30px;">Anatomy Lab Visual Identification Neural Network</h2>
|
||||
<h4 style="text-align: center; margin: 0;">Veterinary Anatomy Edition</h4>
|
||||
<p style="text-align: center; margin: 0;">Select a region to begin.</p>
|
||||
<div class="region-grid">
|
||||
<!--</f7-button><f7-button :class="`region-button thorax`" :href="'/detect/thorax/'">-->
|
||||
<f7-button :class="`region-button thorax${isAgreed ? '' : ' disabled'}`" :href="isAgreed && '/detect/thorax/'">
|
||||
<RegionIcon class="region-image" :region="0" />
|
||||
</f7-button>
|
||||
<f7-button style="height: auto; width: auto;" href="/detect/abdomen/">
|
||||
<img src="../assets/regions/abdpel.svg" />
|
||||
<!--<f7-button :class="`region-button abdomen${siteSettings.siteAgreement ? '' : ' disabled'}`" :href="siteSettings.siteAgreement && '/detect/abdomen/'">-->
|
||||
<f7-button :class="`region-button abdomen${isAgreed ? '' : ' disabled'}`" :href="isAgreed && '/detect/abdomen/'">
|
||||
<RegionIcon class="region-image" :region="1" />
|
||||
</f7-button>
|
||||
<f7-button style="height: auto; width: auto;" href="/detect/limbs/">
|
||||
<img src="../assets/regions/limb.svg" />
|
||||
<!--<f7-button :class="`region-button limbs${siteSettings.siteAgreement ? '' : ' disabled'}`" :href="siteSettings.siteAgreement && '/detect/limbs/'">-->
|
||||
<f7-button :class="`region-button limbs${isAgreed ? '' : ' disabled'}`" :href="isAgreed && '/detect/limbs/'">
|
||||
<RegionIcon class="region-image" :region="2" />
|
||||
</f7-button>
|
||||
<f7-button style="height: auto; width: auto;" href="/detect/head/">
|
||||
<img src="../assets/regions/headneck.svg" />
|
||||
<!--<f7-button class="region-button headneck disabled" :href="siteSettings.siteAgreement && '/detect/head/'">-->
|
||||
<f7-button class="region-button headneck disabled" :href="'/detect/head/'">
|
||||
<RegionIcon class="region-image" :region="3" />
|
||||
</f7-button>
|
||||
</f7-segmented>
|
||||
</div>
|
||||
</div>
|
||||
</f7-page>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.region-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: minmax(120px,max-content) minmax(120px,max-content);
|
||||
justify-items: center;
|
||||
align-content: center;
|
||||
gap: 20px 20px;
|
||||
margin: 30px;
|
||||
margin-top: 0;
|
||||
transform: scale(97%);
|
||||
}
|
||||
|
||||
.region-button {
|
||||
aspect-ratio: 1;
|
||||
min-width: 100px;
|
||||
height: auto;
|
||||
width: 100%;
|
||||
background-color: var(--avn-button-bg-color);
|
||||
padding: 5px;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.region-image {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
.region-grid {
|
||||
grid-template-columns: 1fr [thorax] auto [abdomen] auto [limbs] auto [headneck] auto 1fr;
|
||||
grid-template-rows: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.thorax {
|
||||
grid-column-start: thorax;
|
||||
}
|
||||
|
||||
.abdomen {
|
||||
grid-column-start: abdomen;
|
||||
}
|
||||
|
||||
.limbs {
|
||||
grid-column-start: limbs;
|
||||
}
|
||||
|
||||
.headneck {
|
||||
grid-column-start: headneck;
|
||||
}
|
||||
|
||||
.region-button {
|
||||
max-height: calc(100% - 15px);
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import RegionIcon from '../components/region-icon.vue'
|
||||
import store from '../js/store'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
RegionIcon
|
||||
},
|
||||
setup() {
|
||||
return store()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -15,7 +15,13 @@
|
||||
<f7-list-input :disabled="!serverSettings.use" v-model:value="serverSettings.address" label="Server address" type="text" placeholder="127.0.0.1" />
|
||||
<f7-list-input :disabled="!serverSettings.use" v-model:value="serverSettings.port" label="Server port" type="text" placeholder="9001" />
|
||||
</f7-list>
|
||||
<f7-button @click="saveServerSettings" >Save</f7-button>
|
||||
<f7-block-title medium>Dark Mode</f7-block-title>
|
||||
<f7-list style="width: 100%;">
|
||||
<f7-list-item title="Auto" :checked="themeSettings.darkMode == 'auto'" radio name="darkmode" radio-icon="end" @change="setDarkMode('auto')" ></f7-list-item>
|
||||
<f7-list-item title="Light" :checked="themeSettings.darkMode.toString() == 'false'" radio name="darkmode" radio-icon="end" @change="setDarkMode(false)" ></f7-list-item>
|
||||
<f7-list-item title="Dark" :checked="themeSettings.darkMode.toString() == 'true'" radio name="darkmode" radio-icon="end" @change="setDarkMode(true)" ></f7-list-item>
|
||||
</f7-list>
|
||||
<f7-button @click="saveAllSettings" >Save</f7-button>
|
||||
</div>
|
||||
</f7-block>
|
||||
</f7-page>
|
||||
@@ -27,23 +33,29 @@
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
serverSettings: {
|
||||
use: false,
|
||||
address: '10.170.64.22',
|
||||
port: '9001'
|
||||
},
|
||||
serverSettings: {
|
||||
use: false,
|
||||
address: '10.170.64.22',
|
||||
port: '9001'
|
||||
},
|
||||
themeSettings: {
|
||||
darkMode: 'auto'
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
var loadServerSettings = localStorage.getItem('serverSettings')
|
||||
if (loadServerSettings) this.serverSettings = JSON.parse(loadServerSettings)
|
||||
var loadThemeSettings = localStorage.getItem('themeSettings')
|
||||
if (loadThemeSettings) this.themeSettings = JSON.parse(loadThemeSettings)
|
||||
},
|
||||
methods: {
|
||||
saveServerSettings () {
|
||||
saveAllSettings () {
|
||||
let saveSetting = new Promise(
|
||||
(saved,failed) => {
|
||||
try {
|
||||
localStorage.setItem('serverSettings',JSON.stringify(this.serverSettings))
|
||||
localStorage.setItem('themeSettings',JSON.stringify(this.themeSettings))
|
||||
saved()
|
||||
} catch {
|
||||
failed()
|
||||
@@ -67,6 +79,10 @@
|
||||
}
|
||||
)
|
||||
},
|
||||
setDarkMode (mode) {
|
||||
this.themeSettings.darkMode = mode
|
||||
f7.setDarkMode(mode)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
67
src/pages/submit-mixin.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import { f7 } from 'framework7-vue'
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
newUid (length) {
|
||||
const uidLength = length || 16
|
||||
const uidChars = 'abcdefghijklmnopqrstuvwxyz0123456789'
|
||||
var uid = []
|
||||
for (var i = 0; i < uidLength; i++) {
|
||||
uid.push(uidChars.charAt(Math.floor(Math.random() * ((i < 4) ? 26 : 36))))
|
||||
}
|
||||
return uid.join('')
|
||||
},
|
||||
uploadData (imagePayload, classPayload, prevUid) {
|
||||
let uploadImage = new Promise (resolve => {
|
||||
const dataUid = prevUid || this.newUid(16)
|
||||
var byteChars = window.atob(imagePayload)
|
||||
var byteArrays = []
|
||||
var len = byteChars.length
|
||||
|
||||
for (var offset = 0; offset < len; offset += 1024) {
|
||||
var slice = byteChars.slice(offset, offset + 1024)
|
||||
var byteNumbers = new Array(slice.length)
|
||||
for (var i = 0; i < slice.length; i++) {
|
||||
byteNumbers[i] = slice.charCodeAt(i)
|
||||
}
|
||||
|
||||
var byteArray = new Uint8Array(byteNumbers)
|
||||
byteArrays.push(byteArray)
|
||||
}
|
||||
var imageBlob = new Blob(byteArrays, {type: 'image/jpeg'})
|
||||
|
||||
var xhrJpg = new XMLHttpRequest()
|
||||
var uploadUrl = `https://nextcloud.azgeorgis.net/public.php/webdav/${dataUid}.jpeg`
|
||||
xhrJpg.open("PUT", uploadUrl)
|
||||
xhrJpg.setRequestHeader('Content-Type', 'image/jpeg')
|
||||
xhrJpg.setRequestHeader('X-Method-Override', 'PUT')
|
||||
xhrJpg.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
|
||||
xhrJpg.setRequestHeader("Authorization", "Basic " + btoa("LKBm3H6JdSaywyg:"))
|
||||
xhrJpg.send(imageBlob)
|
||||
|
||||
var xhrTxt = new XMLHttpRequest()
|
||||
var uploadUrl = `https://nextcloud.azgeorgis.net/public.php/webdav/${dataUid}.txt`
|
||||
xhrTxt.open("PUT", uploadUrl)
|
||||
xhrTxt.setRequestHeader('Content-Type', 'text/plain')
|
||||
xhrTxt.setRequestHeader('X-Method-Override', 'PUT')
|
||||
xhrTxt.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
|
||||
xhrTxt.setRequestHeader("Authorization", "Basic " + btoa("LKBm3H6JdSaywyg:"))
|
||||
xhrTxt.send(JSON.stringify(classPayload))
|
||||
|
||||
resolve(dataUid)
|
||||
})
|
||||
return uploadImage.then((newUid) => {
|
||||
var toast = f7.toast.create({
|
||||
text: 'Detections Uploaded: thank you.',
|
||||
closeTimeout: 2000
|
||||
})
|
||||
toast.open()
|
||||
return newUid
|
||||
}).catch((e) => {
|
||||
console.log(e.message)
|
||||
f7.dialog.alert(`Error uploading image: ${e.message}`)
|
||||
return null
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||