|
@@ -6,9 +6,20 @@
|
|
|
<img alt="close button" src="@/assets/icons/cross.svg">
|
|
|
</div>
|
|
|
|
|
|
- <div v-if="src" class="image-container">
|
|
|
- <h3>{{ label }}</h3>
|
|
|
+ <div class="label-container">
|
|
|
+ <h3> {{ label }} </h3>
|
|
|
+
|
|
|
+ <div v-if="this.box.origin === 'user'"
|
|
|
+ ref="create_predictions"
|
|
|
+ class="create-predictions-icon"
|
|
|
+ title="create prediction for this image"
|
|
|
+ :class="{active: isPredictionRunning}"
|
|
|
+ @click="predict_cropped_image">
|
|
|
+ <img alt="create prediction" src="@/assets/icons/rocket.svg">
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
|
|
|
+ <div v-if="src" class="image-container">
|
|
|
<img alt="crop" :src="src"/>
|
|
|
</div>
|
|
|
<div v-else>
|
|
@@ -21,6 +32,29 @@
|
|
|
export default {
|
|
|
name: "cropped-image",
|
|
|
props: ['labels', 'file', 'box'],
|
|
|
+ created: function () {
|
|
|
+ // get data
|
|
|
+ this.getJobs();
|
|
|
+
|
|
|
+ // subscribe to changes
|
|
|
+ this.$root.socket.on('connect', this.getJobs);
|
|
|
+ this.$root.socket.on('create-job', this.addJob);
|
|
|
+ this.$root.socket.on('remove-job', this.removeJob);
|
|
|
+ this.$root.socket.on('edit-job', this.editJob);
|
|
|
+ },
|
|
|
+ destroyed: function () {
|
|
|
+ this.$root.socket.off('connect', this.getJobs);
|
|
|
+ this.$root.socket.off('create-job', this.addJob);
|
|
|
+ this.$root.socket.off('remove-job', this.removeJob);
|
|
|
+ this.$root.socket.off('edit-job', this.editJob);
|
|
|
+ },
|
|
|
+ data: function () {
|
|
|
+ return {
|
|
|
+ jobs: [],
|
|
|
+ labelSelector: false,
|
|
|
+ model: null
|
|
|
+ }
|
|
|
+ },
|
|
|
computed: {
|
|
|
src: function () {
|
|
|
if (!this.box)
|
|
@@ -46,6 +80,55 @@ export default {
|
|
|
return label.name;
|
|
|
|
|
|
return 'Not found';
|
|
|
+ },
|
|
|
+ isPredictionRunning: function () {
|
|
|
+ return this.jobs.filter(j => !j.finished && j.type === 'Model Interaction').length > 0;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ getJobs: function () {
|
|
|
+ this.$root.socket.get('/jobs')
|
|
|
+ .then(response => response.json())
|
|
|
+ .then(jobs => {
|
|
|
+ this.jobs = [];
|
|
|
+ jobs.forEach(this.addJob)
|
|
|
+ });
|
|
|
+ },
|
|
|
+ addJob: function (job) {
|
|
|
+ for (let j of this.jobs)
|
|
|
+ if (j.identifier === job.identifier)
|
|
|
+ return;
|
|
|
+
|
|
|
+ this.jobs.push(job);
|
|
|
+ },
|
|
|
+ removeJob: function (job) {
|
|
|
+ for (let i = 0; i < this.jobs.length; i++) {
|
|
|
+ if (this.jobs[i].identifier === job.identifier) {
|
|
|
+ this.jobs.splice(i, 1);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ editJob: function (job) {
|
|
|
+ for (let i = 0; i < this.jobs.length; i++) {
|
|
|
+ if (this.jobs[i].identifier === job.identifier) {
|
|
|
+ this.$set(this.jobs, i, job);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ predict_cropped_image: function () {
|
|
|
+ // This shouldn't happen, since the icon is only shown if a bounding box
|
|
|
+ // was selected.
|
|
|
+ if (!this.box)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (!this.isPredictionRunning) {
|
|
|
+ // TODO then / error
|
|
|
+ this.$root.socket.post(`/data/${this.file.identifier}/${this.box.identifier}/predict_bounding_box`, {
|
|
|
+ predict: true
|
|
|
+ });
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -78,4 +161,38 @@ export default {
|
|
|
border: 2px solid;
|
|
|
max-width: 100%;
|
|
|
}
|
|
|
+
|
|
|
+.label-container {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ align-items: baseline;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.create-predictions-icon {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ cursor: pointer;
|
|
|
+
|
|
|
+ margin: 0.4rem;
|
|
|
+ border: 1px solid whitesmoke;
|
|
|
+ border-radius: 0.5rem;
|
|
|
+
|
|
|
+ width: 1.6rem;
|
|
|
+ height: 1.6rem;
|
|
|
+}
|
|
|
+
|
|
|
+.create-predictions-icon.active {
|
|
|
+ background-color: rgba(0, 0, 0, 0.2);
|
|
|
+ box-shadow: inset 0 0 5px 0 rgba(0, 0, 0, 0.2);
|
|
|
+}
|
|
|
+
|
|
|
+.create-predictions-icon > img {
|
|
|
+ max-width: 1.1rem;
|
|
|
+ max-height: 1.1rem;
|
|
|
+ filter: invert(1);
|
|
|
+}
|
|
|
+
|
|
|
</style>
|