Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

Raw File Download

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
content badge
swh:1:cnt:e9895c5122751d0b3cb55b01ddf4ed198bc4386d

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
<template>
  <v-row class="image-view fill-width mr-0">
    <v-col cols="12" class="image-content pa-0">
      <div class="image-legend">
        <div class="info-control">
          <div class="confidence-slider">
            <span class="" v-text="'Conf. ≥ 0.' + confidence"
            style="overflow: hidden; text-overflow: ellipsis;white-space: nowrap;"></span>

            <v-slider
              v-model="confidence"
              max="100"
              :color="'grey'"
              :track-color="'grey lighten-2'"
              :thumb-color="'grey darken-1'"
              style="
                                width: 110px;
                                height: 24px;
                            "
            ></v-slider>
          </div>
          <div class="bounding-label">
            <span style="margin-left: 15px; margin-right: 5px;">Label</span>
            <svg
              :width="30 + 5 * 8.1"
              height="26"
              style="margin: 0 0 0 0"
            >
              <rect
                class="background"
                rx="3.3333333333333335"
                ry="3.3333333333333335"
                x="0"
                y="0"
                height="32"
                :width="30 + 5 * 8.1"
                style="
                                    fill: rgb(235, 235, 243);
                                    fill-opacity: 1;
                                "
              ></rect>
              <text
                class="node-name"
                text-anchor="start"
                x="15"
                y="16"
                font-size="18px"
                dy=".3em"
                style="opacity: 1"
              >
                <!-- {{ selectClass }} -->
                bench
              </text>
            </svg>
          </div>
        </div>
        <div class="edit-legend">
          <!-- <svg
                        id="btn-edit"
                        :opacity="mode == 'grid' ? 0 : 1"
                        v-on:click="beginEditBoundingBox()"
                        height="20px"
                        viewBox="0 0 512 511"
                        width="20px"
                    >
                        <rect
                            x="0"
                            width="512"
                            y="0"
                            height="512"
                            fill="black"
                            fill-opacity="0"
                        ></rect>
                        <path
                            d="m405.332031 256.484375c-11.796875 0-21.332031 9.558594-21.332031 21.332031v170.667969c0 11.753906-9.558594 21.332031-21.332031 21.332031h-298.667969c-11.777344 0-21.332031-9.578125-21.332031-21.332031v-298.667969c0-11.753906 9.554687-21.332031 21.332031-21.332031h170.667969c11.796875 0 21.332031-9.558594 21.332031-21.332031 0-11.777344-9.535156-21.335938-21.332031-21.335938h-170.667969c-35.285156 0-64 28.714844-64 64v298.667969c0 35.285156 28.714844 64 64 64h298.667969c35.285156 0 64-28.714844 64-64v-170.667969c0-11.796875-9.539063-21.332031-21.335938-21.332031zm0 0"
                        />
                        <path
                            d="m200.019531 237.050781c-1.492187 1.492188-2.496093 3.390625-2.921875 5.4375l-15.082031 75.4375c-.703125 3.496094.40625 7.101563 2.921875 9.640625 2.027344 2.027344 4.757812 3.113282 7.554688 3.113282.679687 0 1.386718-.0625 2.089843-.210938l75.414063-15.082031c2.089844-.429688 3.988281-1.429688 5.460937-2.925781l168.789063-168.789063-75.414063-75.410156zm0 0"
                        />
                        <path
                            d="m496.382812 16.101562c-20.796874-20.800781-54.632812-20.800781-75.414062 0l-29.523438 29.523438 75.414063 75.414062 29.523437-29.527343c10.070313-10.046875 15.617188-23.445313 15.617188-37.695313s-5.546875-27.648437-15.617188-37.714844zm0 0"
                        />
                    </svg> -->

          <div id="left-page" @click="left_page()">
            <svg
              t="1626190600743"
              class="icon"
              viewBox="0 0 1024 1024"
              version="1.1"
              xmlns="http://www.w3.org/2000/svg"
              p-id="1177"
              width="20"
              height="20"
            >
              <path
                d="M495.976 476.195c19.777 17.656 21.494 48 3.837 67.774a48.003 48.003 0 0 1-3.837 3.836L536.082 512l-40.106-35.805zM864 212.083v-82.217a8 8 0 0 0-13.328-5.967L442.69 488.13c-0.9 0.804-1.754 1.657-2.558 2.557-11.772 13.184-10.626 33.412 2.558 45.183l407.983 364.231A8 8 0 0 0 864 894.134v-82.217a16 16 0 0 0-5.344-11.936L536.082 512l322.574-287.981A16 16 0 0 0 864 212.083zM495.976 476.195c19.777 17.656 21.494 48 3.837 67.774a48.003 48.003 0 0 1-3.837 3.836L536.082 512l-40.106-35.805zM864 212.083v-82.217a8 8 0 0 0-13.328-5.967L442.69 488.13c-0.9 0.804-1.754 1.657-2.558 2.557-11.772 13.184-10.626 33.412 2.558 45.183l407.983 364.231A8 8 0 0 0 864 894.134v-82.217a16 16 0 0 0-5.344-11.936L536.082 512l322.574-287.981A16 16 0 0 0 864 212.083z"
                p-id="1178"
              ></path>
              <path
                d="M223.976 476.195c19.777 17.656 21.494 48 3.837 67.774a48.003 48.003 0 0 1-3.837 3.836L264.082 512l-40.106-35.805zM592 212.083v-82.217a8 8 0 0 0-13.328-5.967L170.69 488.13c-0.9 0.804-1.754 1.657-2.558 2.557-11.772 13.184-10.626 33.412 2.558 45.183l407.983 364.231A8 8 0 0 0 592 894.134v-82.217a16 16 0 0 0-5.344-11.936L264.082 512l322.574-287.981A16 16 0 0 0 592 212.083zM223.976 476.195c19.777 17.656 21.494 48 3.837 67.774a48.003 48.003 0 0 1-3.837 3.836L264.082 512l-40.106-35.805zM592 212.083v-82.217a8 8 0 0 0-13.328-5.967L170.69 488.13c-0.9 0.804-1.754 1.657-2.558 2.557-11.772 13.184-10.626 33.412 2.558 45.183l407.983 364.231A8 8 0 0 0 592 894.134v-82.217a16 16 0 0 0-5.344-11.936L264.082 512l322.574-287.981A16 16 0 0 0 592 212.083z"
                p-id="1179"
              ></path>
            </svg>
          </div>
          <div id="right-page" @click="right_page()">
            <svg
              t="1626190628077"
              class="icon"
              viewBox="0 0 1024 1024"
              version="1.1"
              xmlns="http://www.w3.org/2000/svg"
              p-id="1968"
              width="20"
              height="20"
            >
              <path
                d="M160.117 212.026v-82.233a8 8 0 0 1 13.33-5.966l407.697 364.298c0.9 0.804 1.753 1.658 2.556 2.558 11.764 13.186 10.62 33.419-2.556 45.192L173.448 900.173a8 8 0 0 1-13.33-5.966v-82.233a16 16 0 0 1 5.338-11.93L487.814 512 165.456 223.957a16 16 0 0 1-5.339-11.931z m272.057 0v-82.233a8 8 0 0 1 13.33-5.966l407.697 364.298c0.9 0.804 1.753 1.658 2.556 2.558 11.764 13.186 10.62 33.419-2.556 45.192L445.505 900.173a8 8 0 0 1-13.33-5.966v-82.233a16 16 0 0 1 5.339-11.93L759.87 512 437.514 223.957a16 16 0 0 1-5.34-11.931z"
                p-id="1969"
              ></path>
            </svg>
          </div>
        </div>
        <!-- <svg
                    id="btn-svg"
                    width="60px"
                    height="24px"
                    viewBox="40 0 60 24"
                    style="margin-right: 20px"
                ></svg> -->
      </div>
      <div
        class="image-edit"
        :style="mode == 'grid' ? 'opacity: 0' : 'opacity: 1'"
      >
        <span
          class="image-edit-text"
          v-text="'Confirm the bounding box as'"
          style="margin-right: 4px"
        ></span>
        <!-- <input id="confirm-input" type="text" text-align="middle" value="truck" style="max-width: 48.626px;"> -->
        <v-autocomplete
          :value="
            selected_node.node_ids.length > 0
              ? classNames[selected_node.node_ids[0]]
              : ''
          "
          :items="classNames"
          height="20px"
          style="width: 20px!important; height: 26px!important; padding-top: 0px!important"
        ></v-autocomplete>
        <div
          id="confirm-icon"
          @click="confirmClick()"
          style="margin-right: 20px"
        >
          <!-- <svg
            t="1626855250123"
            class="icon"
            viewBox="0 0 1024 1024"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            p-id="1767"
            width="20"
            height="20"
            style="display: block"
          >
            <path
              d="M725.333333 213.333333v64h21.312H234.666667v469.333334h512V511.978667L810.666667 512v-64.042667V746.666667a64 64 0 0 1-64 64H234.666667a64 64 0 0 1-64-64V277.333333a64 64 0 0 1 64-64h490.666666z m105.152 41.6l45.696 44.8-315.626666 322.090667-156.842667-161.365333 45.909333-44.608 111.125334 114.346666L830.485333 254.933333z m-41.173333-25.301333c0.896 0.789333 1.749333 1.6 2.581333 2.432l-2.56 2.581333v-5.013333z"
              p-id="1768"
            ></path>
          </svg> -->
          <svg version="1.1" id="tc_1" 
          xmlns="http://www.w3.org/2000/svg" 
          xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
            width="20"
            height="20" viewBox="-393 720.6 144.8 121.3" style="display: block;enable-background:new -393 720.6 144.8 121.3;" xml:space="preserve">
