|
@@ -6,28 +6,42 @@
|
|
|
<img alt="close button" src="@/assets/icons/cross.svg">
|
|
|
</div>
|
|
|
|
|
|
- <div class="label-container">
|
|
|
- <h3>{{ label }}</h3>
|
|
|
-
|
|
|
- <div 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>
|
|
|
|
|
|
+ <h3 v-html="label"></h3>
|
|
|
<div v-if="src">
|
|
|
<div class="image-container">
|
|
|
<img alt="crop" :src="src" />
|
|
|
</div>
|
|
|
|
|
|
- <div v-if="getAnnotator() !== ''" class="annotation-info">
|
|
|
+ <div class="label-container">
|
|
|
+ <div ref="create_prediction"
|
|
|
+ class="icon"
|
|
|
+ title="predict label for this image patch"
|
|
|
+ :class="{active: isPredictionRunning}"
|
|
|
+ @click="predictCroppedImage">
|
|
|
+ <img alt="predict label" src="@/assets/icons/rocket.svg">
|
|
|
+ </div>
|
|
|
+ <div v-if="hasLabel"
|
|
|
+ ref="remove_prediction"
|
|
|
+ class="icon"
|
|
|
+ title="remove this label"
|
|
|
+ @click="removeLabel">
|
|
|
+ <img alt="remove label" src="@/assets/icons/untag.svg">
|
|
|
+ </div>
|
|
|
+ <div v-if="!hasLabel"
|
|
|
+ ref="add_label"
|
|
|
+ class="icon"
|
|
|
+ title="set label"
|
|
|
+ @click="setLabel">
|
|
|
+ <img alt="set label" src="@/assets/icons/tag.svg">
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="hasAnnotator" class="annotation-info">
|
|
|
<table border="0">
|
|
|
<tr>
|
|
|
<td>Annotated by:</td>
|
|
|
- <td align="left"><b>{{getAnnotator()}}</b></td>
|
|
|
+ <td align="left"><b v-html="getAnnotator"></b></td>
|
|
|
</tr>
|
|
|
<tr v-if="box.confirmations.length !== 0">
|
|
|
<td colspan="2" align="left">Confirmed by:</td>
|
|
@@ -55,7 +69,7 @@
|
|
|
</div>
|
|
|
|
|
|
<div v-else>
|
|
|
- click a bounding box
|
|
|
+ Click a bounding box
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
@@ -101,21 +115,42 @@ export default {
|
|
|
|
|
|
return this.$root.socket.url(`/data/${file}/1024/${x}x${y}x${w}x${h}?uuid=${uuid}`)
|
|
|
},
|
|
|
+
|
|
|
+ hasLabel: function() {
|
|
|
+ return this.box && this.box.label_id !== null;
|
|
|
+ },
|
|
|
+
|
|
|
label: function () {
|
|
|
- if (!this.box)
|
|
|
- return false;
|
|
|
- if (this.box.label_id == null)
|
|
|
- return 'Unknown'
|
|
|
+ if (!this.hasLabel)
|
|
|
+ return '<em>Unknown</em>'
|
|
|
|
|
|
for (let label of this.labels)
|
|
|
if (label.identifier === this.box.label_id)
|
|
|
return label.name;
|
|
|
|
|
|
- return 'Not found';
|
|
|
+ return '<em>Not found</em>';
|
|
|
},
|
|
|
isPredictionRunning: function () {
|
|
|
return this.jobs.filter(j => !j.finished && j.type === 'Model Interaction').length > 0;
|
|
|
- }
|
|
|
+ },
|
|
|
+
|
|
|
+ hasAnnotator: function() {
|
|
|
+ return this.box && this.box.origin_user !== null;
|
|
|
+ },
|
|
|
+ getAnnotator: function () {
|
|
|
+ let current_user = this.$root.socket.username;
|
|
|
+ if (this.box.origin === 'user' && this.box.origin_user !== null) {
|
|
|
+ if (this.box.origin_user === current_user)
|
|
|
+ return "<em>You</em>"
|
|
|
+ else
|
|
|
+ return this.box.origin_user;
|
|
|
+ }
|
|
|
+ if (this.box.origin === 'pipeline') {
|
|
|
+ return 'Pipeline';
|
|
|
+ }
|
|
|
+ return '<em>Unknown</em>';
|
|
|
+
|
|
|
+ },
|
|
|
},
|
|
|
methods: {
|
|
|
getJobs: function () {
|
|
@@ -149,27 +184,29 @@ export default {
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
- getAnnotator: function () {
|
|
|
- if (this.box.origin === 'user' && this.box.origin_user !== null) {
|
|
|
- return this.box.origin_user;
|
|
|
- }
|
|
|
- if (this.box.origin === 'pipeline') {
|
|
|
- return 'pipeline';
|
|
|
- }
|
|
|
- return '';
|
|
|
|
|
|
- }, predict_cropped_image: function () {
|
|
|
+ removeLabel: function () {
|
|
|
+ if (!this.box)
|
|
|
+ return;
|
|
|
+ this.$emit("removeLabel", this.box.identifier);
|
|
|
+ },
|
|
|
+
|
|
|
+ setLabel: function () {
|
|
|
+ if (!this.box)
|
|
|
+ return;
|
|
|
+ console.log(this.box);
|
|
|
+ this.$emit("setLabel", this.box);
|
|
|
+ },
|
|
|
+
|
|
|
+ predictCroppedImage: 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
|
|
|
- });
|
|
|
- }
|
|
|
+ if (this.isPredictionRunning)
|
|
|
+ return
|
|
|
+ this.$emit("predictBox", this.file.identifier, this.box.identifier)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -183,6 +220,14 @@ export default {
|
|
|
position: relative;
|
|
|
padding: 2rem 1rem;
|
|
|
text-align: center;
|
|
|
+ border-left: 2px solid;
|
|
|
+ margin-left: 0.5rem;
|
|
|
+}
|
|
|
+
|
|
|
+h3 {
|
|
|
+ max-width: 100%;
|
|
|
+ max-height: 5%;
|
|
|
+ margin: 0.5em;
|
|
|
}
|
|
|
|
|
|
.close-button {
|
|
@@ -195,7 +240,8 @@ export default {
|
|
|
}
|
|
|
|
|
|
.close-button img {
|
|
|
- width: 1.5rem;
|
|
|
+ width: 24px;
|
|
|
+ height: 24px;
|
|
|
}
|
|
|
|
|
|
.image-container img {
|
|
@@ -211,7 +257,7 @@ export default {
|
|
|
justify-content: center;
|
|
|
}
|
|
|
|
|
|
-.create-predictions-icon {
|
|
|
+.icon {
|
|
|
display: flex;
|
|
|
justify-content: center;
|
|
|
align-items: center;
|
|
@@ -222,16 +268,15 @@ export default {
|
|
|
border: 1px solid whitesmoke;
|
|
|
border-radius: 0.5rem;
|
|
|
|
|
|
- width: 1.6rem;
|
|
|
- height: 1.6rem;
|
|
|
+ width: 32px;
|
|
|
+ height: 32px;
|
|
|
}
|
|
|
|
|
|
-.create-predictions-icon.active {
|
|
|
+.icon:hover, .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 {
|
|
|
+.icon > img {
|
|
|
max-width: 1.1rem;
|
|
|
max-height: 1.1rem;
|
|
|
filter: invert(1);
|