diff --git a/frontend/mixins/outputs/cocoMethods.js b/frontend/mixins/outputs/cocoMethods.js index 00f8ba8c39fd967aec54c216fdea89fa1b0cf441..2d28a976cef434fbfa449b1a19a946298afd21ed 100644 --- a/frontend/mixins/outputs/cocoMethods.js +++ b/frontend/mixins/outputs/cocoMethods.js @@ -10,10 +10,11 @@ export default { mixins: [imageAndLabelMethods], methods: { async getCategoriesArr (allLabels) { + var alreadyCheckedContentID = {} var categoriesArr = [] for (var labelIdx in allLabels) { var label = allLabels[labelIdx] - if (this.imagesObj[label["image_id"]]) { + if (this.imagesObj[label["image_id"]] && !alreadyCheckedContentID[label["label_content_id"]]) { // Category var labelResponse = await this.getLabelContentByID(label["label_content_id"]) var categoryObj = { @@ -21,6 +22,7 @@ export default { name: labelResponse["content_name"], supercategory: "" } + alreadyCheckedContentID[label["label_content_id"]] = true categoriesArr.push(categoryObj) } } diff --git a/frontend/mixins/outputs/datasetMethods.js b/frontend/mixins/outputs/datasetMethods.js new file mode 100644 index 0000000000000000000000000000000000000000..b48042f42c14dcb8d6d2f26424b39ff828da5bd0 --- /dev/null +++ b/frontend/mixins/outputs/datasetMethods.js @@ -0,0 +1,20 @@ +export default { + data () { + return { + dataset: '', + updateUI: false + } + }, + mounted () { + if (this.$route.query.dataset) { + this.dataset = this.$route.query.dataset + this.updateUI = !this.updateUI + } + }, + methods: { + changeDataset (newDataset) { + this.dataset = newDataset + this.updateUI = !this.updateUI + } + } +} \ No newline at end of file diff --git a/frontend/mixins/outputs/pascalMethods.js b/frontend/mixins/outputs/pascalMethods.js new file mode 100644 index 0000000000000000000000000000000000000000..fe679a034efc80752c12cd76a18f6b5a40a2ad93 --- /dev/null +++ b/frontend/mixins/outputs/pascalMethods.js @@ -0,0 +1,70 @@ +import { backendURL } from '~/config.js' +import imageAndLabelMethods from '~/mixins/outputs/imageAndLabelMethods' +export default { + data () { + return { + backendURL: backendURL, + imagesAttributes: {}, + pascalJSON: {} + } + }, + mixins: [imageAndLabelMethods], + methods: { + async getObjectsAttr (allLabels) { + var objectsAttr = {} + for (var labelIdx in allLabels) { + var label = allLabels[labelIdx] + if (this.pascalJSON[label["image_id"]]) { + var labelResponse = await this.getLabelContentByID(label["label_content_id"]) + // If exists, push to the bounding box + if (objectsAttr[label["label_content_id"]]) { + objectsAttr[label["label_content_id"]].boundingBox.push(this.getBoundingBoxAttr(label)) + this.pascalJSON[label["image_id"]].object.pop() + this.pascalJSON[label["image_id"]].object.push(objectsAttr[label["label_content_id"]]) + } else { + // Else, Construct new Obj + var objectAttr = { + name: labelResponse["content_name"], + pose: "Unspecified", + truncated: 0, + difficult: 0, + boundingBox: [this.getBoundingBoxAttr(label)] + } + objectsAttr[label["label_content_id"]] = objectAttr + this.pascalJSON[label["image_id"]].object.push(objectsAttr[label["label_content_id"]]) + } + } + } + }, + getBoundingBoxAttr (label) { + var x_top_left = Math.round(label.label_x_center - (0.5 * label.label_width)) + var y_top_left = Math.round(label.label_y_center - (0.5 * label.label_height)) + var x_bot_right = Math.round(label.label_x_center + (0.5 * label.label_width)) + var y_bot_right = Math.round(label.label_y_center - (0.5 * label.label_height)) + return { + xmin: x_top_left, + ymin: y_top_left, + xmax: x_bot_right, + ymax: y_bot_right + } + }, + async setSingleImageAttr (image, dataset) { + var url = this.backendURL + '/api/' + image.ImagePath + var imageMeta = await this.getImageMeta(url) + this.pascalJSON[image.ImageID] = { + folder: dataset, + filename: image.Filename, + path: url, + source: { + database: dataset + }, + size: { + width: imageMeta.naturalWidth, + height: imageMeta.naturalHeight, + depth: 3 + }, + object: [] + } + } + } +} \ No newline at end of file diff --git a/frontend/pages/main/coco.vue b/frontend/pages/main/coco.vue index e9e974b95793737c9eb49ada18a74e5e41490360..2dbab7e08d56611fd0dddfd1d493462043d65b22 100644 --- a/frontend/pages/main/coco.vue +++ b/frontend/pages/main/coco.vue @@ -43,6 +43,7 @@ <script> import cocoMethods from '~/mixins/outputs/cocoMethods' +import datasetMethods from '~/mixins/outputs/datasetMethods' import Label from '~/components/label/Label' import Dropdown from '~/components/dropdown/Dropdown' @@ -51,28 +52,16 @@ export default { Label, Dropdown }, - mixins: [cocoMethods], + mixins: [cocoMethods, datasetMethods], data () { return { - dataset: '', isOutputViewer: true, isLabeled: true, standard: 'coco', - search: '', - updateUI: false - } - }, - mounted () { - if (this.$route.query.dataset) { - this.dataset = this.$route.query.dataset - this.updateUI = !this.updateUI + search: '' } }, methods: { - changeDataset (newDataset) { - this.dataset = newDataset - this.updateUI = !this.updateUI - }, async getCOCOJSON () { var infoObj = this.getInfoObj() var licensesArr = this.getLicensesArr() diff --git a/frontend/pages/main/pascal.vue b/frontend/pages/main/pascal.vue index e6fd65fb49bed0fa0bd5ec3ad25819f948fe6eb6..405a9794e16b666f5cb9a750144eb9d2dff10885 100644 --- a/frontend/pages/main/pascal.vue +++ b/frontend/pages/main/pascal.vue @@ -2,23 +2,40 @@ <div class="col"> <div class="ml-4"> <div class="row"> - <div class="col-3"> - <h5 class="title users-margin"> - Complete XML - </h5> + <div class="col"> + <Dropdown + class="margin-dropdown" + :dropdown-value="dataset" + @onDatasetChanged="changeDataset" + /> + </div> + </div> + <div :key="updateUI" class="row animated fadeIn"> + <div class="col"> + <div style="display: flex"> + <h5 class="title users-margin"> + {{ dataset }} Pascal file: + <span v-if="!dataset" style="margin-left: 20px; font-size: 0.85rem;"> + Choose Folder First + </span> + </h5> + <button v-if="dataset" class="btn-white margin-download" @click="downloadXML()"> + {{ dataset }}.xml + <i class="ml-3 mt-1 fas fa-download" /> + </button> + </div> </div> - <button class="btn-white" @click="downloadXML()"> - XMLFile.xml - <i class="ml-3 mt-1 fas fa-download" /> - </button> </div> </div> - <div class="row"> + <div v-if="dataset" class="row" style="margin-top: -1.5rem;"> <Label + :key="updateUI" :is-output-viewer="isOutputViewer" :is-labeled="isLabeled" + :dataset="dataset" title="XML Per Image" viewer-u-r-l="/main/output-view" + :output="{type: 'xml', standard: standard}" /> </div> </div> @@ -26,89 +43,63 @@ <script> -import getAllLabeledImages from '~/mixins/image/getAllLabeledImages.js' -import { backendURL } from '~/config.js' +import datasetMethods from '~/mixins/outputs/datasetMethods' +import pascalMethods from '~/mixins/outputs/pascalMethods' import Convert from 'xml-js' import Label from '~/components/label/Label' +import Dropdown from '~/components/dropdown/Dropdown' + export default { components: { - Label + Label, + Dropdown }, - mixins: [getAllLabeledImages], + mixins: [datasetMethods, pascalMethods], data () { return { isOutputViewer: true, isLabeled: true, - backendURL: backendURL, standard: 'pascal', - search: '' - } - }, - computed: { - filterImages: function(){ - return this.labeledImages.filter((labs) => { - return labs.Filename.match(this.search) - }) + search: '', } }, methods: { - async getAllLabelJSON(standard){ - var url = '/api/label' - var response = await this.$axios(url).catch(error => console.log(error)) - if (response && response.status === 200) { - var rawJSON = response.data.data - return this.standardJSON(rawJSON, standard) - } else { - return null + async getPascalXML () { + this.pascalJSON = {} + var newPascalJSON = {"annotation": []} + var allImages = await this.getAllImages() + if (allImages) { + this.imagesAttributes = {} + var allLabels = await this.getAllLabel() + await this.setImagesAttr(allImages) + await this.getObjectsAttr(allLabels) + + for (var key in this.pascalJSON) { + newPascalJSON["annotation"].push(this.pascalJSON[key]) + } + return newPascalJSON } }, - standardJSON(rawJSON, standard){ - var JSONstandard = [] - rawJSON.forEach(element => { - var area = element.label_width * element.label_height, - x_top_left = element.label_x_center - (0.5 * element.label_width), - y_top_left = element.label_y_center - (0.5 * element.label_height), - x_bot_right = element.label_x_center + (0.5 * element.label_width), - y_bot_right = element.label_y_center - (0.5 * element.label_height), - json = { - id: element.label_id, - image_id: element.image_id, - category_id: element.label_content_id, - area: area, - bounding_box: { - }, - created_at: element.created_at, - updated_at: element.updated_at - } - if(standard == "coco"){ - json.bounding_box.x_top_left = x_top_left - json.bounding_box.y_top_left = y_top_left - json.bounding_box.width = element.label_width - json.bounding_box.height = element.label_height - JSONstandard.push(json) - }else if(standard == "pascal"){ - json.bounding_box.x_top_left = x_top_left - json.bounding_box.y_top_left = y_top_left - json.bounding_box.x_bot_right = x_bot_right - json.bounding_box.y_bot_right = y_bot_right - JSONstandard.push(json) - } - }) - return JSONstandard + async setImagesAttr (allImages) { + for (var imageIdx in allImages) { + var image = allImages[imageIdx] + await this.setSingleImageAttr(image, this.dataset) + } }, convertToXML(json){ var option = { compact: true, - spaces: 4 + spaces: 4, + attributes_key: 'annotation' } - var result = Convert.json2xml(JSON.stringify(json,0,4),option) + var result = Convert.json2xml(JSON.stringify(json,0,4), option) return result }, async downloadXML() { var filename = 'XMLFile.xml' var element = document.createElement('a') - var label = await this.getAllLabelJSON(this.standard) - var xml = this.convertToXML(label) + var newPascalJSON = await this.getPascalXML() + var xml = this.convertToXML(newPascalJSON) element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(xml)) element.setAttribute('download',filename) element.style.display = 'none' @@ -162,6 +153,11 @@ export default { padding-right: 1rem; } + .btn-white:hover { + background-color: #F7F7F7; + } + + .standard-dropdown{ background: yellow; width: 200px; @@ -176,4 +172,13 @@ export default { color: #1E889B; } + .margin-download { + margin-left: 2.25rem; + } + + .margin-dropdown { + margin-top: 3.5rem; + margin-left: -0.775rem; + } + </style> \ No newline at end of file