Add real-time detection to camera stream #143
14
src/assets/target.svg
Normal file
14
src/assets/target.svg
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
<svg version="1.1" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<defs>
|
||||||
|
<filter id="filter1847" x="-.075" y="-.075" width="1.15" height="1.15" color-interpolation-filters="sRGB">
|
||||||
|
<feGaussianBlur stdDeviation="0.3125"/>
|
||||||
|
</filter>
|
||||||
|
<radialGradient id="radialGradient1903" cx="5" cy="5" r="5.75" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#ff0" offset="0"/>
|
||||||
|
<stop stop-color="#ff0" stop-opacity="0" offset="1"/>
|
||||||
|
</radialGradient>
|
||||||
|
</defs>
|
||||||
|
<path d="m0 5 3.833274-1.166726 1.166726-3.833274 1.166726 3.833274 3.833274 1.166726-3.833274 1.166726-1.166726 3.833274-1.166726-3.833274z" color="#000000" fill="url(#radialGradient1903)" fill-rule="evenodd" filter="url(#filter1847)" opacity=".63"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 870 B |
11
src/models/abdomen-mini/descript.json
Normal file
11
src/models/abdomen-mini/descript.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"version": "0.0.0-n1",
|
||||||
|
"region": "Coco",
|
||||||
|
"size": 640,
|
||||||
|
"epochs": 1000,
|
||||||
|
"name": "coco128 test",
|
||||||
|
"yolo-version": "8.1.20 docker",
|
||||||
|
"date": "2024-03-12",
|
||||||
|
"export": "coco128.yaml"
|
||||||
|
}
|
||||||
|
|
||||||
BIN
src/models/abdomen-mini/group1-shard1of4.bin
Normal file
BIN
src/models/abdomen-mini/group1-shard1of4.bin
Normal file
Binary file not shown.
BIN
src/models/abdomen-mini/group1-shard2of4.bin
Normal file
BIN
src/models/abdomen-mini/group1-shard2of4.bin
Normal file
Binary file not shown.
BIN
src/models/abdomen-mini/group1-shard3of4.bin
Normal file
BIN
src/models/abdomen-mini/group1-shard3of4.bin
Normal file
Binary file not shown.
BIN
src/models/abdomen-mini/group1-shard4of4.bin
Normal file
BIN
src/models/abdomen-mini/group1-shard4of4.bin
Normal file
Binary file not shown.
92
src/models/abdomen-mini/metadata.yaml
Normal file
92
src/models/abdomen-mini/metadata.yaml
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
description: Ultralytics best model trained on /usr/src/ultralytics/ultralytics/cfg/datasets/coco128.yaml
|
||||||
|
author: Ultralytics
|
||||||
|
license: AGPL-3.0 https://ultralytics.com/license
|
||||||
|
date: '2024-03-12T16:25:00.089873'
|
||||||
|
version: 8.1.20
|
||||||
|
stride: 32
|
||||||
|
task: detect
|
||||||
|
batch: 1
|
||||||
|
imgsz:
|
||||||
|
- 640
|
||||||
|
- 640
|
||||||
|
names:
|
||||||
|
0: person
|
||||||
|
1: bicycle
|
||||||
|
2: car
|
||||||
|
3: motorcycle
|
||||||
|
4: airplane
|
||||||
|
5: bus
|
||||||
|
6: train
|
||||||
|
7: truck
|
||||||
|
8: boat
|
||||||
|
9: traffic light
|
||||||
|
10: fire hydrant
|
||||||
|
11: stop sign
|
||||||
|
12: parking meter
|
||||||
|
13: bench
|
||||||
|
14: bird
|
||||||
|
15: cat
|
||||||
|
16: dog
|
||||||
|
17: horse
|
||||||
|
18: sheep
|
||||||
|
19: cow
|
||||||
|
20: elephant
|
||||||
|
21: bear
|
||||||
|
22: zebra
|
||||||
|
23: giraffe
|
||||||
|
24: backpack
|
||||||
|
25: umbrella
|
||||||
|
26: handbag
|
||||||
|
27: tie
|
||||||
|
28: suitcase
|
||||||
|
29: frisbee
|
||||||
|
30: skis
|
||||||
|
31: snowboard
|
||||||
|
32: sports ball
|
||||||
|
33: kite
|
||||||
|
34: baseball bat
|
||||||
|
35: baseball glove
|
||||||
|
36: skateboard
|
||||||
|
37: surfboard
|
||||||
|
38: tennis racket
|
||||||
|
39: bottle
|
||||||
|
40: wine glass
|
||||||
|
41: cup
|
||||||
|
42: fork
|
||||||
|
43: knife
|
||||||
|
44: spoon
|
||||||
|
45: bowl
|
||||||
|
46: banana
|
||||||
|
47: apple
|
||||||
|
48: sandwich
|
||||||
|
49: orange
|
||||||
|
50: broccoli
|
||||||
|
51: carrot
|
||||||
|
52: hot dog
|
||||||
|
53: pizza
|
||||||
|
54: donut
|
||||||
|
55: cake
|
||||||
|
56: chair
|
||||||
|
57: couch
|
||||||
|
58: potted plant
|
||||||
|
59: bed
|
||||||
|
60: dining table
|
||||||
|
61: toilet
|
||||||
|
62: tv
|
||||||
|
63: laptop
|
||||||
|
64: mouse
|
||||||
|
65: remote
|
||||||
|
66: keyboard
|
||||||
|
67: cell phone
|
||||||
|
68: microwave
|
||||||
|
69: oven
|
||||||
|
70: toaster
|
||||||
|
71: sink
|
||||||
|
72: refrigerator
|
||||||
|
73: book
|
||||||
|
74: clock
|
||||||
|
75: vase
|
||||||
|
76: scissors
|
||||||
|
77: teddy bear
|
||||||
|
78: hair drier
|
||||||
|
79: toothbrush
|
||||||
1
src/models/abdomen-mini/model.json
Normal file
1
src/models/abdomen-mini/model.json
Normal file
File diff suppressed because one or more lines are too long
73
src/models/abdomen/classes.json
Normal file
73
src/models/abdomen/classes.json
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
[
|
||||||
|
"person",
|
||||||
|
"bicycle",
|
||||||
|
"car",
|
||||||
|
"motorcycle",
|
||||||
|
"airplane",
|
||||||
|
"bus",
|
||||||
|
"train",
|
||||||
|
"truck",
|
||||||
|
"boat",
|
||||||
|
"traffic light",
|
||||||
|
"stop sign",
|
||||||
|
"bench",
|
||||||
|
"bird",
|
||||||
|
"cat",
|
||||||
|
"dog",
|
||||||
|
"horse",
|
||||||
|
"elephant",
|
||||||
|
"bear",
|
||||||
|
"zebra",
|
||||||
|
"giraffe",
|
||||||
|
"backpack",
|
||||||
|
"umbrella",
|
||||||
|
"handbag",
|
||||||
|
"tie",
|
||||||
|
"suitcase",
|
||||||
|
"frisbee",
|
||||||
|
"skis",
|
||||||
|
"snowboard",
|
||||||
|
"sports ball",
|
||||||
|
"kite",
|
||||||
|
"baseball bat",
|
||||||
|
"baseball glove",
|
||||||
|
"skateboard",
|
||||||
|
"tennis racket",
|
||||||
|
"bottle",
|
||||||
|
"wine glass",
|
||||||
|
"cup",
|
||||||
|
"fork",
|
||||||
|
"knife",
|
||||||
|
"spoon",
|
||||||
|
"bowl",
|
||||||
|
"banana",
|
||||||
|
"sandwich",
|
||||||
|
"orange",
|
||||||
|
"broccoli",
|
||||||
|
"carrot",
|
||||||
|
"hot dog",
|
||||||
|
"pizza",
|
||||||
|
"donut",
|
||||||
|
"cake",
|
||||||
|
"chair",
|
||||||
|
"couch",
|
||||||
|
"potted plant",
|
||||||
|
"bed",
|
||||||
|
"dining table",
|
||||||
|
"toilet",
|
||||||
|
"tv",
|
||||||
|
"laptop",
|
||||||
|
"mouse",
|
||||||
|
"remote",
|
||||||
|
"cell phone",
|
||||||
|
"microwave",
|
||||||
|
"oven",
|
||||||
|
"sink",
|
||||||
|
"refrigerator",
|
||||||
|
"book",
|
||||||
|
"clock",
|
||||||
|
"vase",
|
||||||
|
"scissors",
|
||||||
|
"teddy bear",
|
||||||
|
"toothbrush"
|
||||||
|
]
|
||||||
11
src/models/abdomen/descript.json
Normal file
11
src/models/abdomen/descript.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"version": "0.0.0-n1",
|
||||||
|
"region": "Coco",
|
||||||
|
"size": 640,
|
||||||
|
"epochs": 1000,
|
||||||
|
"name": "coco128 test",
|
||||||
|
"yolo-version": "8.1.20 docker",
|
||||||
|
"date": "2024-03-12",
|
||||||
|
"export": "coco128.yaml"
|
||||||
|
}
|
||||||
|
|
||||||
BIN
src/models/abdomen/group1-shard1of4.bin
Normal file
BIN
src/models/abdomen/group1-shard1of4.bin
Normal file
Binary file not shown.
BIN
src/models/abdomen/group1-shard2of4.bin
Normal file
BIN
src/models/abdomen/group1-shard2of4.bin
Normal file
Binary file not shown.
BIN
src/models/abdomen/group1-shard3of4.bin
Normal file
BIN
src/models/abdomen/group1-shard3of4.bin
Normal file
Binary file not shown.
BIN
src/models/abdomen/group1-shard4of4.bin
Normal file
BIN
src/models/abdomen/group1-shard4of4.bin
Normal file
Binary file not shown.
92
src/models/abdomen/metadata.yaml
Normal file
92
src/models/abdomen/metadata.yaml
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
description: Ultralytics best model trained on /usr/src/ultralytics/ultralytics/cfg/datasets/coco128.yaml
|
||||||
|
author: Ultralytics
|
||||||
|
license: AGPL-3.0 https://ultralytics.com/license
|
||||||
|
date: '2024-03-12T16:25:00.089873'
|
||||||
|
version: 8.1.20
|
||||||
|
stride: 32
|
||||||
|
task: detect
|
||||||
|
batch: 1
|
||||||
|
imgsz:
|
||||||
|
- 640
|
||||||
|
- 640
|
||||||
|
names:
|
||||||
|
0: person
|
||||||
|
1: bicycle
|
||||||
|
2: car
|
||||||
|
3: motorcycle
|
||||||
|
4: airplane
|
||||||
|
5: bus
|
||||||
|
6: train
|
||||||
|
7: truck
|
||||||
|
8: boat
|
||||||
|
9: traffic light
|
||||||
|
10: fire hydrant
|
||||||
|
11: stop sign
|
||||||
|
12: parking meter
|
||||||
|
13: bench
|
||||||
|
14: bird
|
||||||
|
15: cat
|
||||||
|
16: dog
|
||||||
|
17: horse
|
||||||
|
18: sheep
|
||||||
|
19: cow
|
||||||
|
20: elephant
|
||||||
|
21: bear
|
||||||
|
22: zebra
|
||||||
|
23: giraffe
|
||||||
|
24: backpack
|
||||||
|
25: umbrella
|
||||||
|
26: handbag
|
||||||
|
27: tie
|
||||||
|
28: suitcase
|
||||||
|
29: frisbee
|
||||||
|
30: skis
|
||||||
|
31: snowboard
|
||||||
|
32: sports ball
|
||||||
|
33: kite
|
||||||
|
34: baseball bat
|
||||||
|
35: baseball glove
|
||||||
|
36: skateboard
|
||||||
|
37: surfboard
|
||||||
|
38: tennis racket
|
||||||
|
39: bottle
|
||||||
|
40: wine glass
|
||||||
|
41: cup
|
||||||
|
42: fork
|
||||||
|
43: knife
|
||||||
|
44: spoon
|
||||||
|
45: bowl
|
||||||
|
46: banana
|
||||||
|
47: apple
|
||||||
|
48: sandwich
|
||||||
|
49: orange
|
||||||
|
50: broccoli
|
||||||
|
51: carrot
|
||||||
|
52: hot dog
|
||||||
|
53: pizza
|
||||||
|
54: donut
|
||||||
|
55: cake
|
||||||
|
56: chair
|
||||||
|
57: couch
|
||||||
|
58: potted plant
|
||||||
|
59: bed
|
||||||
|
60: dining table
|
||||||
|
61: toilet
|
||||||
|
62: tv
|
||||||
|
63: laptop
|
||||||
|
64: mouse
|
||||||
|
65: remote
|
||||||
|
66: keyboard
|
||||||
|
67: cell phone
|
||||||
|
68: microwave
|
||||||
|
69: oven
|
||||||
|
70: toaster
|
||||||
|
71: sink
|
||||||
|
72: refrigerator
|
||||||
|
73: book
|
||||||
|
74: clock
|
||||||
|
75: vase
|
||||||
|
76: scissors
|
||||||
|
77: teddy bear
|
||||||
|
78: hair drier
|
||||||
|
79: toothbrush
|
||||||
1
src/models/abdomen/model.json
Normal file
1
src/models/abdomen/model.json
Normal file
File diff suppressed because one or more lines are too long
@@ -5,7 +5,6 @@ export default {
|
|||||||
const devicesList = await navigator.mediaDevices.enumerateDevices()
|
const devicesList = await navigator.mediaDevices.enumerateDevices()
|
||||||
this.videoDeviceAvailable = devicesList.some( d => d.kind == "videoinput")
|
this.videoDeviceAvailable = devicesList.some( d => d.kind == "videoinput")
|
||||||
if (this.videoDeviceAvailable) {
|
if (this.videoDeviceAvailable) {
|
||||||
navigator.mediaDevices.getUserMedia({video: true})
|
|
||||||
var vidConstraint = {
|
var vidConstraint = {
|
||||||
video: {
|
video: {
|
||||||
width: {
|
width: {
|
||||||
@@ -37,9 +36,6 @@ export default {
|
|||||||
const tempCtx = tempCVS.getContext('2d')
|
const tempCtx = tempCVS.getContext('2d')
|
||||||
tempCtx.drawImage(vidViewer, 0, 0)
|
tempCtx.drawImage(vidViewer, 0, 0)
|
||||||
this.getImage(tempCVS.toDataURL())
|
this.getImage(tempCVS.toDataURL())
|
||||||
},
|
|
||||||
async videoStream() {
|
|
||||||
//TODO
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,12 +6,12 @@
|
|||||||
</f7-navbar>
|
</f7-navbar>
|
||||||
<f7-block class="detect-grid">
|
<f7-block class="detect-grid">
|
||||||
<div class="image-container" ref="image_container">
|
<div class="image-container" ref="image_container">
|
||||||
<canvas id="im-draw" ref="image_cvs" @click="structureClick" :style="`display: ${imageLoaded ? 'block' : 'none'}; flex: 1 1 0%; 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 && !videoAvailable" :icon="f7route.params.region" fill-color="var(--avn-theme-color)" @click="selectImage" />
|
<SvgIcon v-if="!imageView && !videoAvailable" :icon="f7route.params.region" fill-color="var(--avn-theme-color)" @click="selectImage" />
|
||||||
<div class="vid-container" v-if="videoAvailable" style="width: 100%; height: 100%">
|
<div class="vid-container" :style="`display: ${videoAvailable ? 'block' : 'none'}; position: absolute; width: 100%; height: 100%;`">
|
||||||
<video id="vid-view" ref="vid_viewer" :srcObject="cameraStream" :autoPlay="true" style="width: 100%; height: 100%"></video>
|
<video id="vid-view" ref="vid_viewer" :srcObject="cameraStream" :autoPlay="true" style="width: 100%; height: 100%"></video>
|
||||||
<f7-button @click="captureVidFrame()" style="position: absolute; bottom: 32px; left: 50%; transform: translateX(-50%);" fill large>Capture</f7-button>
|
<f7-button @click="captureVidFrame()" style="position: absolute; bottom: 32px; left: 50%; transform: translateX(-50%); z-index: 3;" fill large>Capture</f7-button>
|
||||||
</div>
|
</div>
|
||||||
|
<canvas id="im-draw" ref="image_cvs" @click="structureClick" :style="`display: ${(imageLoaded || videoAvailable) ? 'block' : 'none'}; flex: 1 1 0%; max-width: 100%; max-height: 100%; min-width: 0; min-height: 0; background-size: contain; background-position: center; background-repeat: no-repeat; z-index: 2;`" />
|
||||||
</div>
|
</div>
|
||||||
<div class="chip-results" style="grid-area: result-view; flex: 0 0 auto; align-self: center;">
|
<div 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 })"
|
<f7-chip v-for="result in showResults.filter( r => { return r.aboveThreshold && r.isSearched && !r.isDeleted })"
|
||||||
@@ -56,6 +56,7 @@
|
|||||||
</f7-button>
|
</f7-button>
|
||||||
</f7-segmented>
|
</f7-segmented>
|
||||||
<input type="file" ref="image_chooser" @change="getImage()" accept="image/*" style="display: none;"/>
|
<input type="file" ref="image_chooser" @change="getImage()" accept="image/*" style="display: none;"/>
|
||||||
|
<img src="../assets/target.svg" ref="target_image" style="display: none;" />
|
||||||
</f7-block>
|
</f7-block>
|
||||||
|
|
||||||
<f7-panel :id="detectorName + '-settings'" right cover :backdrop="false" :container-el="`#${detectorName}-detect-page`">
|
<f7-panel :id="detectorName + '-settings'" right cover :backdrop="false" :container-el="`#${detectorName}-detect-page`">
|
||||||
@@ -147,7 +148,9 @@
|
|||||||
uploadUid: null,
|
uploadUid: null,
|
||||||
uploadDirty: false,
|
uploadDirty: false,
|
||||||
modelLocation: '',
|
modelLocation: '',
|
||||||
|
miniLocation: '',
|
||||||
modelLoading: true,
|
modelLoading: true,
|
||||||
|
reloadModel: false,
|
||||||
videoDeviceAvailable: false,
|
videoDeviceAvailable: false,
|
||||||
videoAvailable: false,
|
videoAvailable: false,
|
||||||
cameraStream: null
|
cameraStream: null
|
||||||
@@ -163,17 +166,23 @@
|
|||||||
case 'thorax':
|
case 'thorax':
|
||||||
this.activeRegion = 0
|
this.activeRegion = 0
|
||||||
this.detectorName = 'thorax'
|
this.detectorName = 'thorax'
|
||||||
//this.classesList = thoraxClasses
|
|
||||||
/* VITE setting */
|
/* VITE setting */
|
||||||
this.modelLocation = `../models/thorax${this.otherSettings.mini ? '-mini' : ''}/model.json`
|
this.modelLocation = `../models/thorax${this.otherSettings.mini ? '-mini' : ''}/model.json`
|
||||||
|
this.miniLocation = `../models/thorax-mini/model.json`
|
||||||
/* PWA Build setting */
|
/* PWA Build setting */
|
||||||
//this.modelLocation = `./models/thorax${this.otherSettings.mini ? '-mini' : ''}/model.json`
|
//this.modelLocation = `./models/thorax${this.otherSettings.mini ? '-mini' : ''}/model.json`
|
||||||
this.modelLocationCordova = `https://localhost/models/thorax${this.otherSettings.mini ? '-mini' : ''}/model.json`
|
this.modelLocationCordova = `https://localhost/models/thorax${this.otherSettings.mini ? '-mini' : ''}/model.json`
|
||||||
break;
|
break;
|
||||||
case 'abdomen':
|
case 'abdomen':
|
||||||
this.activeRegion = 1
|
this.activeRegion = 1
|
||||||
this.detectorName = 'combined'
|
this.detectorName = 'abdomen'
|
||||||
break;
|
/* VITE setting */
|
||||||
|
this.modelLocation = `../models/abdomen${this.otherSettings.mini ? '-mini' : ''}/model.json`
|
||||||
|
this.miniLocation = `../models/abdomen-mini/model.json`
|
||||||
|
/* PWA Build setting */
|
||||||
|
//this.modelLocation = `./models/abdomen${this.otherSettings.mini ? '-mini' : ''}/model.json`
|
||||||
|
this.modelLocationCordova = `https://localhost/models/abdomen${this.otherSettings.mini ? '-mini' : ''}/model.json`
|
||||||
|
break;
|
||||||
case 'limbs':
|
case 'limbs':
|
||||||
this.activeRegion = 2
|
this.activeRegion = 2
|
||||||
this.detectorName = 'defaultNew'
|
this.detectorName = 'defaultNew'
|
||||||
@@ -195,7 +204,7 @@
|
|||||||
this.modelLoading = false
|
this.modelLoading = false
|
||||||
} else {
|
} else {
|
||||||
this.modelLoading = true
|
this.modelLoading = true
|
||||||
this.loadModel(this.isCordova ? this.modelLocationCordova : this.modelLocation).then(() => {
|
this.loadModel(this.isCordova ? this.modelLocationCordova : this.modelLocation, true).then(() => {
|
||||||
this.modelLoading = false
|
this.modelLoading = false
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
console.log(e.message)
|
console.log(e.message)
|
||||||
@@ -253,7 +262,11 @@
|
|||||||
chipGradient (confVal) {
|
chipGradient (confVal) {
|
||||||
return `--chip-media-background: hsl(${confVal / 100 * 120}deg 100% 50%)`
|
return `--chip-media-background: hsl(${confVal / 100 * 120}deg 100% 50%)`
|
||||||
},
|
},
|
||||||
setData () {
|
async setData () {
|
||||||
|
if (this.reloadModel) {
|
||||||
|
await this.loadModel(this.isCordova ? this.modelLocationCordova : this.modelLocation)
|
||||||
|
this.reloadModel = false
|
||||||
|
}
|
||||||
if (this.serverSettings && this.serverSettings.use) {
|
if (this.serverSettings && this.serverSettings.use) {
|
||||||
this.remoteDetect()
|
this.remoteDetect()
|
||||||
} else {
|
} else {
|
||||||
@@ -284,7 +297,20 @@
|
|||||||
}
|
}
|
||||||
if (mode == "camera") {
|
if (mode == "camera") {
|
||||||
this.videoAvailable = await this.openCamera(this.$refs.image_container)
|
this.videoAvailable = await this.openCamera(this.$refs.image_container)
|
||||||
if (this.videoAvailable) { return }
|
if (this.videoAvailable) {
|
||||||
|
this.imageLoaded = false
|
||||||
|
this.imageView = null
|
||||||
|
this.$refs.image_cvs.style['background-image'] = 'none'
|
||||||
|
this.resultData = {}
|
||||||
|
var trackDetails = this.cameraStream.getVideoTracks()[0].getSettings()
|
||||||
|
var vidElement = this.$refs.vid_viewer
|
||||||
|
vidElement.width = trackDetails.width
|
||||||
|
vidElement.height = trackDetails.height
|
||||||
|
if (!this.otherSettings.disableVideo) {
|
||||||
|
this.videoFrameDetect(vidElement)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (mode == 'sample') {
|
if (mode == 'sample') {
|
||||||
f7.dialog.create({
|
f7.dialog.create({
|
||||||
@@ -350,6 +376,7 @@
|
|||||||
if (this.videoAvailable) {
|
if (this.videoAvailable) {
|
||||||
this.closeCamera()
|
this.closeCamera()
|
||||||
this.detecting = true
|
this.detecting = true
|
||||||
|
this.reloadModel = true
|
||||||
resolve(searchImage)
|
resolve(searchImage)
|
||||||
} else if (this.isCordova && this.imageLoadMode == "camera") {
|
} else if (this.isCordova && this.imageLoadMode == "camera") {
|
||||||
this.detecting = true
|
this.detecting = true
|
||||||
@@ -421,7 +448,7 @@
|
|||||||
this.selectChip(findBox >= 0 ? this.resultData.detections[findBox].resultIndex : this.selectedChip)
|
this.selectChip(findBox >= 0 ? this.resultData.detections[findBox].resultIndex : this.selectedChip)
|
||||||
},
|
},
|
||||||
box2cvs(boxInput) {
|
box2cvs(boxInput) {
|
||||||
if (!boxInput) return []
|
if (!boxInput || boxInput.length == 0) return []
|
||||||
const boxList = boxInput.length ? boxInput : [boxInput]
|
const boxList = boxInput.length ? boxInput : [boxInput]
|
||||||
const [imCanvas, imageCtx] = this.resetView()
|
const [imCanvas, imageCtx] = this.resetView()
|
||||||
var imgWidth
|
var imgWidth
|
||||||
|
|||||||
@@ -5,11 +5,23 @@ var model = null
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
methods: {
|
methods: {
|
||||||
async loadModel(weights) {
|
async loadModel(weights, preload) {
|
||||||
|
if (model && model.modelURL == weights) {
|
||||||
|
return model
|
||||||
|
} else if (model) {
|
||||||
|
model.dispose()
|
||||||
|
}
|
||||||
model = await tf.loadGraphModel(weights)
|
model = await tf.loadGraphModel(weights)
|
||||||
const [modelWidth, modelHeight] = model.inputs[0].shape.slice(1, 3)
|
const [modelWidth, modelHeight] = model.inputs[0].shape.slice(1, 3)
|
||||||
const dummyT = tf.ones([1,modelWidth,modelHeight,3])
|
/*****************
|
||||||
model.predict(dummyT) //Run model once to preload weights for better response time
|
* If preloading then run model
|
||||||
|
* once on fake data to preload
|
||||||
|
* weights for a faster response
|
||||||
|
*****************/
|
||||||
|
if (preload) {
|
||||||
|
const dummyT = tf.ones([1,modelWidth,modelHeight,3])
|
||||||
|
model.predict(dummyT)
|
||||||
|
}
|
||||||
return model
|
return model
|
||||||
},
|
},
|
||||||
async localDetect(imageData) {
|
async localDetect(imageData) {
|
||||||
@@ -150,7 +162,60 @@ export default {
|
|||||||
remoteTimeout () {
|
remoteTimeout () {
|
||||||
this.detecting = false
|
this.detecting = false
|
||||||
f7.dialog.alert('No connection to remote ALVINN instance. Please check app settings.')
|
f7.dialog.alert('No connection to remote ALVINN instance. Please check app settings.')
|
||||||
}
|
},
|
||||||
|
async videoFrameDetect (vidData) {
|
||||||
|
await this.loadModel(this.miniLocation)
|
||||||
|
const [modelWidth, modelHeight] = model.inputs[0].shape.slice(1, 3)
|
||||||
|
const imCanvas = this.$refs.image_cvs
|
||||||
|
const imageCtx = imCanvas.getContext("2d")
|
||||||
|
const target = this.$refs.target_image
|
||||||
|
await tf.nextFrame();
|
||||||
|
imCanvas.width = imCanvas.clientWidth
|
||||||
|
imCanvas.height = imCanvas.clientHeight
|
||||||
|
imageCtx.clearRect(0,0,imCanvas.width,imCanvas.height)
|
||||||
|
var imgWidth
|
||||||
|
var imgHeight
|
||||||
|
const imgAspect = vidData.clientWidth / vidData.clientHeight
|
||||||
|
const rendAspect = imCanvas.width / imCanvas.height
|
||||||
|
if (imgAspect >= rendAspect) {
|
||||||
|
imgWidth = imCanvas.width
|
||||||
|
imgHeight = imCanvas.width / imgAspect
|
||||||
|
} else {
|
||||||
|
imgWidth = imCanvas.height * imgAspect
|
||||||
|
imgHeight = imCanvas.height
|
||||||
|
}
|
||||||
|
while (this.videoAvailable) {
|
||||||
|
console.time('frame-process')
|
||||||
|
try {
|
||||||
|
const input = tf.tidy(() => {
|
||||||
|
return tf.image.resizeBilinear(tf.browser.fromPixels(vidData), [modelWidth, modelHeight]).div(255.0).expandDims(0)
|
||||||
|
})
|
||||||
|
const res = model.predict(input)
|
||||||
|
const rawRes = tf.transpose(res,[0,2,1]).arraySync()[0]
|
||||||
|
|
||||||
|
let rawCoords = []
|
||||||
|
if (rawRes) {
|
||||||
|
for (var i = 0; i < rawRes.length; i++) {
|
||||||
|
var getScores = rawRes[i].slice(4)
|
||||||
|
if (getScores.some( s => s > .5)) {
|
||||||
|
rawCoords.push(rawRes[i].slice(0,2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imageCtx.clearRect(0,0,imCanvas.width,imCanvas.height)
|
||||||
|
for (var coord of rawCoords) {
|
||||||
|
console.log(`x: ${coord[0]}, y: ${coord[1]}`)
|
||||||
|
let pointX = (imCanvas.width - imgWidth) / 2 + (coord[0] / modelWidth) * imgWidth -5
|
||||||
|
let pointY = (imCanvas.height - imgHeight) / 2 + (coord[1] / modelHeight) * imgHeight -5
|
||||||
|
imageCtx.drawImage(target, pointX, pointY, 20, 20)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
console.timeEnd('frame-process')
|
||||||
|
await tf.nextFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,6 +27,10 @@
|
|||||||
<span style="margin-left: 16px;">Enable demo mode</span>
|
<span style="margin-left: 16px;">Enable demo mode</span>
|
||||||
<f7-toggle v-model:checked="otherSettings.demo" style="margin-right: 16px;" @change="setDirty()" />
|
<f7-toggle v-model:checked="otherSettings.demo" style="margin-right: 16px;" @change="setDirty()" />
|
||||||
</div>
|
</div>
|
||||||
|
<div style="display:flex; justify-content:space-between; width: 100%; margin-bottom: 10px;">
|
||||||
|
<span style="margin-left: 16px;">Disable video estimates<f7-icon size="16" style="padding-left: 5px;" f7="question_diamond_fill" tooltip="faster: recommended for slower devices" /></span>
|
||||||
|
<f7-toggle v-model:checked="otherSettings.disableVideo" style="margin-right: 16px;" />
|
||||||
|
</div>
|
||||||
<div style="display:flex; justify-content:space-between; width: 100%">
|
<div style="display:flex; justify-content:space-between; width: 100%">
|
||||||
<span style="margin-left: 16px;">Use external server</span>
|
<span style="margin-left: 16px;">Use external server</span>
|
||||||
<f7-toggle v-model:checked="serverSettings.use" style="margin-right: 16px;" @change="setDirty()" />
|
<f7-toggle v-model:checked="serverSettings.use" style="margin-right: 16px;" @change="setDirty()" />
|
||||||
|
|||||||
@@ -13,7 +13,8 @@
|
|||||||
<f7-list style="width: 100%;">
|
<f7-list style="width: 100%;">
|
||||||
<f7-list-item :class="otherSettings.mini ? 'unused-model' : ''" title="Thorax" :after="thoraxDetails.version"></f7-list-item>
|
<f7-list-item :class="otherSettings.mini ? 'unused-model' : ''" title="Thorax" :after="thoraxDetails.version"></f7-list-item>
|
||||||
<f7-list-item title="Thorax-m" :after="miniThoraxDetails.version"></f7-list-item>
|
<f7-list-item title="Thorax-m" :after="miniThoraxDetails.version"></f7-list-item>
|
||||||
<f7-list-item title="Abdomen/Pelvis" :after="abdomenDetails.version"></f7-list-item>
|
<f7-list-item :class="otherSettings.mini ? 'unused-model' : ''" title="Abdomen/Pelvis" :after="abdomenDetails.version"></f7-list-item>
|
||||||
|
<f7-list-item title="Abd/Pel-m" :after="miniAbdomenDetails.version"></f7-list-item>
|
||||||
<f7-list-item title="Limbs" :after="limbsDetails.version"></f7-list-item>
|
<f7-list-item title="Limbs" :after="limbsDetails.version"></f7-list-item>
|
||||||
<f7-list-item title="Head/Neck" :after="headneckDetails.version"></f7-list-item>
|
<f7-list-item title="Head/Neck" :after="headneckDetails.version"></f7-list-item>
|
||||||
</f7-list>
|
</f7-list>
|
||||||
@@ -39,7 +40,8 @@
|
|||||||
return {
|
return {
|
||||||
thoraxDetails: {},
|
thoraxDetails: {},
|
||||||
miniThoraxDetails: {},
|
miniThoraxDetails: {},
|
||||||
abdomenDetails: { "version": "N/A" },
|
abdomenDetails: {},
|
||||||
|
miniAbdomenDetails: {},
|
||||||
limbsDetails: { "version": "N/A" },
|
limbsDetails: { "version": "N/A" },
|
||||||
headneckDetails: { "version": "N/A" },
|
headneckDetails: { "version": "N/A" },
|
||||||
alvinnVersion: store().getVersion,
|
alvinnVersion: store().getVersion,
|
||||||
@@ -60,7 +62,14 @@
|
|||||||
.then((mod) => {
|
.then((mod) => {
|
||||||
this.miniThoraxDetails = mod.default
|
this.miniThoraxDetails = mod.default
|
||||||
})
|
})
|
||||||
|
import('../models/abdomen/descript.json')
|
||||||
|
.then((mod) => {
|
||||||
|
this.abdomenDetails = mod.default
|
||||||
|
})
|
||||||
|
import('../models/abdomen-mini/descript.json')
|
||||||
|
.then((mod) => {
|
||||||
|
this.miniAbdomenDetails = mod.default
|
||||||
|
})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user