diff --git a/frontend/domain/models/tasks/boundingBox.ts b/frontend/domain/models/tasks/boundingBox.ts new file mode 100644 index 00000000..d6b737ed --- /dev/null +++ b/frontend/domain/models/tasks/boundingBox.ts @@ -0,0 +1,43 @@ +export class BoundingBoxItem { + constructor( + public id: number, + public uuid: string, + public label: number, + public x: number, + public y: number, + public width: number, + public height: number + ) {} + + static valueOf({ + id, + uuid, + label, + x, + y, + width, + height + }: { + id: number + uuid: string + label: number + x: number + y: number + width: number + height: number + }): BoundingBoxItem { + return new BoundingBoxItem(id, uuid, label, x, y, width, height) + } + + toObject(): Object { + return { + id: this.id, + uuid: this.uuid, + label: this.label, + x: this.x, + y: this.y, + width: this.width, + height: this.height + } + } +} diff --git a/frontend/pages/projects/_id/object-detection/index.vue b/frontend/pages/projects/_id/object-detection/index.vue new file mode 100644 index 00000000..0f721735 --- /dev/null +++ b/frontend/pages/projects/_id/object-detection/index.vue @@ -0,0 +1,319 @@ + + + + + diff --git a/frontend/plugins/services.ts b/frontend/plugins/services.ts index b320b20a..4e6d3358 100644 --- a/frontend/plugins/services.ts +++ b/frontend/plugins/services.ts @@ -42,6 +42,8 @@ import { DownloadFormatApplicationService } from '~/services/application/downloa import { APITagRepository } from '~/repositories/tag/apiTagRepository' import { TagApplicationService } from '~/services/application/tag/tagApplicationService' import { ApiRelationRepository } from '~/repositories/tasks/sequenceLabeling/apiRelationRepository' +import { ApiBoundingBoxRepository } from '~/repositories/tasks/boundingBox/apiBoundingBoxRepository' +import { BoundingBoxApplicationService } from '~/services/application/tasks/boundingBox/boundingBoxApplicationService' export interface Services { categoryType: LabelApplicationService @@ -67,6 +69,7 @@ export interface Services { downloadFormat: DownloadFormatApplicationService download: DownloadApplicationService tag: TagApplicationService + bbox: BoundingBoxApplicationService } declare module 'vue/types/vue' { @@ -97,6 +100,7 @@ const plugin: Plugin = (_, inject) => { const taskStatusRepository = new APITaskStatusRepository() const downloadFormatRepository = new APIDownloadFormatRepository() const downloadRepository = new APIDownloadRepository() + const boundingBoxRepository = new ApiBoundingBoxRepository() const categoryType = new LabelApplicationService(new APILabelRepository('category-type')) const spanType = new LabelApplicationService(new APILabelRepository('span-type')) @@ -113,6 +117,7 @@ const plugin: Plugin = (_, inject) => { sequenceLabelingRepository, linkRepository ) + const bbox = new BoundingBoxApplicationService(boundingBoxRepository) const seq2seq = new Seq2seqApplicationService(seq2seqRepository) const option = new OptionApplicationService(optionRepository) const config = new ConfigApplicationService(configRepository) @@ -148,7 +153,8 @@ const plugin: Plugin = (_, inject) => { taskStatus, downloadFormat, download, - tag + tag, + bbox } inject('services', services) } diff --git a/frontend/repositories/tasks/boundingBox/apiBoundingBoxRepository.ts b/frontend/repositories/tasks/boundingBox/apiBoundingBoxRepository.ts new file mode 100644 index 00000000..7fc74660 --- /dev/null +++ b/frontend/repositories/tasks/boundingBox/apiBoundingBoxRepository.ts @@ -0,0 +1,40 @@ +import { AnnotationRepository } from '@/domain/models/tasks/annotationRepository' +import { BoundingBoxItem } from '~/domain/models/tasks/boundingBox' + +export class ApiBoundingBoxRepository extends AnnotationRepository { + constructor() { + super(BoundingBoxItem) + } + + async list(projectId: string, exampleId: number): Promise { + const url = `/projects/${projectId}/examples/${exampleId}/bboxes` + const response = await this.request.get(url) + return response.data.map((box: any) => BoundingBoxItem.valueOf(box)) + } + + async create(projectId: string, exampleId: number, item: BoundingBoxItem): Promise { + const url = `/projects/${projectId}/examples/${exampleId}/bboxes` + await this.request.post(url, item.toObject()) + } + + async update( + projectId: string, + exampleId: number, + boxId: number, + item: BoundingBoxItem + ): Promise { + const url = `/projects/${projectId}/examples/${exampleId}/bboxes/${boxId}` + const response = await this.request.patch(url, item.toObject()) + return BoundingBoxItem.valueOf(response.data) + } + + async delete(projectId: string, exampleId: number, boxId: number): Promise { + const url = `/projects/${projectId}/examples/${exampleId}/bboxes/${boxId}` + await this.request.delete(url) + } + + async bulkDelete(projectId: string, exampleId: number, boxIds: number[]): Promise { + const url = `/projects/${projectId}/examples/${exampleId}/bboxes` + await this.request.delete(url, { ids: boxIds }) + } +} diff --git a/frontend/services/application/tasks/boundingBox/boundingBoxApplicationService.ts b/frontend/services/application/tasks/boundingBox/boundingBoxApplicationService.ts new file mode 100644 index 00000000..ed2b6d97 --- /dev/null +++ b/frontend/services/application/tasks/boundingBox/boundingBoxApplicationService.ts @@ -0,0 +1,55 @@ +import { AnnotationApplicationService } from '../annotationApplicationService' +import { BoundingBoxDTO } from './boundingBoxData' +import { ApiBoundingBoxRepository } from '~/repositories/tasks/boundingBox/apiBoundingBoxRepository' +import { BoundingBoxItem } from '~/domain/models/tasks/boundingBox' + +export class BoundingBoxApplicationService extends AnnotationApplicationService { + constructor(readonly repository: ApiBoundingBoxRepository) { + super(new ApiBoundingBoxRepository()) + } + + public async list(projectId: string, exampleId: number): Promise { + const items = await this.repository.list(projectId, exampleId) + return items.map((item) => new BoundingBoxDTO(item)) + } + + public async create( + projectId: string, + exampleId: number, + uuid: string, + label: number, + x: number, + y: number, + width: number, + height: number + ): Promise { + const item = new BoundingBoxItem(0, uuid, label, x, y, width, height) + try { + await this.repository.create(projectId, exampleId, item) + } catch (e: any) { + console.log(e.response.data.detail) + } + } + + public async update( + projectId: string, + exampleId: number, + annotationId: number, + item: BoundingBoxDTO + ): Promise { + const bbox = new BoundingBoxItem( + item.id, + item.uuid, + item.label, + item.x, + item.y, + item.width, + item.height + ) + try { + await this.repository.update(projectId, exampleId, annotationId, bbox) + } catch (e: any) { + console.log(e.response.data.detail) + } + } +} diff --git a/frontend/services/application/tasks/boundingBox/boundingBoxData.ts b/frontend/services/application/tasks/boundingBox/boundingBoxData.ts new file mode 100644 index 00000000..c4b29890 --- /dev/null +++ b/frontend/services/application/tasks/boundingBox/boundingBoxData.ts @@ -0,0 +1,21 @@ +import { BoundingBoxItem } from '~/domain/models/tasks/boundingBox' + +export class BoundingBoxDTO { + id: number + uuid: string + label: number + x: number + y: number + width: number + height: number + + constructor(item: BoundingBoxItem) { + this.id = item.id + this.uuid = item.uuid + this.label = item.label + this.x = item.x + this.y = item.y + this.width = item.width + this.height = item.height + } +}