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 @@
+
+
+
+
+
+
+ {{ mdiFormatListBulleted }}
+
+
+ {{ mdiText }}
+
+
+
+
+
+
+
+
+
+
+ {{ item.text }}
+
+ {{ item.suffixKey }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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
+ }
+}