增加在线标注

This commit is contained in:
weiweiw 2024-11-26 12:44:17 +08:00
parent 4ddb442c71
commit 7b6429a58c
7 changed files with 208 additions and 71 deletions

View File

@ -15,7 +15,7 @@ import java.util.Map;
public class TaskParam {
private Data data;
private int id;
private Long id;
private Map<String, String> meta;
@JsonProperty("created_at")
private String createdAt;
@ -40,7 +40,7 @@ public class TaskParam {
private int unresolvedCommentCount;
@JsonProperty("last_comment_updated_at")
private String lastCommentUpdatedAt;
private int project;
private Long project;
@JsonProperty("updated_by")
private List<UpdatedBy> updatedBy;
@JsonProperty("file_upload")

View File

@ -48,7 +48,7 @@ public interface AnnotationTaskMapper
*/
List<AnnotationTaskEntity> selectAnnotationTaskList(AnnotationTaskEntity annotationTask);
int insertAnnotTaskannotator(List<AnnotationTaskAnnotatorEntity> annotationTaskAnnotatorEntities);
int insertAnnotTaskannotator(AnnotationTaskAnnotatorEntity annotationTaskAnnotatorEntities);
int deleteTaskById(Long taskId);

View File

@ -2,6 +2,7 @@ package com.bonus.ai.service.Impl.dataset;
import com.alibaba.fastjson.JSON;
import com.bonus.ai.client.*;
import com.bonus.ai.config.MinioConfig;
import com.bonus.ai.config.OnlineAnnotateConfig;
import com.bonus.ai.domain.DataSetBasicFileEntity;
import com.bonus.ai.domain.dataset.*;
@ -17,6 +18,7 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import okhttp3.OkHttpClient;
@ -42,40 +44,25 @@ public class AnnotationTaskServiceImpl implements AnnotationTaskService {
@Resource
private DatasetFileMapper datasetFileMapper;
@Autowired
private AnnotationTaskEntity annotationTaskEntity;
@Autowired
private AnnotationTaskAnnotatorEntity annotationTaskAnnotatorEntity;
@Resource
private MinioConfig minioConfig;
private final ThreadPoolTaskExecutor executorService = SpringUtils.getBean(ThreadPoolTaskExecutor.class);
public void callWithOkHttp() throws Exception {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://127.0.0.1:8080/api/projects")
.addHeader("Authorization", "Token bb0859020874c95809212f7b9616d15bbdbb2422")
.build();
try (Response response = client.newCall(request).execute()) {
System.out.println(response.body().string());
}
}
/**
* 创建标注任务
*/
@Override
public int createTask(AnnotationTaskEntity task){
int status =0;
//调用label studio 接口获取其project id
//1.调用label studio 接口创建project将projectid 关联到标注任务里
ProjectInputParam lSProject= new ProjectInputParam();
lSProject.setTitle(task.getTaskName());
lSProject.setDescription(task.getTaskDesc());
//需要将标签转换为json格式
String labelConfig = OnlineAnnotateUtil.rectangleImageLabels(task.getLabels());
lSProject.setLabelConfig(labelConfig);
String project = "";
Long projectId = 0L;
try {
ProjectParam projectParams = onlineAnnotationService.createProject(lSProject);
@ -85,32 +72,83 @@ public class AnnotationTaskServiceImpl implements AnnotationTaskService {
}
}catch (Exception e){
e.printStackTrace();
return 0;
}
//需要生成32位uuid
//2.插入标注任务表
String uuid = UUID.randomUUID().toString().replace("-", "");
task.setTaskUuid(uuid);
status= annotationTaskMapper.insertAnnotationTask(task);
AnnotationTaskEntity tempTask = new AnnotationTaskEntity();
tempTask.setTaskUuid(uuid);
List<AnnotationTaskEntity> tempTasks = annotationTaskMapper.selectAnnotationTaskList(task);
Long annotationTaskid;
if (!tempTasks.isEmpty()){
annotationTaskid = tempTasks.get(0).getTaskId();
} else {
annotationTaskid = 0L;
}
//3. 根据标注任务创建标注人等信息对任务里的文件进行分配
//通过数据集id 从数据集和文件关联关系表获取文件信息
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 插入数据集
for (AnnotationTaskAnnotatorEntity entity : annotationTaskAnnotatorEntities) {
//每个文件都需要创建label studio的 task id 与之唯一对应
entity.setTaskId(annotationTaskid);
}
// 使用 CountDownLatch 来确保所有任务完成后才执行下一步操作
CountDownLatch latch = new CountDownLatch(annotationTaskAnnotatorEntities.size());
//4. 文件分配后需要调用label studio 接口创建task将taskid与 文件和标注人表进行关联因为文件较多启用异步调用
for (AnnotationTaskAnnotatorEntity entity : annotationTaskAnnotatorEntities) {
//每个文件都需要创建label studio的 task id 与之唯一对应
entity.setTaskId(annotationTaskid);
Long finalProjectId = projectId;
executorService.execute(()->{
TaskParam taskParam = new TaskParam();
TaskParam.Data data = new TaskParam.Data();
String imageUrl = minioConfig.getEndpoint() + "/" + minioConfig.getBucketName() + "/"+ entity.getFileUrl();
data.setImage(imageUrl);
data.setText("Hello, world!");
taskParam.setData(data);
taskParam.setProject(finalProjectId);
try {
TaskParam lStask = onlineAnnotationService.createTask(taskParam);
synchronized (entity) {
entity.setLabelStudioTaskId(lStask.getId());
}
}catch(Exception e){
e.printStackTrace();
}
finally {
latch.countDown(); // 每个任务完成后减少计数
}
});
}
// 等待所有线程完成
try {
latch.await(); // 主线程会等待所有子线程完成
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
// 5. 将文件和标注人等信息 插入文件和标注人关联表
if(status == 1){
status = annotationTaskMapper.insertAnnotTaskannotator(annotationTaskAnnotatorEntities);
try {
for(AnnotationTaskAnnotatorEntity entity:annotationTaskAnnotatorEntities){
status = annotationTaskMapper.insertAnnotTaskannotator(entity);
}
}catch (Exception e){
log.error("Error updating task: ", e);
throw e; // 抛出异常以触发回滚
}
}
return status;
}
@ -135,7 +173,12 @@ public class AnnotationTaskServiceImpl implements AnnotationTaskService {
@Override
public int updateTask(AnnotationTaskEntity task){
//将信息也同步更新到label studio studio 对应的project
int result = annotationTaskMapper.updateAnnotationTaskById(task);
if (result != 1){
return 0;
}
//防止labels信息发生变化信息也同步更新到label studio studio 对应的project
AnnotationTaskEntity newTask = new AnnotationTaskEntity();
newTask.setTaskId(task.getTaskId());
//处理与label studio 相关的更新

View File

@ -4,6 +4,7 @@ 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;
import com.bonus.common.security.utils.SecurityUtils;
import java.util.ArrayList;
import java.util.List;
@ -22,6 +23,33 @@ public class AverageUtil {
List<Long> annotators = taskEntity.getAnnotators(); // 获取标注者列表
List<Long> reviewers = taskEntity.getReviewers(); // 获取审核者列表
//标注者与审核者都为空时默认为当前用户
if(("1").equals(taskEntity.getIsStartTeam())){
if (annotators == null || annotators.size() == 0 ) {
annotators = new ArrayList<>();
Long userId = SecurityUtils.getUserId();
annotators.add(userId);
}
if (reviewers == null || reviewers.size() == 0) {
reviewers = new ArrayList<>();
Long userId = SecurityUtils.getUserId();
reviewers.add(userId);
}
}else{
if (annotators == null || annotators.size() == 0) {
annotators = new ArrayList<>();
Long userId = SecurityUtils.getUserId();
annotators.add(userId);
}
if (reviewers == null || reviewers.size() == 0) {
reviewers = new ArrayList<>();
Long userId = SecurityUtils.getUserId();
reviewers.add(userId);
}
}
//这里应该是根据标注者列表和审核人列表里的人 与文件相关进行平均分
// 平分文件给标注者
for (int i = 0; i < files.size(); i++) {
@ -39,10 +67,6 @@ public class AverageUtil {
for (int i = 0; i < annotatorEntities.size(); i++) {
Long reviewerId = reviewers.get(i % reviewers.size()); // 轮流分配给审核者
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

@ -77,7 +77,7 @@
<!-- </select>-->
<!-- 插入标注任务 -->
<insert id="insertAnnotationTask" parameterType="com.bonus.ai.domain.dataset.AnnotationTaskEntity">
<insert id="insertAnnotationTask" parameterType="com.bonus.ai.domain.dataset.AnnotationTaskEntity" useGeneratedKeys="true" keyProperty="taskId">
INSERT INTO ai_annotation_task
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="datasetId != null">dataset_id,</if>
@ -114,73 +114,141 @@
<if test="updateTime != null">#{updateTime},</if>
</trim>
</insert>
<insert id="insertAnnotTaskannotator">
<insert id="insertAnnotTaskannotator" parameterType="com.bonus.ai.domain.dataset.AnnotationTaskAnnotatorEntity">
INSERT INTO ai_annotation_task_annotator_map
<trim prefix="(" suffix=")" suffixOverrides=",">
task_id
<if test="taskId != null">
task_id ,
</if>
<if test="fileId != null">
, file_id
file_id ,
</if>
<if test="fileUrl != null">
, file_url
</if>
<if test="fileUrl != null">
, file_url
file_url ,
</if>
<if test="labelStudioTaskId != null">
, label_studio_task_id
label_studio_task_id,
</if>
<if test="annotatorId != null">
, annotator_id
annotator_id ,
</if>
<if test="reviewerId != null">
, reviewer_id
reviewer_id ,
</if>
<if test="description != null and description != ''">
, description
description ,
</if>
<if test="annotationStatus != null">
, annotation_status
annotation_status ,
</if>
<if test="annotationResult != null">
, annotation_result
annotation_result ,
</if>
<if test="annotationResource != null">
, annotation_resource
annotation_resource ,
</if>
</trim>
<trim prefix="VALUES (" suffix=")" suffixOverrides=",">
#{taskId}
VALUES
<!-- <foreach collection="list" index="index" item="entity" separator=",">-->
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="taskId != null">
#{taskId},
</if>
<if test="fileId != null">
, #{fileId}
#{fileId},
</if>
<if test="fileUrl != null">
, #{fileUrl}
#{fileUrl},
</if>
<if test="labelStudioTaskId != null">
, #{labelStudioTaskId}
#{labelStudioTaskId},
</if>
<if test="annotatorId != null">
, #{annotatorId}
#{annotatorId},
</if>
<if test="reviewerId != null">
, #{reviewerId}
#{reviewerId},
</if>
<if test="description != null and description != ''">
, #{description}
<if test="description != null and entity.description != ''">
#{description} ,
</if>
<if test="annotationStatus != null">
, #{annotationStatus}
#{annotationStatus},
</if>
<if test="annotationResult != null">
, #{annotationResult}
#{annotationResult},
</if>
<if test="annotationResource != null">
, #{annotationResource}
#{annotationResource},
</if>
</trim>
<!-- </foreach>-->
</insert>
<!-- <insert id="insertAnnotTaskannotator" parameterType="java.util.List">-->
<!-- 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="labelStudioTaskId != null">-->
<!-- , label_studio_task_id-->
<!-- </if>-->
<!-- <if test="annotatorId != null">-->
<!-- , annotator_id-->
<!-- </if>-->
<!-- <if test="reviewerId != null">-->
<!-- , reviewer_id-->
<!-- </if>-->
<!-- <if test="description != null and description != ''">-->
<!-- , description-->
<!-- </if>-->
<!-- <if test="annotationStatus != null">-->
<!-- , annotation_status-->
<!-- </if>-->
<!-- <if test="annotationResult != null">-->
<!-- , annotation_result-->
<!-- </if>-->
<!-- <if test="annotationResource != null">-->
<!-- , annotation_resource-->
<!-- </if>-->
<!-- </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>-->
<!-- <if test="reviewerId != null">-->
<!-- , #{reviewerId}-->
<!-- </if>-->
<!-- <if test="description != null and description != ''">-->
<!-- , #{description}-->
<!-- </if>-->
<!-- <if test="annotationStatus != null">-->
<!-- , #{annotationStatus}-->
<!-- </if>-->
<!-- <if test="annotationResult != null">-->
<!-- , #{annotationResult}-->
<!-- </if>-->
<!-- <if test="annotationResource != null">-->
<!-- , #{annotationResource}-->
<!-- </if>-->
<!-- </trim>-->
<!-- </insert>-->
<!-- 根据任务ID更新标注任务 -->
<update id="updateAnnotationTaskById" parameterType="com.bonus.ai.domain.dataset.AnnotationTaskEntity">

View File

@ -46,8 +46,8 @@
<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
WHERE df.dataset_id = #{datasetId}
</select>
<!-- 更新是否已标注状态 -->

View File

@ -106,6 +106,8 @@ create table ai_dataset
data_source char(1) comment '数据来源(0:local,1:obs)',
input_path varchar(200) comment '输入路径',
output_path varchar(200) comment '输入路径',
input_id bigint(20) comment '输入文件夹id',
output_id bigint(20) comment '输出文件夹id'
annotation_status char(1) comment '标注状态(0no, 1 yes)',
is_public char(1) default '0' comment '0 no, 1 yes',
del_flag char(1) default '0' comment '是否删除(0代表存在,1代表删除)',