<path d="M-280.3,723.2v12.5h4.2h-100v91.7h100v-45.8h12.5v-12.5v58.3c0,6.9-5.6,12.5-12.5,12.5h-100c-6.9,0-12.5-5.6-12.5-12.5
	v-91.7c0-6.9,5.6-12.5,12.5-12.5C-376.2,723.2-280.3,723.2-280.3,723.2z M-259.8,731.3l8.9,8.8l-61.6,62.9l-30.6-31.5l9-8.7
	l21.7,22.3L-259.8,731.3L-259.8,731.3z M-267.8,726.3c0.2,0.2,0.3,0.3,0.5,0.5l-0.5,0.5V726.3L-267.8,726.3z"/>
</svg>
        </div>
        <div id="add-icon" @click="addBoxClick()" style="margin-right: 2px">
          <svg t="1626943899206" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3106" width="20" height="20"><path d="M512 938.667c-235.264 0-426.667-191.424-426.667-426.645 0-235.264 191.403-426.688 426.667-426.688 235.243 0 426.667 191.424 426.667 426.688 0 235.221-191.424 426.645-426.667 426.645zM512 128c-211.733 0-384 172.267-384 384.021 0 211.733 172.267 383.979 384 383.979s384-172.245 384-383.979c0-211.755-172.267-384.021-384-384.021zM490.667 277.333l42.667 0 0 469.333-42.667 0 0-469.333zM277.333 490.667l469.333 0 0 42.667-469.333 0 0-42.667z" p-id="3107"></path></svg>
        </div>
        <div id="remove-icon" @click="removeBoundingBox()">
          <svg id="btn-remove" height="20px" viewBox="0 0 74 74" width="20px">
            <!-- <rect class="remove-icon"
                                x="0"
                                width="75"
                                y="0"
                                height="75"
                                fill="black"
                                fill-opacity="0"
                            ></rect> -->
            <path
              d="m51.512 71.833h-28.723a4.661 4.661 0 0 1 -4.631-4.3l-2.858-39.146a1 1 0 1 1 1.995-.146l2.854 39.142a2.652 2.652 0 0 0 2.636 2.45h28.727a2.651 2.651 0 0 0 2.635-2.45l2.853-39.142a1 1 0 0 1 2 .146l-2.857 39.142a4.661 4.661 0 0 1 -4.631 4.304z"
            />
            <path
              d="m58.741 29.314h-43.184a3.072 3.072 0 0 1 -3.069-3.068v-4.468a3.072 3.072 0 0 1 3.069-3.068h43.184a3.071 3.071 0 0 1 3.068 3.068v4.468a3.071 3.071 0 0 1 -3.068 3.068zm-43.184-8.6a1.07 1.07 0 0 0 -1.069 1.068v4.468a1.071 1.071 0 0 0 1.069 1.068h43.184a1.07 1.07 0 0 0 1.068-1.068v-4.472a1.069 1.069 0 0 0 -1.068-1.068z"
            />
            <path
              d="m58 20.71h-41.7a1 1 0 0 1 -.944-1.329l5.035-14.464a4.6 4.6 0 0 1 4.338-3.084h24.839a4.6 4.6 0 0 1 4.339 3.084l5.034 14.464a1 1 0 0 1 -.941 1.329zm-40.289-2h38.879l-4.572-13.136a2.6 2.6 0 0 0 -2.45-1.741h-24.839a2.6 2.6 0 0 0 -2.449 1.741z"
            />
            <path
              d="m51.5 20.71h-28.7a1 1 0 0 1 -.944-1.329l3.314-9.515a2.294 2.294 0 0 1 2.165-1.538h19.627a2.293 2.293 0 0 1 2.165 1.538l3.312 9.515a1 1 0 0 1 -.939 1.329zm-27.285-2h25.873l-2.85-8.187a.292.292 0 0 0 -.276-.2h-19.627a.292.292 0 0 0 -.276.2z"
            />
            <path
              d="m27.345 52.7a1 1 0 0 1 -.977-.79 11.029 11.029 0 0 1 18.814-9.887 1 1 0 1 1 -1.457 1.37 8.943 8.943 0 0 0 -6.576-2.842 9.038 9.038 0 0 0 -9.028 9.028 9.133 9.133 0 0 0 .2 1.911 1 1 0 0 1 -.767 1.187.953.953 0 0 1 -.209.023z"
            />
            <path
              d="m37.149 60.6a11.072 11.072 0 0 1 -8.033-3.473 1 1 0 1 1 1.457-1.37 8.944 8.944 0 0 0 6.576 2.843 9.038 9.038 0 0 0 9.028-9.028 9.157 9.157 0 0 0 -.119-1.47 1 1 0 0 1 1.974-.321 11.19 11.19 0 0 1 .145 1.791 11.042 11.042 0 0 1 -11.028 11.028z"
            />
            <path
              d="m40.083 44.563a1 1 0 0 1 -.192-1.98l3.388-.668-.667-3.388a1 1 0 0 1 1.962-.387l.861 4.369a1 1 0 0 1 -.788 1.175l-4.37.86a.918.918 0 0 1 -.194.019z"
            />
            <path
              d="m30.7 61.814a1 1 0 0 1 -.98-.807l-.861-4.369a1 1 0 0 1 .787-1.175l4.37-.86a1 1 0 1 1 .387 1.961l-3.389.668.668 3.389a1 1 0 0 1 -.782 1.179.989.989 0 0 1 -.2.014z"
            />
          </svg>
        </div>
      </div>
      <div id="image-div" style="overflow: auto;">
        <svg id="image-svg"></svg>
      </div>
    </v-col>
  </v-row>
