From 238b97c4c64f37206f30f4c50df9dd95fe1957f9 Mon Sep 17 00:00:00 2001 From: weiweiw <14335254+weiweiw22@user.noreply.gitee.com> Date: Mon, 25 Nov 2024 18:10:02 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=9C=A8=E7=BA=BF=E6=A0=87?= =?UTF-8?q?=E6=B3=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/OnlineAnnotationServiceOkHttp.java | 63 +++++++++++++++++++ .../AnnotationTaskAnnotatorEntity.java | 2 + .../domain/dataset/AnnotationTaskEntity.java | 2 +- .../bonus/ai/mapper/DatasetFileMapper.java | 3 +- .../dataset/AnnotationTaskServiceImpl.java | 40 ++++++++---- .../java/com/bonus/ai/utils/AverageUtil.java | 18 ++++-- .../resources/mapper/AnnotationTaskMapper.xml | 33 ++++++++-- .../resources/mapper/DatasetFileMapper.xml | 9 +-- sql/bonus_ai.sql | 31 ++++----- 9 files changed, 158 insertions(+), 43 deletions(-) diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/OnlineAnnotationServiceOkHttp.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/OnlineAnnotationServiceOkHttp.java index 64de405..23b6e70 100644 --- a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/OnlineAnnotationServiceOkHttp.java +++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/OnlineAnnotationServiceOkHttp.java @@ -21,6 +21,10 @@ public class OnlineAnnotationServiceOkHttp { private OnlineAnnotateConfig onlineAnnotateConfig; + /** + * project <------> 人工智能数据中心的标注任务 + * project 增删改查 + */ // Create project public ProjectParam createProject(ProjectInputParam projectParam) throws IOException { String url = onlineAnnotateConfig.getUrl()+ "/projects/"; @@ -81,6 +85,65 @@ public class OnlineAnnotationServiceOkHttp { return ProjectParam.fromJson(response); } + /** + * task <------> 人工智能数据中心的具体的标注文件 + * task 增删改查 + */ + public TaskParam createTask(TaskParam taskParam) throws IOException { + String url = onlineAnnotateConfig.getUrl()+ "/tasks/"; + String json = objectMapper.writeValueAsString(taskParam); + + RequestBody body = RequestBody.create(json, MediaType.parse("application/json")); + + log.error("Request JSON: " + json); + Request request = new Request.Builder() + .url(url) + .post(body) + .addHeader("Authorization", onlineAnnotateConfig.getApikey()) + .build(); + + String response = executeRequest(request); + return TaskParam.fromJson(response); + } + + public TaskParam getTaskById(String id) throws IOException { + String url = onlineAnnotateConfig.getUrl()+ "/tasks/" + id + "/"; + Request request = new Request.Builder() + .url(url) + .get() + .addHeader("Authorization", onlineAnnotateConfig.getApikey()) + .build(); + + String response = executeRequest(request); + return TaskParam.fromJson(response); + } + + public boolean deleteTaskById(String id) throws IOException { + String url = onlineAnnotateConfig.getUrl()+ "/tasks/" + id + "/"; + Request request = new Request.Builder() + .url(url) + .delete() + .addHeader("Authorization", onlineAnnotateConfig.getApikey()) + .build(); + + String response = executeRequest(request); + return response != null && !response.isEmpty(); + } + + public TaskParam updateTask(TaskParam taskParam) throws IOException { + String url = onlineAnnotateConfig.getUrl()+ "/tasks/"+ taskParam.getId() + "/"; + + String json = objectMapper.writeValueAsString(taskParam); + RequestBody body = RequestBody.create(json, MediaType.parse("application/json")); + Request request = new Request.Builder() + .url(url) + .patch(body) + .addHeader("Authorization", onlineAnnotateConfig.getApikey()) + .build(); + + String response = executeRequest(request); + return TaskParam.fromJson(response); + } // Execute the request private String executeRequest(Request request) throws IOException { try (Response response = client.newCall(request).execute()) { diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/dataset/AnnotationTaskAnnotatorEntity.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/dataset/AnnotationTaskAnnotatorEntity.java index 8f05907..35a745f 100644 --- a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/dataset/AnnotationTaskAnnotatorEntity.java +++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/dataset/AnnotationTaskAnnotatorEntity.java @@ -12,6 +12,8 @@ public class AnnotationTaskAnnotatorEntity { private Long reviewerId; // 审核人员 private Long taskId; // 任务ID private Long fileId; // 文件ID + private String fileUrl; // 文件URL + private Long labelStudioTaskId; //label studio 里对应的任务id private String description; // 描述 private String annotationStatus; // 标注状态 private String annotationResult; // 标注结果 (JSON串) diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/dataset/AnnotationTaskEntity.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/dataset/AnnotationTaskEntity.java index 503a375..62b7c9f 100644 --- a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/dataset/AnnotationTaskEntity.java +++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/dataset/AnnotationTaskEntity.java @@ -34,7 +34,7 @@ public class AnnotationTaskEntity extends BaseEntity { private String labels; /**在线标注工具里关联的项目id 和label studio里 项目详情*/ - Long projectId; + Long labelStudioProjectId; ProjectParam projectParam; private String annotateTaskStatus; diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/mapper/DatasetFileMapper.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/mapper/DatasetFileMapper.java index b0c1030..85adb74 100644 --- a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/mapper/DatasetFileMapper.java +++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/mapper/DatasetFileMapper.java @@ -1,5 +1,6 @@ package com.bonus.ai.mapper; +import com.bonus.ai.domain.DataSetBasicFileEntity; import com.bonus.ai.domain.dataset.DatasetFile; import org.apache.ibatis.annotations.Param; @@ -46,7 +47,7 @@ public interface DatasetFileMapper { * @param datasetId 数据集ID * @return 文件映射列表 */ - List selectFilesByDatasetId(Long datasetId); + List selectFilesByDatasetId(Long datasetId); /** * 更新是否已标注状态 diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/Impl/dataset/AnnotationTaskServiceImpl.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/Impl/dataset/AnnotationTaskServiceImpl.java index ce33548..c30f47c 100644 --- a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/Impl/dataset/AnnotationTaskServiceImpl.java +++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/Impl/dataset/AnnotationTaskServiceImpl.java @@ -1,23 +1,24 @@ package com.bonus.ai.service.Impl.dataset; import com.alibaba.fastjson.JSON; -import com.bonus.ai.client.OnlineAnnotateUtil; -import com.bonus.ai.client.OnlineAnnotationServiceOkHttp; -import com.bonus.ai.client.ProjectInputParam; -import com.bonus.ai.client.ProjectParam; +import com.bonus.ai.client.*; import com.bonus.ai.config.OnlineAnnotateConfig; +import com.bonus.ai.domain.DataSetBasicFileEntity; import com.bonus.ai.domain.dataset.*; import com.bonus.ai.mapper.AnnotationTaskMapper; import com.bonus.ai.mapper.DatasetFileMapper; import com.bonus.ai.service.dataset.AnnotationTaskService; import com.bonus.ai.utils.AverageUtil; +import com.bonus.common.core.utils.SpringUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Service; import javax.annotation.Resource; -import java.io.IOException; import java.util.List; import java.util.UUID; + + import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; @@ -26,6 +27,7 @@ import okhttp3.Response; @Service public class AnnotationTaskServiceImpl implements AnnotationTaskService { + private static final int THREAD_POOL_SIZE = 10; @Resource OnlineAnnotationServiceOkHttp onlineAnnotationService; @@ -46,6 +48,8 @@ public class AnnotationTaskServiceImpl implements AnnotationTaskService { @Autowired private AnnotationTaskAnnotatorEntity annotationTaskAnnotatorEntity; + private final ThreadPoolTaskExecutor executorService = SpringUtils.getBean(ThreadPoolTaskExecutor.class); + public void callWithOkHttp() throws Exception { OkHttpClient client = new OkHttpClient(); @@ -72,11 +76,12 @@ public class AnnotationTaskServiceImpl implements AnnotationTaskService { String labelConfig = OnlineAnnotateUtil.rectangleImageLabels(task.getLabels()); lSProject.setLabelConfig(labelConfig); String project = ""; + Long projectId = 0L; try { ProjectParam projectParams = onlineAnnotationService.createProject(lSProject); if (projectParams != null) { - Long projectId = Long.valueOf(projectParams.getId()); - task.setProjectId(projectId); + projectId = Long.valueOf(projectParams.getId()); + task.setLabelStudioProjectId(projectId); } }catch (Exception e){ e.printStackTrace(); @@ -87,9 +92,22 @@ public class AnnotationTaskServiceImpl implements AnnotationTaskService { task.setTaskUuid(uuid); status= annotationTaskMapper.insertAnnotationTask(task); //通过数据集id 从数据集和文件关联关系表获取文件信息 - List datafile = datasetFileMapper.selectFilesByDatasetId(task.getDatasetId()); + List datafile = datasetFileMapper.selectFilesByDatasetId(task.getDatasetId()); //根据标注任务file list 和标注人信息自动分配 List annotationTaskAnnotatorEntities = AverageUtil.distributeFiles(datafile, task); +// for (AnnotationTaskAnnotatorEntity entity : annotationTaskAnnotatorEntities) { +// //TODO 每个文件都需要创建label studio的 task id 与之唯一对应 +// executorService.execute(()->{ +// TaskParam taskParam = new TaskParam(); +// taskParam.setProject(projectId); +// try { +// onlineAnnotationService.createTask(taskParam); +// }catch(Exception e){ +// e.printStackTrace(); +// } +// }); + //entity.setLabelStudioTaskId(0); +// } // 获取project id以后调用AnnotationTaskMapper 和AnnotationTaskAnnotatorMapper 插入数据集 if(status == 1){ status = annotationTaskMapper.insertAnnotTaskannotator(annotationTaskAnnotatorEntities); @@ -123,7 +141,7 @@ public class AnnotationTaskServiceImpl implements AnnotationTaskService { //处理与label studio 相关的更新 List resultTasks = annotationTaskMapper.selectAnnotationTaskList(newTask); if (!resultTasks.isEmpty()) { - Long project = resultTasks.get(0).getProjectId(); + Long project = resultTasks.get(0).getLabelStudioProjectId(); //调用label studio 接口获取其project id ProjectInputParam lSProject= new ProjectInputParam(); lSProject.setTitle(task.getTaskName()); @@ -163,7 +181,7 @@ public class AnnotationTaskServiceImpl implements AnnotationTaskService { // //查询对应的label studio project id , 删除这个标注任务对应的label studio project List resultTask = annotationTaskMapper.selectAnnotationTaskList(task); if (!resultTask.isEmpty()){ - Long project = resultTask.get(0).getProjectId(); + Long project = resultTask.get(0).getLabelStudioProjectId(); //删除对应的标注任务文件关联关系表信息 onlineAnnotationService.deleteProjectById(String.valueOf(project)); } @@ -189,7 +207,7 @@ public class AnnotationTaskServiceImpl implements AnnotationTaskService { //查询对应的label studio project id , 从label studio这个标注任务对应的label studio project List resultTasks = annotationTaskMapper.selectAnnotationTaskList(task); if (!resultTasks.isEmpty()) { - Long project = resultTasks.get(0).getProjectId(); + Long project = resultTasks.get(0).getLabelStudioProjectId(); //删除对应的标注任务文件关联关系表信息 ProjectParam projectParam = onlineAnnotationService.getProjectById(String.valueOf(project)); AnnotationTaskEntity resultTask = resultTasks.get(0); diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/utils/AverageUtil.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/utils/AverageUtil.java index 9970350..5e1b6d2 100644 --- a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/utils/AverageUtil.java +++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/utils/AverageUtil.java @@ -1,5 +1,6 @@ package com.bonus.ai.utils; +import com.bonus.ai.domain.DataSetBasicFileEntity; import com.bonus.ai.domain.dataset.AnnotationTaskAnnotatorEntity; import com.bonus.ai.domain.dataset.AnnotationTaskEntity; import com.bonus.ai.domain.dataset.DatasetFile; @@ -15,28 +16,33 @@ public class AverageUtil { * @param taskEntity 标注任务实体 * @return 分配结果,包含标注者和审核者的文件分配 */ - public static List distributeFiles(List files, AnnotationTaskEntity taskEntity) { + public static List distributeFiles(List files, AnnotationTaskEntity taskEntity) { List annotatorEntities = new ArrayList<>(); List annotators = taskEntity.getAnnotators(); // 获取标注者列表 List reviewers = taskEntity.getReviewers(); // 获取审核者列表 + //这里应该是根据标注者列表和审核人列表里的人 与文件相关进行平均分 // 平分文件给标注者 for (int i = 0; i < files.size(); i++) { Long annotatorId = annotators.get(i % annotators.size()); // 轮流分配给标注者 AnnotationTaskAnnotatorEntity entity = new AnnotationTaskAnnotatorEntity(); entity.setAnnotatorId(annotatorId); // 设置标注者 ID entity.setFileId(files.get(i).getFileId()); // 假设 DatasetFile 有 getId() 方法 + + //这里需要将基础文件的url也获取并赋值 + entity.setFileUrl(files.get(i).getFileUrl() ); annotatorEntities.add(entity); } // 平分文件给审核者 - for (int i = 0; i < files.size(); i++) { + for (int i = 0; i < annotatorEntities.size(); i++) { Long reviewerId = reviewers.get(i % reviewers.size()); // 轮流分配给审核者 - AnnotationTaskAnnotatorEntity entity = new AnnotationTaskAnnotatorEntity(); - entity.setAnnotatorId(reviewerId); // 设置审核者 ID - entity.setFileId(files.get(i).getFileId()); // 假设 DatasetFile 有 getId() 方法 - annotatorEntities.add(entity); + annotatorEntities.get(i).setReviewerId(reviewerId); +// AnnotationTaskAnnotatorEntity entity = new AnnotationTaskAnnotatorEntity(); +// entity.setAnnotatorId(reviewerId); // 设置审核者 ID +// entity.setFileId(files.get(i).getFileId()); // 假设 DatasetFile 有 getId() 方法 +// annotatorEntities.add(entity); } return annotatorEntities; diff --git a/bonus-modules/bonus-ai/src/main/resources/mapper/AnnotationTaskMapper.xml b/bonus-modules/bonus-ai/src/main/resources/mapper/AnnotationTaskMapper.xml index a963b53..27002fc 100644 --- a/bonus-modules/bonus-ai/src/main/resources/mapper/AnnotationTaskMapper.xml +++ b/bonus-modules/bonus-ai/src/main/resources/mapper/AnnotationTaskMapper.xml @@ -14,7 +14,7 @@ - + @@ -49,7 +49,7 @@ - + @@ -89,7 +89,7 @@ labels, is_annotation_team, annotation_status, - project_id, + label_studio_project_id, del_flag, create_by, create_time, @@ -106,7 +106,7 @@ #{labels}, #{isStartTeam}, #{annotateTaskStatus}, - #{projectId}, + #{labelStudioProjectId}, #{delFlag}, #{createBy}, sysdate(), @@ -118,6 +118,18 @@ INSERT INTO ai_annotation_task_annotator_map task_id + + , file_id + + + , file_url + + + , file_url + + + , label_studio_task_id + , annotator_id @@ -139,6 +151,15 @@ #{taskId} + + , #{fileId} + + + , #{fileUrl} + + + , #{labelStudioTaskId} + , #{annotatorId} @@ -174,7 +195,7 @@ labels = #{labels}, is_annotation_team = #{isStartTeam}, annotation_status = #{annotateTaskStatus}, - project_id = #{projectId}, + label_studio_project_id = #{labelStudioProjectId}, del_flag = #{delFlag}, update_by = #{updateBy}, update_time = sysdate(), diff --git a/bonus-modules/bonus-ai/src/main/resources/mapper/DatasetFileMapper.xml b/bonus-modules/bonus-ai/src/main/resources/mapper/DatasetFileMapper.xml index c496467..d46bffb 100644 --- a/bonus-modules/bonus-ai/src/main/resources/mapper/DatasetFileMapper.xml +++ b/bonus-modules/bonus-ai/src/main/resources/mapper/DatasetFileMapper.xml @@ -42,10 +42,11 @@ - + SELECT bf.file_id AS fileId, bf.file_url AS fileUrl + FROM ai_dataset_file_map df + WHERE df.dataset_id = #{datasetId} + left join ai_basic_file bf on bf.file_id = df.file_id diff --git a/sql/bonus_ai.sql b/sql/bonus_ai.sql index 6acb561..5539f6d 100644 --- a/sql/bonus_ai.sql +++ b/sql/bonus_ai.sql @@ -4,21 +4,22 @@ drop table if exists ai_annotation_task; create table ai_annotation_task ( - task_id bigint(20) not null auto_increment comment '任务id', - dataset_id bigint(20) not null comment '数据集id', - task_name varchar(64) comment '任务名称', - task_uuid varchar(64) comment '任务唯一标识id', - description varchar(500) default '' comment '描述', - annotation_scene char(1) comment '标注场景(对应数据集的数据类型)', - annotation_type char(2) comment '标注类型(不同数据类型有不同的标注类型,如图片:图像分类,物体检测等)', - labels varchar(200) comment '允许的标签集', + task_id bigint(20) not null auto_increment comment '任务id', + dataset_id bigint(20) not null comment '数据集id', + task_name varchar(64) comment '任务名称', + task_uuid varchar(64) comment '任务唯一标识id', + description varchar(500) default '' comment '描述', + annotation_scene char(1) comment '标注场景(对应数据集的数据类型)', + annotation_type char(2) comment '标注类型(不同数据类型有不同的标注类型,如图片:图像分类,物体检测等)', + labels varchar(200) comment '允许的标签集', is_annotation_team char(1) comment '标注团队id(0未启用,1启用团队)', - annotation_status char(1) default '0' comment '0未标注,1正在标注,2已标注,3正在审核,4已审核', - del_flag char(1) default '0' comment '是否删除(0代表存在,1代表删除)', - create_by varchar(64) default '' comment '创建者', - update_by varchar(64) default '' comment '更新者', - update_time datetime default null comment '更新时间', - create_time datetime default null comment '创建时间', + annotation_status char(1) default '0' comment '0未标注,1正在标注,2已标注,3正在审核,4已审核', + label_studio_project_id bigint(20) comment "label studio 对应的projectId" + del_flag char(1) default '0' comment '是否删除(0代表存在,1代表删除)', + create_by varchar(64) default '' comment '创建者', + update_by varchar(64) default '' comment '更新者', + update_time datetime default null comment '更新时间', + create_time datetime default null comment '创建时间', primary key (task_id) )engine=innodb comment = '标注任务'; @@ -32,6 +33,8 @@ create table ai_annotation_task_annotator_map reviewer_id bigint(20) not null comment '审核人员', task_id bigint(20) not null comment '任务id', file_id bigint(20) not null comment '文件id', + file_url varchar(200) default '' comment '文件网络路径', + label_studio_task_id bigint(20) comment "label studio 对应的taskId" description varchar(500) default '' comment '描述', annotation_status char(1) comment '0未标注,1正在标注,2已标注,3正在审核,4 审核驳回,5已审核', annotation_result text comment '标注结果(json串)',