Forráskód Böngészése

refactor most crop-view components
- use new api (fix caching problems)
- reactivity
- correctly fire resize event

Eric Tröbs 3 éve
szülő
commit
2c17f5af5b

+ 17 - 30
webui/src/components/media/annotated-image.vue

@@ -9,8 +9,9 @@
                    :label="label"
                    @label="label = $event"
                    :labels="labels"
-                   @infoBox="toggleInfoBox"
                    :zoomBox="zoomBox"
+                   :infoBox="infoBox !== false"
+                   @infoBox="infoBox = $event; interaction=false"
                    @unzoom="zoomBox=false; interaction=false"
                    @prevzoom="$refs.overlay.prevZoom()"
                    @nextzoom="$refs.overlay.nextZoom()"/>
@@ -48,16 +49,17 @@
                             :video="video"
                             :results="results"
                             :labels="labels"
-                            @crop="selectCrop($event)"
+                            :crop="infoBox"
+                            @crop="infoBox = $event"
                             :zoom="zoomBox"
                             @zoom="zoomBox = $event"/>
       </div>
 
-      <cropped-image ref="info"
-                     :width="300"
-                     :margin="10"
+      <cropped-image v-if="infoBox !== false"
                      :labels="labels"
-                     @closed="infoBoxClosed"/>
+                     :file="current"
+                     :box="infoBox"
+                     @close="infoBox=false"/>
     </template>
   </div>
 </template>