</template>

<script>
import { mapActions, mapMutations, mapState } from "vuex";
import * as d3 from "d3";
import * as d3ContextMenu from "d3-context-menu";
import * as Global from "../plugins/global";
export default {
  name: "DetImage",
  data: () => ({
    confidence: 51,
    selectRect: null,
    mode: "unselected",
    isEditing: false,
    selectClass: "",
    // for debug
    items: ["foo", "bar", "fizz", "buzz"],
    values: ["foo", "bar"],
    value: null,
  }),
  watch: {
    selected_images() {
      let that = this;
      console.log("triger selected node");
      that.mode = "grid";
      that.update_data();
      that.update_view();
    },
    focus_image() {
      let that = this;
      console.log("triger focus image");
      that.mode = "focus-image";
      that.update_data();
      that.update_view();
    },
    focus_text() {
      let that = this;
      console.log("triger focus text");
      that.mode = "focus-text";
      that.update_data();
      that.update_view();
    },
    confidence() {
      console.log("confidence change");
      // this.one_image_boxes_threshold = this.confidence / 100;
      this.set_one_image_boxes_threshold(this.confidence / 100);
      this.update_data();
      this.update_view();
    },
  },
  computed: {
    ...mapState([
      "server_url",
      "selected_images",
      "focus_image",
      "focus_text",
      "one_image_boxes_threshold",
      "classNames",
      "selected_node",
    ]),
  },
  methods: {
    ...mapActions(["fetch_text_by_ids"]),
    ...mapMutations(["set_one_image_boxes_threshold", "set_focus_text"]),
    left_page() {
      this.change_grid_page(-1);
    },
    right_page() {
      this.change_grid_page(1);
    },
    change_confidence(){
      this.confidence = 50;
    },
    update_data() {
      let that = this;
      if (that.mode === "grid") {
        // get data by selected_node
        let data = that.selected_images;
        that.grid_images = data;
        that.grid_images.forEach((d) => {
          let dets = d.data.d;
          let boxes = [];
          for (let i = 0; i < dets.length; i++) {
            let x = dets[i][0];
            let w = dets[i][2] - dets[i][0];
            let y = dets[i][1];
            let h = dets[i][3] - dets[i][1];
            let idx = d.id + "-" + i;
            boxes.push({ x, y, w, h, idx });
          }
          d.boxes = boxes;
        });
        that.grid_page = 0;
        let end_num = that.x_grid_num * that.y_grid_num;
        if (that.grid_images.length < end_num) {
          end_num = that.grid_images.length;
        }
        that.grid_images_showing = that.grid_images.slice(0, end_num);
      } else if (that.mode === "focus-image") {
        // one-image mode, focus image
        let aspect_ratio = that.focus_image.h / that.focus_image.w;
        let width = 0;
        let height = 0;
        let image_x = 0;
        let image_y = 0;
        // let label = -1;
        let boxes = [];
        let dets = that.focus_image.d;
        that.dets = dets;
        if (aspect_ratio > that.aspect_ratio) {
          height = that.layout_height;
          width =
            (that.layout_height * that.focus_image.w) / that.focus_image.h;
          image_x = (that.layout_width - width) / 2;
          image_y = 0;
        } else {
          width = that.layout_width;
          height = (width * that.focus_image.h) / that.focus_image.w;
          image_x = 0;
          image_y = (that.layout_height - height) / 2;
        }
        for (let i = 0; i < dets.length; i++) {
          let x = width * dets[i][0] + image_x;
          let w = width * (dets[i][2] - dets[i][0]);
          let y = height * dets[i][1] + image_y;
          let h = height * (dets[i][3] - dets[i][1]);
          let conf = dets[i][4];
          let idx = that.focus_image.idx + "-" + i;
          let label = dets[i][5];
          boxes.push({ x, y, w, h, conf, idx, label });
        }
        this.one_image_data = {
          width,
          height,
          idx: that.focus_image.idx,
          x: image_x,
          y: image_y,
        };
        this.one_image_box_data = boxes;
      } else if (that.mode === "focus-text") {
        // one-image mode, focus image
        let aspect_ratio = that.focus_text.h / that.focus_text.w;
        let width = 0;
        let height = 0;
        let image_x = 0;
        let image_y = 0;
        let boxes = [];
        let dets = that.focus_text.d;
        that.dets = dets;
        if (aspect_ratio > that.aspect_ratio) {
          height = that.layout_height;
          width = (that.layout_height * that.focus_text.w) / that.focus_text.h;
          image_x = (that.layout_width - width) / 2;
          image_y = 0;
        } else {
          width = that.layout_width;
          height = (width * that.focus_text.h) / that.focus_text.w;
          image_x = 0;
          image_y = (that.layout_height - height) / 2;
        }
        that.image_x = image_x;
        that.image_y = image_y;
        that.image_width = width;
        that.image_height = height;
        for (let i = 0; i < dets.length; i++) {
          let x = width * dets[i][0] + image_x;
          let w = width * (dets[i][2] - dets[i][0]);
          let y = height * dets[i][1] + image_y;
          let h = height * (dets[i][3] - dets[i][1]);
          let conf = dets[i][4];
          let label = dets[i][5];
          let idx = that.focus_text.idx + "-" + i;
          let box_idx = i;
          boxes.push({ x, y, w, h, conf, idx, label, box_idx });
        }
        this.one_image_data = {
          width,
          height,
          idx: that.focus_text.idx,
          x: image_x,
          y: image_y,
        };
        this.one_image_box_data = boxes;
      }
      if (this.one_image_box_data) {
        this.one_image_box_data = this.one_image_box_data.filter(
          (d) => d.conf > that.one_image_boxes_threshold
        );
      }

      that.update_view();
      that.show_detail(null, -1);
    },
    update_view() {
      let that = this;

      if (that.mode === "grid") {
        that.one_image_group.style("display", "none");
        that.one_image_box_group.style("display", "none");
        that.grid_group.style("display", "block");
        that.grid_box_group.style("display", "block");
        that.detail_group.style("display", "block");
        that.cover_group.style("display", "block");
        that.e_data = that.svg.selectAll(".one-image").data([that.image_id]);
      } else {
        that.one_image_group.style("display", "block");
        that.one_image_box_group.style("display", "block");
        that.grid_group.style("display", "none");
        that.grid_box_group.style("display", "none");
        that.detail_group.style("display", "none");
        that.cover_group.style("display", "none");
        // for one-image, focus image
        that.one_images = that.one_image_group
          .selectAll(".info-image")
          .data([that.one_image_data], (d) => d.idx);
        that.one_image_boxes = that.one_image_box_group
          .selectAll(".info-box")
          .data(that.one_image_box_data, (d) => d.idx);
      }

      that.create();
      that.update();
      that.remove();
    },
    create() {
      let that = this;
      that.isEditing = false;
      that.main_group.selectAll("#drag-bar-g").remove();
      if (that.selectRect !== null) that.selectRect.remove();
      that.selectRect = null;
      that.selectClass = "";
      if (that.mode === "grid") {
        that.grid_group_g = that.grid_group
          .selectAll(".grid-image")
          .data(that.grid_images_showing);
        that.grid_group.selectAll("image").data(that.img_grid_urls);
        that.grid_group.selectAll("rect").data(that.img_grid_urls);
        let grid_group_enters = that.grid_group_g
          .enter()
          .append("g")
          .attr("class", "grid-image")
          .attr("transform", "translate(0,0)");

        grid_group_enters
          .append("image")
          .attr("class", "one-grid-image")
          .attr(
            "xlink:href",
            (d) => that.server_url + `/image/image?filename=${d.id}.jpg`
          )
          .attr(
            "x",
            (d, i) =>
              that.img_padding +
              (i % that.x_grid_num) * (that.grid_size + that.grid_offset)
          )
          .attr(
            "y",
            (d, i) =>
              that.img_padding +
              Math.floor(i / that.x_grid_num) *
                (that.grid_size + that.grid_offset) -
              2
          )
          .attr("width", that.grid_size)
          .attr("height", that.grid_size);

        that.cover_group_rect_g = that.cover_group
          .selectAll(".one-grid-image-rect-g")
          .data(that.grid_images_showing);
        let cover_group_rect_g_enters = that.cover_group_rect_g
          .enter()
          .append("g")
          .attr("class", "one-grid-image-rect-g")
          .attr("transform", "translate(0,0)");
        cover_group_rect_g_enters
          .append("rect")
          .attr("class", "one-grid-image-rect")
          .attr(
            "x",
            (d, i) =>
              that.img_padding +
              (i % that.x_grid_num) * (that.grid_size + that.grid_offset) -
              2
          )
          .attr(
            "y",
            (d, i) =>
              that.img_padding +
              Math.floor(i / that.x_grid_num) *
                (that.grid_size + that.grid_offset) -
              2
          )
          .attr("width", that.grid_size + 4)
          .attr("height", that.grid_size + 4)
          .attr("fill-opacity", 0)
          .on("click", function(_, d) {
            console.log("show detail", d, d.index);
            that.show_detail(d, d.index);
          });
        if (that.detail_pos !== -1) {
          that.grid_boxes
            .enter()
            .append("rect")
            .attr("class", "info-box")
            .attr("x", (d) => d.data.x * d.image_width + d.image_x)
            .attr("y", (d) => d.data.y * d.image_height + d.image_y)
            .attr("width", (d) => d.data.w * d.image_width)
            .attr("height", (d) => d.data.h * d.image_height)
            .style("fill", "none")
            .style("stroke", Global.BoxRed)
            .style("stroke-width", 1);
        }
      } else {
        // one image, focus_image
        that.one_images
          .enter()
          .append("image")
          .attr("class", "info-image")
          .attr("x", (d) => d.x)
          .attr("y", (d) => d.y)
          .attr("width", (d) => d.width)
          .attr("height", (d) => d.height)
          .attr(
            "href",
            (d) => that.server_url + `/image/origin_image?filename=${d.idx}.jpg`
          )
          .on("click", (_, d) => {
            console.log("click single images");
            that.fetch_text_by_ids([d.idx]);
          });
        // drag events
        let dragbarw = 12;
        let dragmove = function(event, d) {
          if (!that.isEditing) return;
          that.selectRect
            .attr("x", (d.x = Math.max(0, event.x)))
            .attr("y", (d.y = Math.max(0, event.y)))
            .attr("width", (d) => d.w)
            .attr("height", (d) => d.h);
          that.dragbarlefttop
            .attr("x", d.x - dragbarw / 2)
            .attr("y", d.y - dragbarw / 2);
          that.dragbarbottomright
            .attr("x", d.x + d.w - dragbarw / 2)
            .attr("y", d.y + d.h - dragbarw / 2);
          that.save_edit_data();
        };

        let rdragresize = function(event) {
          if (!that.isEditing) return;
          let d = that.selectRect.datum();
          d.x = event.dx + d.x;
          d.y = event.dy + d.y;
          d.w = d.w - event.dx;
          d.h = d.h - event.dy;
          that.selectRect
            .attr("x", (d.x = Math.max(0, event.x)))
            .attr("y", (d.y = Math.max(0, event.y)))
            .attr("width", (d) => d.w)
            .attr("height", (d) => d.h);
          that.dragbarlefttop
            .attr("x", d.x - dragbarw / 2)
            .attr("y", d.y - dragbarw / 2);
          that.save_edit_data();
        };

        let tdragresize = function(event) {
          if (!that.isEditing) return;
          let d = that.selectRect.datum();
          d.w = d.w + event.dx;
          d.h = d.h + event.dy;
          that.selectRect.attr("width", (d) => d.w).attr("height", (d) => d.h);
          that.dragbarbottomright
            .attr("x", d.x + d.w - dragbarw / 2)
            .attr("y", d.y + d.h - dragbarw / 2);
          that.save_edit_data();
        };

        let drag = d3.drag().on("drag", dragmove);

        that.one_image_boxes
          .enter()
          .append("rect")
          .attr("class", "info-box")
          .attr("x", (d) => d.x)
          .attr("y", (d) => d.y)
          .attr("width", (d) => d.w)
          .attr("height", (d) => d.h)
          .style("fill", "white")
          .style("fill-opacity", "0")
          .style("stroke", Global.BoxRed)
          .style("stroke-width", 1)
          .on("click", function(event, d) {
            // if (that.isEditing) return;
            that.selectRect = d3.select(this);
            that.selectec_rect_idx = d;
            that.one_image_boxes.style("stroke-width", 1);
            that.selectRect.style("stroke-width", 4);
            that.selectClass = that.$store.state.classNames[d.label];
            // add dragbar
            that.main_group.selectAll("#drag-bar-g").remove();
            let new_g = that.main_group.append("g").attr("id", "drag-bar-g");
            let draglefttop = d3.drag().on("drag", rdragresize);

            let dragbottomright = d3.drag().on("drag", tdragresize);

            that.dragbarlefttop = new_g
              .append("rect")
              .attr("x", d.x - dragbarw / 2)
              .attr("y", d.y - dragbarw / 2)
              .attr("height", dragbarw)
              .attr("id", "dragleft")
              .attr("width", dragbarw)
              .attr("fill", Global.BoxRed)
              .attr("fill-opacity", 1)
              .attr("cursor", "nw-resize")
              .call(draglefttop);

            that.dragbarbottomright = new_g
              .append("rect")
              .attr("x", d.x + d.w - dragbarw / 2)
              .attr("y", d.y + d.h - dragbarw / 2)
              .attr("height", dragbarw)
              .attr("id", "dragleft")
              .attr("width", dragbarw)
              .attr("fill", Global.BoxRed)
              .attr("fill-opacity", 1)
              .attr("cursor", "nw-resize")
              .call(dragbottomright);

            that.one_image_boxes.on("mousedown.drag", null);
            that.selectRect.call(drag);
            that.beginEditBoundingBox();
          })
          .append("title")
          .style("font-size", "16px")
          .text((d) => this.classNames[d.label]);
      }
    },
    update() {
      let that = this;
      if (that.mode === "grid") {
        that.e_data.attr(
          "href",
          (d) => that.server_url + `/image/image?filename=${d.id}.jpg`
        );
        that.grid_group
          .selectAll(".one-grid-image")
          .data(that.grid_images_showing)
          .attr(
            "xlink:href",
            (d) => that.server_url + `/image/image?filename=${d.id}.jpg`
          );
        that.cover_group
          .selectAll(".one-grid-image-rect")
          .data(that.grid_images_showing);

        that.grid_group_g
          .selectAll("rect")
          .transition()
          .duration(that.update_ani);

        that.grid_group_g
          .transition()
          .duration(that.update_ani)
          .attr(
            "transform",
            (d, i) =>
              "translate(" +
              0 +
              ", " +
              (that.detail_pos !== -1 &&
                Math.floor(i / that.x_grid_num) >
                  Math.floor(that.detail_pos / that.x_grid_num)) *
                (that.img_padding * 3 + that.img_size) +
              ")"
          );
        that.cover_group_rect_g
          .transition()
          .duration(that.update_ani)
          .attr(
            "transform",
            (d, i) =>
              "translate(" +
              0 +
              ", " +
              (that.detail_pos !== -1 &&
                Math.floor(i / that.x_grid_num) >
                  Math.floor(that.detail_pos / that.x_grid_num)) *
                (that.img_padding * 3 + that.img_size) +
              ")"
          );
        if (that.detail_pos !== -1) {
          that.grid_boxes
            .transition()
            .duration(that.update_ani)
            .attr("x", (d) => d.data.x * d.image_width + d.image_x)
            .attr("y", (d) => d.data.y * d.image_height + d.image_y)
            .attr("width", (d) => d.data.w * d.image_width)
            .attr("height", (d) => d.data.h * d.image_height)
            .style("fill", "none")
            .style("stroke", Global.BoxRed)
            .style("stroke-width", 1);
        }
      } else {
        that.one_images
          .attr("width", (d) => d.width)
          .attr("height", (d) => d.height)
          .attr(
            "href",
            (d) => that.server_url + `/image/origin_image?filename=${d.idx}.jpg`
          );
      }
    },
    remove() {
      let that = this;
      if (that.mode === "grid") {
        that.grid_group_g.exit().remove();
        that.cover_group_rect_g.exit().remove();
        if (that.detail_pos !== -1) {
          that.grid_boxes.exit().remove();
        }
      } else {
        that.one_images.exit().remove();
        that.one_image_boxes.exit().remove();

        setTimeout(() => {
          let bbox = that.one_image_group.node().getBoundingClientRect();
          that.svg
            .transition()
            .duration(that.update_ani)
            .attr("height", bbox.height + that.one_image_data.y);
        }, that.update_ani);
      }
    },
    show_detail(d, i) {
      let that = this;
      that.selected_id = d ? [d.id] : [];

      if (i === -1) {
        that.detail_pos = -1;
        that.detail_group.style("opacity", 0);
        that.detail_group.selectAll("image").remove();
        that.detail_group.append("image");
        that.grid_box_group.style("opacity", 0);
        that.grid_box_group.selectAll("rect").remove();
      } else {
        let img_url =
          that.server_url + `/image/origin_image?filename=${d.id}.jpg`;
        console.log("show detail:", that.detail_pos, img_url);
        that.layout_width = parseFloat(that.svg.attr("width"));
        let x_padding = (that.layout_width - that.img_size) / 2;
        let _y =
          that.img_padding +
          (Math.floor(i / that.x_grid_num) + 1) *
            (that.grid_size + that.grid_offset);
        let aspect_ratio = d.data.h / d.data.w;
        let image_width = that.img_size,
          image_height = that.img_size,
          image_x = x_padding,
          image_y = _y;
        if (aspect_ratio >= 1) {
          image_width /= aspect_ratio;
          image_x += (that.img_size - image_width) / 2;
        } else {
          image_height *= aspect_ratio;
          image_y += (that.img_size - image_height) / 2;
        }
        if (that.detail_pos === -1) {
          that.detail_pos = i;
          that.detail_group
            .transition()
            .duration(that.update_ani)
            .style("opacity", 1);
          that.grid_box_group
            .transition()
            .duration(that.update_ani)
            .style("opacity", 1);
          that.detail_group
            .select("image")
            .attr("xlink:href", img_url)
            .attr("x", that.img_padding)
            .attr("y", _y)
            .attr("width", 0)
            .attr("height", 0)
            .transition()
            .duration(that.update_ani)
            .attr("x", x_padding)
            .attr("y", _y)
            .attr("width", that.img_size)
            .attr("height", that.img_size);
          that.grid_boxes = that.grid_box_group.selectAll(".info-box").data(
            d.boxes.map((_d) => {
              return {
                idx: _d.idx,
                data: _d,
                image_x: image_x,
                image_y: image_y,
                image_width: image_width,
                image_height: image_height,
              };
            }),
            (_d) => _d.idx
          );
          that.grid_box_group
            .select("image")
            .attr("xlink:href", img_url)
            .attr("x", that.img_padding)
            .attr("y", _y)
            .attr("width", 0)
            .attr("height", 0)
            .transition()
            .duration(that.update_ani)
            .attr("x", x_padding)
            .attr("y", _y)
            .attr("width", that.img_size)
            .attr("height", that.img_size);
          that.update_view();
        } else if (that.detail_pos === i) {
          that.detail_pos = -1;
          that.detail_group
            .transition()
            .duration(that.update_ani)
            .style("opacity", 0);
          that.grid_box_group.style("opacity", 0);
          that.grid_box_group.selectAll("rect").remove();
          that.detail_group
            .select("image")
            .transition()
            .duration(that.update_ani)
            .attr("x", x_padding)
            .attr("y", _y)
            .attr("width", 0)
            .attr("height", 0);
          that.update_view();
        } else {
          that.detail_pos = i;
          that.detail_group
            .transition()
            .duration(that.update_ani)
            .style("opacity", 1);
          that.grid_box_group
            .transition()
            .duration(that.update_ani)
            .style("opacity", 1);
          that.detail_group
            .select("image")
            .attr("xlink:href", img_url)
            .transition()
            .duration(that.update_ani)
            .attr("x", x_padding)
            .attr("y", _y)
            .attr("width", 0)
            .attr("height", 0)
            .on("end", function() {
              let image = d3.select(this);
              image.remove();
            });
          that.grid_boxes = that.grid_box_group.selectAll(".info-box").data(
            d.boxes.map((_d) => {
              return {
                idx: _d.idx,
                data: _d,
                image_x: image_x,
                image_y: image_y,
                image_width: image_width,
                image_height: image_height,
              };
            }),
            (_d) => _d.idx
          );
          that.detail_group
            .append("image")
            .attr("xlink:href", img_url)
            .attr("x", that.img_padding)
            .attr("y", _y)
            .attr("width", 0)
            .attr("height", 0)
            .transition()
            .duration(that.update_ani)
            .attr("x", x_padding)
            .attr("y", _y)
            .attr("width", that.img_size)
            .attr("height", that.img_size);
          that.update_view();
        }
      }
      setTimeout(() => {
        let bbox = that.main_group.node().getBoundingClientRect();
        that.svg
          .transition()
          .duration(that.update_ani)
          .attr("height", bbox.height + 10);
      }, that.update_ani);
    },
    change_grid_page(direction) {
      let that = this;
      let start_num =
        that.x_grid_num * that.y_grid_num * (that.grid_page + direction);
      if (start_num >= 0 && start_num < that.grid_images.length) {
        that.grid_page += direction;
        let end_num = start_num + that.x_grid_num * that.y_grid_num;
        if (that.grid_images.length < end_num) {
          end_num = that.grid_images.length;
        }
        that.grid_images_showing = that.grid_images.slice(start_num, end_num);
        that.update_view();
        that.show_detail(null, -1);
      }
    },
    beginEditBoundingBox() {
      let that = this;
      this.isEditing = true;
      that.one_image_boxes.attr("cursor", "default");
      that.selectRect.attr("cursor", "move");
      // add context menu
      let selectNode =
        window.detection.selected_node.node_ids.length == 0
          ? null
          : window.detection.selected_node.node_ids[0];
      let menuOptions = [
        {
          title:
            selectNode == null
              ? "Confirm"
              : "Confirm as " + that.$store.state.classNames[selectNode],
          action: function() {
            console.log("Confirm!");
            // TODO: send confirm msg

            that.isEditing = false;
            that.one_image_boxes.attr("cursor", "default");
            that.main_group.selectAll("#drag-bar-g").remove();
            that.one_image_boxes.style("stroke-width", 1);
            that.selectRect.on("contextmenu", "none");
            that.selectRect = null;
          },
        },
        {
          title: "delete",
          action: function() {
            if (that.selectRect == null) return;
            that.isEditing = false;
            that.main_group.selectAll("#drag-bar-g").remove();
            // TODO: add 'deleting bounding box' code here
            // let removedBoundBox = that.selectRect.datum();

            that.selectRect.remove();
            that.selectRect = null;
            that.selectClass = "";
          },
        },
      ];
      that.selectRect.on("contextmenu", d3ContextMenu(menuOptions));
    },
    confirmClick() {
      console.log("confirm click");
    },
    addBoxClick() {
      console.log("addBoxClick click");
      this.focus_text.d.push([0.1, 0.1, 0.3, 0.3, 1, 0]);
      this.update_data();
      this.update_view();
    },
    save_edit_data(){
      let d = this.selectRect.datum();
      let d0 = (d.x - this.image_x) / this.image_width;
      let d1 = (d.y - this.image_y) / this.image_height;
      let d2 = d.w / this.image_width + d0;
      let d3 = d.h / this.image_height + d1;
      this.focus_text.d[d.box_idx][0] = d0;
      this.focus_text.d[d.box_idx][1] = d1;
      this.focus_text.d[d.box_idx][2] = d2;
      this.focus_text.d[d.box_idx][3] = d3;
    },
    removeBoundingBox() {
      let that = this;
      if (that.selectRect == null) return;
      that.isEditing = false;
      that.main_group.selectAll("#drag-bar-g").remove();
      // TODO: add 'deleting bounding box' code here
      // let removedBoundBox = that.selectRect.datum();

      that.selectRect.remove();
      that.selectRect = null;
      that.selectClass = "";
    },
    clean(){
      this.confidence = 50;
      this.mode = "grid";
      this.one_image_group.select("image").remove();
      this.one_image_box_group.selectAll("rect").remove();
      this.grid_box_group.selectAll("rect").remove();
      this.grid_group.selectAll("g").remove();
      this.detail_group.selectAll("image").remove();
      this.detail_group.append("image");
    }
  },
  async mounted() {
    window.image = this;
    let that = this;
    let container = d3.select(".image-content");
    // console.log("container", container);
    container.style("height", `${Global.WindowHeight * 0.405}px`);
    let bbox = container.node().getBoundingClientRect();
    that.width = bbox.width;
    that.height = bbox.height;
    that.layout_width = that.width - 20;
    that.layout_height = that.height - 75;
    that.img_padding = 10;
    that.grid_page = 0;
    that.x_grid_num = 7;
    that.y_grid_num = 5;
    that.grid_offset = 10;
    that.grid_size =
      Math.min(
        (that.layout_width - that.grid_offset) / that.x_grid_num,
        (that.layout_height - that.grid_offset) / that.y_grid_num
      ) - that.grid_offset;
    that.create_ani = Global.Animation / 4;
    that.update_ani = Global.Animation / 4;
    that.remove_ani = Global.Animation / 4;
    that.detail_pos = -1;
    that.mode = "grid";
    that.grid_images = [];
    that.grid_images_showing = [];
    that.img_grid_urls = [];
    that.aspect_ratio = that.layout_height / that.layout_width;
    that.img_size = 250;

    that.svg = container
      .select("#image-svg")
      .attr("width", that.width)
      .attr("height", that.layout_height - 40);

    that.svg
      .select("#btn-edit")
      .attr("x", that.width - 80)
      .attr("y", 0);
    that.svg
      .select("#btn-remove")
      .attr("x", that.width - 50)
      .attr("y", 0);

    that.main_group = that.svg
      .append("g")
      .attr("id", "main-group")
      .attr("transform", `translate(10,0)`);

    that.detail_group = that.main_group
      .append("g")
      .attr("id", "detail-group")
      .on("click", () => {
        console.log("detail group");
        that.fetch_text_by_ids(that.selected_id);
      });

    that.grid_group = that.main_group.append("g").attr("id", "grid-group");

    that.one_image_group = that.main_group
      .append("g")
      .attr("id", "one_image-group");

    that.grid_box_group = that.main_group
      .append("g")
      .attr("id", "grid_box-group");

    that.one_image_box_group = that.main_group
      .append("g")
      .attr("id", "one_image_box-group");

    that.cover_group = that.main_group.append("g").attr("id", "cover-group");

    if (
      (that.grid_offset + that.grid_size) * that.x_grid_num + that.grid_offset <
      that.layout_width
    ) {
      let delta_x =
        that.layout_width -
        (that.grid_offset + that.grid_size) * that.x_grid_num -
        that.grid_offset;
      that.grid_group.attr("transform", `translate(${delta_x / 2},0)`);
      that.grid_box_group.attr("transform", `translate(${delta_x / 2},0)`);
      that.cover_group.attr("transform", `translate(${delta_x / 2},0)`);
    } else {
      let delta_y =
        that.layout_height -
        (that.grid_offset + that.grid_size) * that.y_grid_num -
        that.grid_offset;
      that.grid_group.attr("transform", `translate(0,${delta_y / 2})`);
      that.grid_box_group.attr("transform", `translate(0,${delta_y / 2})`);
      that.cover_group.attr("transform", `translate(0,${delta_y / 2})`);
    }
    // let btn_svg = d3.select("#btn-svg");
    // let btn_data = [
    //   {
    //     name: "left",
    //     x: 0,
    //   },
    //   {
    //     name: "right",
    //     x: 30,
    //   },
    // ];
    // let btn_group = btn_svg
    //   .append("g")
    //   .attr("id", "btn-group")
    //   .attr("transform", `translate(50,0)`);
    // let arrows = btn_group.selectAll(".arrow").data(btn_data);
    // arrows
    //   .enter()
    //   .append("path")
    //   .attr("class", "arrow")
    //   .attr("d", (d) => {
    //     return Global.get_path_of_page_btn(d.x, 6, 18, 12, d.name);
    //   })
    //   .style("stroke-width", "2px")
    //   .style("fill", "none");
    // let rects = btn_group.selectAll(".btn-cover-rect").data(btn_data);
    // rects
    //   .enter()
    //   .append("rect")
    //   .attr("class", "btn-cover-rect")
    //   .attr("id", (d) => `btn-cover-rect-${d.name}`)
    //   .style("x", (d) => d.x - 2)
    //   .style("y", 4)
    //   .style("rx", 4)
    //   .style("width", 22)
    //   .style("height", 16)
    //   .style("stroke", "none")
    //   .style("fill", "black")
    //   .style("opacity", 0)
    //   .on("mouseenter", (_, d) => {
    //     d3.select(`#btn-cover-rect-${d.name}`)
    //       .transition()
    //       .duration(that.update_ani)
    //       .style("opacity", 0.3);
    //   })
    //   .on("mouseleave", (_, d) => {
    //     d3.select(`#btn-cover-rect-${d.name}`)
    //       .transition()
    //       .duration(that.update_ani)
    //       .style("opacity", 0);
    //   })
    //   .on("click", (_, d) => {
    //     if (d.name === "left") {
    //       that.change_grid_page(-1);
    //     } else {
    //       that.change_grid_page(1);
    //     }
    //   });
  },
};
</script>

