增加在线标注

This commit is contained in:
weiweiw 2024-11-25 18:10:02 +08:00
parent 7fe112bbd9
commit 238b97c4c6
9 changed files with 158 additions and 43 deletions

View File

@ -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()) {

View File

@ -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串)

View File

@ -34,7 +34,7 @@ public class AnnotationTaskEntity extends BaseEntity {
private String labels;
/**在线标注工具里关联的项目id 和label studio里 项目详情*/
Long projectId;
Long labelStudioProjectId;
ProjectParam projectParam;
private String annotateTaskStatus;

View File

@ -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<DatasetFile> selectFilesByDatasetId(Long datasetId);
List<DataSetBasicFileEntity> selectFilesByDatasetId(Long datasetId);
/**
* 更新是否已标注状态

View File

@ -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<DatasetFile> datafile = datasetFileMapper.selectFilesByDatasetId(task.getDatasetId());
List<DataSetBasicFileEntity> datafile = datasetFileMapper.selectFilesByDatasetId(task.getDatasetId());
//根据标注任务file list 和标注人信息自动分配
List<AnnotationTaskAnnotatorEntity> 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<AnnotationTaskEntity> 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<AnnotationTaskEntity> 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<AnnotationTaskEntity> 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);

View File

@ -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<AnnotationTaskAnnotatorEntity> distributeFiles(List<DatasetFile> files, AnnotationTaskEntity taskEntity) {
public static List<AnnotationTaskAnnotatorEntity> distributeFiles(List<DataSetBasicFileEntity> files, AnnotationTaskEntity taskEntity) {
List<AnnotationTaskAnnotatorEntity> annotatorEntities = new ArrayList<>();
List<Long> annotators = taskEntity.getAnnotators(); // 获取标注者列表
List<Long> 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;

View File

@ -14,7 +14,7 @@
<result column="annotation_scene" property="annotateScene" jdbcType="CHAR"/>
<result column="annotation_type" property="annotateType" jdbcType="CHAR"/>
<result column="labels" property="labels" jdbcType="VARCHAR"/>
<result column="project_id" property="projectId" jdbcType="BIGINT"/>
<result column="label_studio_project_id" property="labelStudioProjectId" jdbcType="BIGINT"/>
<result column="isStartTeam" property="isStartTeam" jdbcType="CHAR"/>
<result column="annotation_status" property="annotateTaskStatus" jdbcType="CHAR"/>
<result column="del_flag" property="delFlag" jdbcType="CHAR"/>
@ -49,7 +49,7 @@
<select id="selectAnnotationTaskList" parameterType="com.bonus.ai.domain.dataset.AnnotationTaskEntity" resultMap="BaseResultMap">
SELECT task_id , dataset_id , task_name , task_uuid, description, annotation_scene, annotation_type ,
labels, is_annotation_team AS isStartTeam, annotation_status,
project_id, del_flag, create_by , create_time,
label_studio_project_id, del_flag, create_by , create_time,
update_by , update_time
FROM ai_annotation_task
WHERE del_flag = '0'
@ -73,7 +73,7 @@
</if>
</select>
<!-- <select id="selectAnnotationTaskListUUID" resultType="com.bonus.ai.domain.dataset.AnnotationTaskEntity">-->
<!-- SELECT project_id, task_id, task_uuid FROM ai_annotation_task where del_flag = 0-->
<!-- SELECT label_studio_project_id, task_id, task_uuid FROM ai_annotation_task where del_flag = 0-->
<!-- </select>-->
<!-- 插入标注任务 -->
@ -89,7 +89,7 @@
<if test="labels != null">labels,</if>
<if test="isStartTeam != null">is_annotation_team,</if>
<if test="annotateTaskStatus != null">annotation_status,</if>
<if test="projectId != null">project_id,</if>
<if test="labelStudioProjectId != null">label_studio_project_id,</if>
<if test="delFlag != null">del_flag,</if>
<if test="createBy != null">create_by,</if>
create_time,
@ -106,7 +106,7 @@
<if test="labels != null">#{labels},</if>
<if test="isStartTeam != null">#{isStartTeam},</if>
<if test="annotateTaskStatus != null">#{annotateTaskStatus},</if>
<if test="projectId != null">#{projectId},</if>
<if test="labelStudioProjectId != null">#{labelStudioProjectId},</if>
<if test="delFlag != null">#{delFlag},</if>
<if test="createBy != null">#{createBy},</if>
sysdate(),
@ -118,6 +118,18 @@
INSERT INTO ai_annotation_task_annotator_map
<trim prefix="(" suffix=")" suffixOverrides=",">
task_id
<if test="fileId != null">
, file_id
</if>
<if test="fileUrl != null">
, file_url
</if>
<if test="fileUrl != null">
, file_url
</if>
<if test="labelStudioTaskId != null">
, label_studio_task_id
</if>
<if test="annotatorId != null">
, annotator_id
</if>
@ -139,6 +151,15 @@
</trim>
<trim prefix="VALUES (" suffix=")" suffixOverrides=",">
#{taskId}
<if test="fileId != null">
, #{fileId}
</if>
<if test="fileUrl != null">
, #{fileUrl}
</if>
<if test="labelStudioTaskId != null">
, #{labelStudioTaskId}
</if>
<if test="annotatorId != null">
, #{annotatorId}
</if>
@ -174,7 +195,7 @@
<if test="labels != null">labels = #{labels},</if>
<if test="isStartTeam != null">is_annotation_team = #{isStartTeam},</if>
<if test="annotateTaskStatus != null">annotation_status = #{annotateTaskStatus},</if>
<if test="projectId != null">project_id = #{projectId},</if>
<if test="labelStudioProjectId != null">label_studio_project_id = #{labelStudioProjectId},</if>
<if test="delFlag != null">del_flag = #{delFlag},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
update_time = sysdate(),

View File

@ -42,10 +42,11 @@
<!-- </delete>-->
<!-- 根据数据集ID查询文件列表 -->
<select id="selectFilesByDatasetId" resultType="com.bonus.ai.domain.dataset.DatasetFile">
SELECT file_id AS fileId, dataset_id AS datasetId
FROM ai_dataset_file_map
WHERE dataset_id = #{datasetId}
<select id="selectFilesByDatasetId" parameterType="Long" resultType="com.bonus.ai.domain.DataSetBasicFileEntity">
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
</select>
<!-- 更新是否已标注状态 -->

View File

@ -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 '标注团队id0未启用,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串)',