@@ -75,7 +77,7 @@ export default {
   mounted: function () {
     // add resize listener
     window.addEventListener('resize', this.resize);
-    this.interval = setInterval(this.resize, 100);
+    this.interval = setInterval(this.resize, 1000);
     this.resize();
 
     // add result listener
@@ -132,7 +134,7 @@ export default {
         frame: 0,
         play: false
       },
-      selectedCrop: null,
+      infoBox: false,
       zoomBox: false,
       supported: {
         labeledImages: false,
@@ -183,23 +185,6 @@ export default {
     }
   },
   methods: {
-    toggleInfoBox: function () {
-      let infoBox = this.$refs.info;
-      infoBox.toggle();
-      if (infoBox.visible)
-        this.interaction = "info-box";
-      else {
-        this.interaction = false;
-        this.$refs.overlay.deselectAllBoxes()
-      }
-    },
-    selectCrop: function (event) {
-      this.$refs.info.show(event);
-    },
-    infoBoxClosed: function () {
-      this.interaction = false;
-      this.$refs.overlay.deselectAllBoxes()
-    },
     resize: function () {
       const rect = this.$refs.root.getBoundingClientRect();
 
@@ -267,6 +252,9 @@ export default {
       }
     },
     editResult: function (result) {
+      if (this.infoBox && result.identifier === this.infoBox.identifier)
+        this.infoBox = result;
+
       for (let i = 0; i < this.results.length; i++) {
         if (this.results[i].identifier === result.identifier) {
           this.$set(this.results, i, result);
@@ -322,12 +310,11 @@ export default {
           .then(response => response.json())
           .then(results => {
             this.results = results;
-          })
-        let infoBox = this.$refs.info;
-        if (infoBox == undefined)
-          return;
-        infoBox.box = null;
+          });
       }
+    },
+    infoBox: function () {
+      setTimeout(this.resize, 1);
     }
   }
 }

+ 5 - 27
webui/src/components/media/annotation-box.vue

@@ -1,7 +1,7 @@
 <template>
-  <div class="annotation-box btn"
+  <div class="annotation-box"
        :style="style"
-       :class="{draggable: draggable, shine: shine}"
+       :class="{draggable: draggable}"
        @mousedown.prevent="click" @touchstart.prevent="click">
     <template v-if="draggable">
       <div class="nw" @mousedown.prevent.stop="resize('nw')" @touchstart.prevent.stop="resize('nw')"/>
@@ -35,11 +35,6 @@ export default {
     'labels',
     'shine'
   ],
-  data: function() {
-    return {
-      selected: false
-    }
-  },
   computed: {
     labelName: function () {
       if (!this.box || !this.box.label)
@@ -66,16 +61,9 @@ export default {
         }
       }
 
-      let bgAlpha = 0.6;
-      let borderAlpha = 0.8;
-      if (!this.selected) {
-        bgAlpha *= 0.5;
-        borderAlpha *= 0.5;
-      }
-
       return {
-        backgroundColor: `rgba(${color}, ${bgAlpha})`,
-        borderColor: `rgba(${color}, ${borderAlpha})`,
+        backgroundColor: `rgba(${color}, ${this.shine ? 0.6 : 0.3})`,
+        borderColor: `rgba(${color}, ${this.shine ? 0.8 : 0.4})`,
         top: this.position.y * 100 + '%',
         left: this.position.x * 100 + '%',
         width: this.position.w * 100 + '%',
@@ -114,8 +102,7 @@ export default {
         event.stopPropagation();
       }
       if (this.croppable) {
-        this.$emit('crop', this);
-        this.selected = true;
+        this.$emit('crop', this.box);
         event.stopPropagation();
       }
     },
@@ -137,15 +124,6 @@ export default {
   border: 1px solid transparent;
 }
 
-.annotation-box.shine {
-  background: none !important;
-  border-width: 5px;
-}
-
-.btn {
-  cursor: pointer;
-}
-
 .label {
   position: absolute;
   bottom: 0.25rem;

+ 3 - 16
webui/src/components/media/annotation-overlay.vue

@@ -16,10 +16,10 @@
                     :zoomable="interaction === 'zoom-box'"
                     :croppable="interaction === 'info-box'"
                     :labels="labels"
-                    :shine="zoom"
+                    :shine="crop && box.identifier === crop.identifier"
                     @move="move"
                     @resize="resize"
-                    @crop="crop($event)"
+                    @crop="$emit('crop', $event)"
                     @zoom="zoomBox(index, $event)"/>
 
     <annotation-box v-if="current"
@@ -52,6 +52,7 @@ export default {
     'video',
     'results',
     'labels',
+    'crop',
     'zoom'
   ],
   data: function () {
@@ -269,20 +270,6 @@ export default {
           break;
       }
     },
-    crop: function (box) {
-      this.deselectAllBoxes();
-      if (box == undefined)
-        return;
-
-      this.$emit('crop', box);
-    },
-
-    deselectAllBoxes: function(){
-      this.$refs.box.forEach( child => {
-        child.selected = false;
-      })
-    },
-
     zoomBox: function (index) {
       if (this.boundingBoxes.length === 0) {
         this.zoomed = false;

+ 60 - 104
webui/src/components/media/cropped-image.vue

@@ -1,125 +1,81 @@
 <template>
-    <div v-if="visible" class="cropped-image">
-      <div id="close_btn" class="btn"
-        @mousedown.prevent="close"
-        @touchstart.prevent="close">
-        <img alt="close button" src="@/assets/icons/cross.svg">
-      </div>
-      <div class="image-container" :style="style">
-        <div v-if="box">
-          <h3>{{labelName}}</h3>
-          <img :src="src" ref="crop" alt="crop" />
-        </div>
-        <div v-else>Select a crop to show</div>
-      </div>
+  <div class="cropped-image">
+    <div class="close-button"
+         @mousedown.prevent="$emit('close', true)"
+         @touchstart.prevent="$emit('close', true)">
+      <img alt="close button" src="@/assets/icons/cross.svg">
     </div>
 
+    <div v-if="src" class="image-container">
+      <h3>{{ label }}</h3>
 
+      <img alt="crop" :src="src"/>
+    </div>
+    <div v-else>
+      click a bounding box
+    </div>
+  </div>
 </template>
 
 <script>
-    export default {
-        name: "cropped-image",
-        props: ["width", "margin", "labels"],
-
-
-        // mounted: function() {}
-        // destroyed: function() {}
-
-        data: function() {
-            return {
-                visible: false,
-                box: null,
-            }
-        },
-
-        computed: {
-          src: function () {
-            if (!this.box)
-              return ''
-            var width = this.width - 2 * this.margin /*padding*/;
-            return this.$root.socket.url(`/results/${this.box.identifier}/crop/${width}x${width}`)
-          },
-
-          style: function() {
-
-            return {
-              minWidth: `${this.width}px`,
-              margin: `${this.margin}px`,
-            }
-
-          },
-          labelName: function() {
-            if (!this.box)
-              return ''
-
-            if (this.box.label == null)
-              return 'Unknown'
-
-            let name = 'Not Found';
-            for (let label of this.labels){
-              if (label.identifier != this.box.label)
-                continue;
-
-              name = label.name;
-              break;
-            }
-            return name
-          },
-
-        },
-
-        methods: {
-            toggle: function () {
-              this.set_visible(!this.visible);
-            },
-
-            set_visible: function (visible) {
-              this.visible = visible;
-
-              if (!visible)
-                this.box = null;
-
-              this.$emit("visibility-change", visible);
-            },
-
-            show: function (box_element) {
-              this.box = box_element.box;
-            },
-
-            close: function() {
-              this.set_visible(false);
-              this.$emit("closed");
-            }
-        },
+export default {
+  name: "cropped-image",
+  props: ['labels', 'file', 'box'],
+  computed: {
+    src: function () {
+      if (!this.box)
+        return false;
+
+      const file = this.file.identifier;
+      const uuid = this.file.uuid;
+      const x = this.box.data.x;
+      const y = this.box.data.y;
+      const w = this.box.data.w;
+      const h = this.box.data.h;
+
+      return this.$root.socket.url(`/data/${file}/1024/${x}x${y}x${w}x${h}?uuid=${uuid}`)
+    },
+    label: function () {
+      if (!this.box)
+        return false;
+      if (this.box.label == null)
+        return 'Unknown'
+
+      for (let label of this.labels)
+        if (label.identifier === this.box.label)
+          return label.name;
+
+      return 'Not found';
     }
+  }
+}
 </script>
 
 <style scoped>
-
 .cropped-image {
+  width: 40%;
   height: 100%;
-
-  background-color: rgba(133, 133, 133, 0.7);
-}
-
-.image-container {
-  /*align-items: center;*/
-  justify-content: center;
+  background-color: #858585;
+  position: relative;
+  padding: 2rem 1rem;
   text-align: center;
-  display: flex;
 }
 
-.image-container img {
-  border-width: 2px;
-  border-style: solid;
+.close-button {
+  position: absolute;
+  top: 0;
+  right: 0;
+  padding: 0.5rem 0.67rem;
+  cursor: pointer;
+  filter: invert(1);
 }
 
-#close_btn {
-  justify-content: right;
-  text-align: right;
-
-  margin:  5px;
+.close-button img {
+  width: 1.5rem;
 }
 
+.image-container img {
+  border: 2px solid;
+  max-width: 100%;
+}
 </style>

+ 7 - 4
webui/src/components/media/options-bar.vue

@@ -65,7 +65,7 @@
          class="image"
          title="Show info of a bounding box (I)"
          :class="{active: interaction === 'info-box'}"
-         @click="$emit('infoBox')">
+         @click="$emit('interaction', 'info-box')">
       <img alt="zoom bounding box" src="@/assets/icons/info.svg">
     </div>
 
@@ -129,14 +129,14 @@ import LabelSelector from "@/components/media/label-selector";
 export default {
   name: "options-bar",
   components: {LabelSelector},
-  props: ['file', 'interaction', 'filter', 'label', 'labels', 'zoomBox'],
+  props: ['file', 'interaction', 'filter', 'label', 'labels', 'infoBox', 'zoomBox'],
   created: function () {
     // get data
     this.getJobs();
 
     this.$root.socket.get(`/projects/${this.$root.project.identifier}/model`)
-        .then(response => response.json())
-        .then(model => this.model = model);
+      .then(response => response.json())
+      .then(model => this.model = model);
 
     // subscribe to changes
     this.$root.socket.on('connect', this.getJobs);
@@ -225,6 +225,9 @@ export default {
         case 'v':
           this.$refs.zoom_next_annotation.click();
           break;
+        case 'i':
+          this.$refs.crop_info.click();
+          break;
       }
     },
     getJobs: function () {