<style>
.image-legend {
  display: flex;
  justify-content: space-between;
  width: 100%;
  align-items: center;
  color: rgb(114, 114, 114);
  margin-bottom: 8px;
}

.image-edit {
  margin-bottom: 2px;
  justify-content: flex-end;
  display: flex;
  align-items: center;
  color: rgb(114, 114, 114);
}

#confirm-input {
  color: rgb(114, 114, 114);
  font-size: 16px;
  height: 24px;
  background: white;
  border-style: solid;
  border: 1px solid #e0e0e0;
}

.confidence-slider,
.bounding-label {
  display: flex;
  flex-direction: row;
  align-items: center;
}

.edit-legend {
  display: flex;
  justify-content: center;
}

.image-view {
  height: 34%;
}

.image-content {
  border: 0;
  border-radius: 5px;
  margin: 0px 10px 10px 10px;
  /* height: calc(100% - 24px); */
  overflow-y: auto;
  overflow-x: hidden;
  /* overflow: hidden; */
  display: flex;
  flex-direction: column;
}

#btn-group {
  stroke: rgb(114, 114, 114);
}
.info-control {
  display: flex;
}

#remove-icon {
  display: flex;
}

#remove-icon:hover {
  background: #ddd;
}

#add-icon {
  display: flex;
}

#add-icon:hover {
  background: #ddd;
}


#confirm-icon:hover {
  background: #ddd;
}

#left-page {
  display: flex;
}

#left-page:hover {
  background: #ddd;
}

#right-page {
  display: flex;
}

#right-page:hover {
  background: #ddd;
}

.v-autocomplete__content .v-list__tile {
  height: auto;
}
</style>

back to top

Software Heritage — Copyright (C) 2015–2026, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API