Add photo detection framework (#8)

Closes #5.

Reviewed-on: Georgi_Lab/ALVINN_f7#8
This commit is contained in:
2023-11-14 10:16:33 -07:00
parent 9eed19776f
commit fc6f26d469
16 changed files with 278 additions and 398 deletions

View File

@@ -1,22 +1,21 @@
<template>
<f7-page name="about">
<f7-navbar title="About" back-link="Back"></f7-navbar>
<f7-block-title>About My App</f7-block-title>
<f7-block-title>About A.L.V.I.N.N.</f7-block-title>
<f7-block>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magni molestiae laudantium
dignissimos est nobis delectus nemo ea alias voluptatum architecto, amet similique, saepe
iste consectetur in repellat ut minus quibusdam!
ALVINN is an object detection neural network specializing in the identification of anatomical structures from imagery of dissected, embalmed specimens in anatomy teaching labratories.
ALVINN is intended as a learning aid only; it is not a tool for medical diagnosis, intervention, or treatment.
</p>
<p>
Molestias et distinctio porro nesciunt ratione similique, magni doloribus, rerum nobis,
aliquam quae reiciendis quasi modi. Nam a recusandae, fugiat in ea voluptates fuga eius,
velit corrupti reprehenderit dignissimos consequatur!
All of the images in ALVINN's models have been collected by students and faculty of Midwestern University's anatomy classes.
</p>
<p>
Blanditiis, cumque quo adipisci. Molestiae, dolores dolorum quos doloremque ipsa ullam
eligendi commodi deserunt doloribus inventore magni? Ea mollitia veniam nostrum nihil, iusto
doloribus a at! Ea molestiae ullam delectus!
ALVINN is conceived and created by Justin Georgi.
Other contributions from:
<ul>
<li>Alexandra Davis</li>
</ul>
</p>
</f7-block>
</f7-page>

138
src/pages/detect.vue Normal file
View File

@@ -0,0 +1,138 @@
<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: center; height: calc(100% - var(--f7-navbar-height) - var(--f7-safe-area-top));">
<div class="image-box" style="display: flex; flex-direction: column; align-items: center; flex: 1 1 0%;" >
<img :src="imageView" ref="image_src" style="min-width: 0; min-height: 0; flex: 1 1 0"/>
<div ref="structure_box" style="border: solid 3px yellow; position: absolute; display: none;" />
</div>
<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/png, image/jpg, image/jpeg" style="display: none;"/>
<div v-if="resultData && resultData.detections" class="chip-results" style="flex: 0 0 auto;">
<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>
</template>
<style>
.chip-results {
display: flex;
flex-wrap: wrap;
gap: 5px;
--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(--f7-theme-color);
transform: translate(-2px, -2px);
}
.image-menu .button {
aspect-ratio: 1;
height: auto;
padding: 5px;
}
</style>
<script>
import { f7 } from 'framework7-vue'
import fakeData from './testData.js'
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',
}
},
created () {
switch (this.f7route.params.region) {
case 'thorax':
this.activeRegion = 0
this.imageRegion = '../assets/regions/thorax.svg'
break;
case 'abdomen':
this.activeRegion = 1
this.imageRegion = '../assets/regions/abdpel.svg'
break;
case 'limbs':
this.activeRegion = 2
this.imageRegion = '../assets/regions/limb.svg'
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 () {
this.resultData = fakeData.testData
},
selectImage () {
this.$refs.image_chooser.click()
//TODO This really needs to be a promise and resolve system
this.imageLoaded = true;
var box = this.$refs.structure_box
},
selectChip ( iChip ) {
this.selectedChip = iChip
var box = this.$refs.structure_box
var img = this.$refs.image_src
var imgWidth = img.offsetWidth
var imgHeight = img.offsetHeight
box.style.display = "block"
box.style.left = `calc( 50% - ${imgWidth/2}px + ${this.resultData.detections[iChip].left * imgWidth}px)`
box.style.top = `${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 () {
var example = this.$refs.image_chooser.files[0];
this.imageView = URL.createObjectURL(example);
}
}
}
</script>

View File

@@ -7,54 +7,25 @@
</f7-nav-left>
<f7-nav-title sliding>A.L.V.I.N.N.</f7-nav-title>
</f7-navbar>
<!-- Toolbar-->
<f7-toolbar bottom>
<f7-link>Left Link</f7-link>
<f7-link>Right Link</f7-link>
</f7-toolbar>
<!-- 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;">
<f7-button style="height: auto; width: auto;" href="/detect/thorax/">
<img src="../assets/regions/thorax.svg" />
</f7-button>
<f7-button style="height: auto; width: auto;">
<f7-button style="height: auto; width: auto;" href="/detect/abdomen/">
<img src="../assets/regions/abdpel.svg" />
</f7-button>
<f7-button style="height: auto; width: auto;">
<f7-button style="height: auto; width: auto;" href="/detect/limbs/">
<img src="../assets/regions/limb.svg" />
</f7-button>
<f7-button style="height: auto; width: auto;">
<f7-button style="height: auto; width: auto;" href="/detect/head/">
<img src="../assets/regions/headneck.svg" />
</f7-button>
</f7-segmented>
</div>
<f7-block-title>Navigation</f7-block-title>
<f7-list strong inset dividersIos>
<f7-list-item link="/about/" title="About"></f7-list-item>
</f7-list>
<f7-block-title>Modals</f7-block-title>
<f7-block class="grid grid-cols-2 grid-gap">
<f7-button fill popup-open="#my-popup">Popup</f7-button>
</f7-block>
<f7-list strong inset dividersIos>
<f7-list-item
title="Dynamic (Component) Route"
link="/dynamic-route/blog/45/post/125/?foo=bar#about"
></f7-list-item>
<f7-list-item
title="Default Route (404)"
link="/load-something-that-doesnt-exist/"
></f7-list-item>
<f7-list-item
title="Request Data & Load"
link="/request-and-load/user/123456/"
></f7-list-item>
</f7-list>
</f7-page>
</template>

View File

@@ -1,25 +0,0 @@
<template>
<f7-page>
<f7-navbar :title="`${user.firstName} ${user.lastName}`" back-link="Back"></f7-navbar>
<f7-block strong inset>
{{ user.about }}
</f7-block>
<f7-list strong inset dividers-ios>
<f7-list-item
v-for="(link, index) in user.links"
:key="index"
:link="link.url"
:title="link.title"
external
target="_blank"
></f7-list-item>
</f7-list>
</f7-page>
</template>
<script>
export default {
props: {
user: Object,
},
};
</script>

55
src/pages/testData.js Normal file
View File

@@ -0,0 +1,55 @@
export default {
testData: {
"id": "manual",
"detections": [
{
"top": 0.5543267130851746,
"left": 0.514180064201355,
"bottom": 0.7492024302482605,
"right": 0.5877177715301514,
"label": "Lung, Right",
"confidence": 91.04458093643188
},
{
"top": 0.24095627665519714,
"left": 0.5838792324066162,
"bottom": 0.9880742430686951,
"right": 0.8479938507080078,
"label": "Diaphragm",
"confidence": 88.7181043624878
},
{
"top": 0.3355500102043152,
"left": 0.3559962809085846,
"bottom": 0.4628722071647644,
"right": 0.6606560349464417,
"label": "Aorta",
"confidence": 85.63258051872253
},
{
"top": 0.46723803877830505,
"left": 0.28829023241996765,
"bottom": 0.8604505658149719,
"right": 0.5213174819946289,
"label": "Heart",
"confidence": 85.21404266357422
},
{
"top": 0.428698867559433,
"left": 0.1482502520084381,
"bottom": 0.5519284009933472,
"right": 0.5934896469116211,
"label": "Phrenic nerve",
"confidence": 81.82616829872131
},
{
"top": 0.4919853210449219,
"left": 0.4810255169868469,
"bottom": 0.5394672155380249,
"right": 0.5925238728523254,
"label": "Vena Cava, Caudal",
"confidence": 75.55835843086243
}
]
}
}