增加在线标注
This commit is contained in:
parent
4ddb442c71
commit
7b6429a58c
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 相关的更新
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
<!-- 更新是否已标注状态 -->
|
||||
|
|
|
|||
|
|
@ -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代表删除)',
|
||||
|
|
|
|||
Loading…
Reference in New Issue