mirror of https://github.com/doccano/doccano.git
pythonannotation-tooldatasetsactive-learningtext-annotationdatasetnatural-language-processingdata-labelingmachine-learning
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
332 lines
8.7 KiB
332 lines
8.7 KiB
<template>
|
|
<layout-text v-if="image.id">
|
|
<template #header>
|
|
<toolbar-laptop
|
|
:doc-id="image.id"
|
|
:enable-auto-labeling.sync="enableAutoLabeling"
|
|
:guideline-text="project.guideline"
|
|
:is-reviewd="image.isConfirmed"
|
|
:total="images.count"
|
|
class="d-none d-sm-block"
|
|
@click:clear-label="clear"
|
|
@click:review="confirm"
|
|
>
|
|
<button-zoom class="ms-2" @zoom-in="zoomIn" @zoom-out="zoomOut" />
|
|
</toolbar-laptop>
|
|
<toolbar-mobile :total="images.count" class="d-flex d-sm-none" />
|
|
</template>
|
|
<template #content>
|
|
<v-card>
|
|
<v-card-title>
|
|
<v-chip-group v-model="selectedLabelIndex" column>
|
|
<v-chip
|
|
v-for="item in labels"
|
|
:key="item.id"
|
|
:color="item.backgroundColor"
|
|
filter
|
|
:text-color="$contrastColor(item.backgroundColor)"
|
|
>
|
|
{{ item.text }}
|
|
<v-avatar
|
|
v-if="item.suffixKey"
|
|
right
|
|
color="white"
|
|
class="black--text font-weight-bold"
|
|
>
|
|
{{ item.suffixKey }}
|
|
</v-avatar>
|
|
</v-chip>
|
|
</v-chip-group>
|
|
</v-card-title>
|
|
<v-divider />
|
|
<v-segmentation
|
|
:highlight-id="highlightId"
|
|
:image-url="image.url"
|
|
:labels="bboxLabels"
|
|
:polygons="filteredRegions"
|
|
:selected-label="selectedLabel"
|
|
:scale="scale"
|
|
@add-polygon="add"
|
|
@delete-polygon="remove"
|
|
@select-polygon="selectRegion"
|
|
@update-polygon="update"
|
|
@update-scale="updateScale"
|
|
/>
|
|
</v-card>
|
|
</template>
|
|
<template #sidebar>
|
|
<annotation-progress :progress="progress" />
|
|
<list-metadata :metadata="image.meta" class="mt-4" />
|
|
<region-list
|
|
v-if="annotations.length > 0"
|
|
class="mt-4"
|
|
:regions="regionList"
|
|
@change-visibility="changeVisibility"
|
|
@delete-region="remove"
|
|
@hover-region="hoverRegion"
|
|
@unhover-region="unhoverRegion"
|
|
/>
|
|
</template>
|
|
</layout-text>
|
|
</template>
|
|
|
|
<script>
|
|
import { mdiFormatListBulleted, mdiText } from '@mdi/js'
|
|
import { toRefs, useContext } from '@nuxtjs/composition-api'
|
|
import _ from 'lodash'
|
|
import RegionList from '@/components/tasks/image/RegionList.vue'
|
|
import LayoutText from '@/components/tasks/layout/LayoutText'
|
|
import ListMetadata from '@/components/tasks/metadata/ListMetadata'
|
|
import VSegmentation from '@/components/tasks/segmentation/VSegmentation.vue'
|
|
import AnnotationProgress from '@/components/tasks/sidebar/AnnotationProgress.vue'
|
|
import ButtonZoom from '@/components/tasks/toolbar/buttons/ButtonZoom.vue'
|
|
import ToolbarLaptop from '@/components/tasks/toolbar/ToolbarLaptop'
|
|
import ToolbarMobile from '@/components/tasks/toolbar/ToolbarMobile'
|
|
import { useLabelList } from '@/composables/useLabelList'
|
|
|
|
export default {
|
|
components: {
|
|
AnnotationProgress,
|
|
ButtonZoom,
|
|
LayoutText,
|
|
ListMetadata,
|
|
RegionList,
|
|
ToolbarLaptop,
|
|
ToolbarMobile,
|
|
VSegmentation
|
|
},
|
|
layout: 'workspace',
|
|
|
|
validate({ params, query }) {
|
|
return /^\d+$/.test(params.id) && /^\d+$/.test(query.page)
|
|
},
|
|
|
|
setup() {
|
|
const { app } = useContext()
|
|
const { state, getLabelList, shortKeys } = useLabelList(app.$services.categoryType)
|
|
|
|
return {
|
|
...toRefs(state),
|
|
getLabelList,
|
|
shortKeys
|
|
}
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
annotations: [],
|
|
images: [],
|
|
project: {},
|
|
enableAutoLabeling: false,
|
|
labelOption: 0,
|
|
mdiText,
|
|
mdiFormatListBulleted,
|
|
progress: {},
|
|
highlightId: null,
|
|
selectedLabelIndex: undefined,
|
|
selectedRegion: undefined,
|
|
visibilities: {},
|
|
scale: 1
|
|
}
|
|
},
|
|
|
|
async fetch() {
|
|
this.images = await this.$services.example.fetchOne(
|
|
this.projectId,
|
|
this.$route.query.page,
|
|
this.$route.query.q,
|
|
this.$route.query.isChecked,
|
|
this.$route.query.ordering
|
|
)
|
|
const image = this.images.items[0]
|
|
if (this.enableAutoLabeling) {
|
|
await this.autoLabel(image.id)
|
|
}
|
|
await this.list(image.id)
|
|
},
|
|
|
|
computed: {
|
|
projectId() {
|
|
return this.$route.params.id
|
|
},
|
|
|
|
image() {
|
|
if (_.isEmpty(this.images) || this.images.items.length === 0) {
|
|
return {}
|
|
} else {
|
|
return this.images.items[0]
|
|
}
|
|
},
|
|
|
|
bboxLabels() {
|
|
return this.labels.map((label) => {
|
|
return {
|
|
id: label.id,
|
|
name: label.text,
|
|
color: label.backgroundColor
|
|
}
|
|
})
|
|
},
|
|
|
|
selectedLabel() {
|
|
if (this.selectedLabelIndex !== undefined) {
|
|
return this.labels[this.selectedLabelIndex]
|
|
} else {
|
|
return undefined
|
|
}
|
|
},
|
|
|
|
regionList() {
|
|
return this.annotations.map((annotation) => {
|
|
return {
|
|
id: annotation.uuid,
|
|
category: this.labels.find((label) => annotation.label === label.id).text,
|
|
color: this.labels.find((label) => annotation.label === label.id).backgroundColor,
|
|
visibility:
|
|
annotation.uuid in this.visibilities ? this.visibilities[annotation.uuid] : true
|
|
}
|
|
})
|
|
},
|
|
|
|
filteredRegions() {
|
|
return this.annotations
|
|
.filter((annotation) => this.visibilities[annotation.uuid] !== false)
|
|
.map((a) => {
|
|
return {
|
|
...a,
|
|
id: a.uuid
|
|
}
|
|
})
|
|
}
|
|
},
|
|
|
|
watch: {
|
|
'$route.query': '$fetch',
|
|
async enableAutoLabeling(val) {
|
|
if (val && !this.image.isConfirmed) {
|
|
await this.autoLabel(this.image.id)
|
|
await this.list(this.image.id)
|
|
}
|
|
},
|
|
|
|
async selectedLabel(newLabel) {
|
|
if (newLabel !== undefined && !!this.selectedRegion) {
|
|
this.selectedRegion.label = newLabel.id
|
|
await this.$services.segmentation.update(
|
|
this.projectId,
|
|
this.image.id,
|
|
this.selectedRegion.id,
|
|
this.selectedRegion
|
|
)
|
|
await this.list(this.image.id)
|
|
}
|
|
}
|
|
},
|
|
|
|
async created() {
|
|
this.getLabelList(this.projectId)
|
|
this.project = await this.$services.project.findById(this.projectId)
|
|
this.progress = await this.$repositories.metrics.fetchMyProgress(this.projectId)
|
|
},
|
|
|
|
methods: {
|
|
async list(imageId) {
|
|
this.annotations = await this.$services.segmentation.list(this.projectId, imageId)
|
|
},
|
|
|
|
async remove(id) {
|
|
delete this.visibilities[id]
|
|
const segmentation = this.annotations.find((a) => a.uuid === id)
|
|
await this.$services.segmentation.delete(this.projectId, this.image.id, segmentation.id)
|
|
await this.list(this.image.id)
|
|
},
|
|
|
|
async add(region) {
|
|
this.visibilities[region.id] = true
|
|
await this.$services.segmentation.create(
|
|
this.projectId,
|
|
this.image.id,
|
|
region.id,
|
|
region.label,
|
|
region.points
|
|
)
|
|
await this.list(this.image.id)
|
|
},
|
|
|
|
async update(region) {
|
|
const segmentation = this.annotations.find((a) => a.uuid === region.id)
|
|
await this.$services.segmentation.update(
|
|
this.projectId,
|
|
this.image.id,
|
|
segmentation.id,
|
|
region
|
|
)
|
|
await this.list(this.image.id)
|
|
},
|
|
|
|
changeVisibility(regionId, visibility) {
|
|
this.$set(this.visibilities, regionId, visibility)
|
|
this.visibilities = Object.assign({}, this.visibilities)
|
|
},
|
|
|
|
async clear() {
|
|
await this.$services.segmentation.clear(this.projectId, this.image.id)
|
|
await this.list(this.image.id)
|
|
},
|
|
|
|
async autoLabel(imageId) {
|
|
try {
|
|
await this.$services.segmentation.autoLabel(this.projectId, imageId)
|
|
} catch (e) {
|
|
console.log(e.response.data.detail)
|
|
}
|
|
},
|
|
|
|
async updateProgress() {
|
|
this.progress = await this.$repositories.metrics.fetchMyProgress(this.projectId)
|
|
},
|
|
|
|
async confirm() {
|
|
await this.$services.example.confirm(this.projectId, this.image.id)
|
|
await this.$fetch()
|
|
this.updateProgress()
|
|
},
|
|
|
|
hoverRegion(regionId) {
|
|
this.highlightId = regionId
|
|
},
|
|
|
|
unhoverRegion() {
|
|
this.highlightId = null
|
|
},
|
|
|
|
selectRegion(regionId) {
|
|
if (regionId) {
|
|
this.selectedRegion = this.annotations.find((r) => r.uuid === regionId)
|
|
this.selectedLabelIndex = this.labels.findIndex((l) => l.id === this.selectedRegion.label)
|
|
} else {
|
|
this.selectedRegion = undefined
|
|
this.selectedLabelIndex = undefined
|
|
}
|
|
},
|
|
|
|
updateScale(scale) {
|
|
this.scale = scale
|
|
},
|
|
|
|
zoomOut() {
|
|
this.scale -= 0.1
|
|
},
|
|
|
|
zoomIn() {
|
|
this.scale += 0.1
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.text-pre-wrap {
|
|
white-space: pre-wrap !important;
|
|
}
|
|
</style>
|