Merge remote-tracking branch 'origin/main'

This commit is contained in:
jiang 2024-12-13 18:09:20 +08:00
commit f612b15f62
12 changed files with 560 additions and 12 deletions

View File

@ -0,0 +1,33 @@
package com.bonus.ai.client.intelligentAnnotation;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import java.util.List;
/**
* @author wangvivi
*/
@Data
public class AnnotationRequest {
@JsonProperty("image_urls")
private List<String> imageUrls;
@JsonProperty("target_labels")
private List<String> targetLabels;
@JsonProperty("conf_threshold")
private Double confThreshold;
public static AnnotationRequest fromJson(String json) throws JsonProcessingException {
return new ObjectMapper().readValue(json, AnnotationRequest.class);
}
public String toJson() throws Exception {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(this);
}
}

View File

@ -0,0 +1,49 @@
package com.bonus.ai.client.intelligentAnnotation;
import com.bonus.ai.client.ProjectParam;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* @author wangvivi
*/
@Data
public class AnnotationResult {
@JsonProperty("annotations") // 指定 JSON 属性名
private Map<String, List<Annotation>> annotations;
// 内部类表示每个图片路径下的标注信息
@Data
public static class Annotation {
@JsonProperty("label")
private String label;
@JsonProperty("x")
private double x;
@JsonProperty("y")
private double y;
@JsonProperty("width")
private double width;
@JsonProperty("height")
private double height;
}
public static AnnotationResult fromJson(String json) throws JsonProcessingException {
return new ObjectMapper().readValue(json, AnnotationResult.class);
}
public String toJson() throws Exception {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(this);
}
}

View File

@ -0,0 +1,62 @@
package com.bonus.ai.client.intelligentAnnotation;
import com.bonus.ai.client.ProjectInputParam;
import com.bonus.ai.client.ProjectParam;
import com.bonus.ai.client.TaskInputParam;
import com.bonus.ai.client.TaskParam;
import com.bonus.ai.config.OnlineAnnotateConfig;
import com.bonus.ai.domain.dataset.ReleaseVersionEntity;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service
public class IntelligentAnnotationServiceOkHttp {
private static final OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS).build();
private static final ObjectMapper objectMapper = new ObjectMapper(); // 用于JSON序列化和反序列化
/**
* 调用智能标注服务进行标注
*/
// Create project
public AnnotationResult intelligentAnnotation(String serviceEndPoint, AnnotationRequest annotationParam) throws IOException {
String url = serviceEndPoint ;
String json = objectMapper.writeValueAsString(annotationParam);
RequestBody body = RequestBody.create(json, MediaType.parse("application/json"));
log.error("Request JSON: " + json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
String response = executeRequest(request);
return AnnotationResult.fromJson(response);
}
private String executeRequest(Request request) throws IOException {
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
return response.body() != null ? response.body().string() : null;
}
}
}

View File

@ -0,0 +1,87 @@
package com.bonus.ai.controller;
import com.bonus.ai.client.intelligentAnnotation.AnnotationRequest;
import com.bonus.ai.client.intelligentAnnotation.AnnotationResult;
import com.bonus.ai.client.intelligentAnnotation.IntelligentAnnotationServiceOkHttp;
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.IntelligentAnnotationServiceEntity;
import com.bonus.ai.service.DataSetTeamMemberService;
import com.bonus.ai.service.dataset.AnnotationTaskService;
import com.bonus.ai.service.dataset.IntelligentAnnotationService;
import com.bonus.ai.utils.MinioUtil;
import com.bonus.common.core.web.domain.AjaxResult;
import com.bonus.common.security.annotation.RequiresPermissions;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.catalina.security.SecurityUtil;
import org.apache.commons.io.IOUtils;
import org.checkerframework.checker.units.qual.A;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@RestController
@RequestMapping("/intelligentAnnotation")
public class IntelligentAnnotationController {
@Resource
private IntelligentAnnotationService intelligentAnnotationService;
@Resource
private AnnotationTaskService annotationTaskService;
@RequiresPermissions("dataCenter:annotation:intelligentAnnotation:list")
@GetMapping("/service/list")
public AjaxResult getAIAnnotationServiceList() {
try {
List<IntelligentAnnotationServiceEntity> serviceList = intelligentAnnotationService.getAIAnnotationServiceList();
return AjaxResult.success(serviceList);
}catch (Exception e){
return AjaxResult.error("获取智能标注服务列表失败");
}
}
/**智能标注,预留接口
* @param task 标注文件的信息
* @return 返回影响的行数或错误信,AnnotationTaskAnnotatorEntity
*/
@PostMapping("/annotate")
public AjaxResult aiAnnotate(@Validated @RequestBody AnnotationTaskEntity task) throws IOException {
try {
IntelligentAnnotationServiceEntity service = intelligentAnnotationService.getAIAnnotationServiceById(task.getIntelligentAnnotationServiceId());
if (ObjectUtils.isEmpty(service)) {
return AjaxResult.error("未找到指定的AI服务");
}
//获取标注任务id和标签信息
AnnotationTaskEntity taskInfo = annotationTaskService.getTaskInfo(task.getTaskId());
//根据任务id获取由我标注的未标注的文件列表
List<DataSetBasicFileEntity> fileList = annotationTaskService.getMyAnnotaionTaskBasicFile(task.getTaskId(), "2");
//根据以上信息进行智能标注并将标注结果保存数据库
intelligentAnnotationService.aiAnnotate(taskInfo, fileList, service);
return AjaxResult.success();
}catch (Exception e){
return AjaxResult.error();
}
}
}

View File

@ -203,17 +203,6 @@ public class AnnotationTaskController extends BaseController {
}
/**智能标注,预留接口
* @param subTask 标注文件的信息
* @return 返回影响的行数或错误信
*/
@PostMapping("/aiAnnotate")
public AjaxResult aiAnnotate(@Validated @RequestBody AnnotationTaskAnnotatorEntity subTask) {
annotationTaskService.aiAnnotate(subTask);
return AjaxResult.success();
}
/**获取由我标注的未完成标注的任务列表
* @return 标注任务列表

View File

@ -79,7 +79,8 @@ public class AnnotationTaskEntity extends BaseEntity {
/**最新版本数*/
private String lastVersionName;
private String auditFailedReason;
/**用于传入智能标注信息*/
private Long intelligentAnnotationServiceId;
@Data

View File

@ -0,0 +1,15 @@
package com.bonus.ai.domain.dataset;
import com.bonus.common.core.web.domain.BaseEntity;
import lombok.Data;
@Data
public class IntelligentAnnotationServiceEntity extends BaseEntity {
private Long serviceId;
private String serviceName;
private String serviceUrl;
private String annotationLabels;
/**删除标志0代表存在 1代表删除 */
private String delFlag;
}

View File

@ -0,0 +1,48 @@
package com.bonus.ai.mapper;
import com.bonus.ai.domain.dataset.IntelligentAnnotationServiceEntity;
import java.util.List;
/**
* 文件基础Mapper接口
*
* @author bonus
* @date 2024-11-14
*/
public interface IntelligentAnnotationMapper
{
/**
* 插入智能标注服务信息
* @param serviceInfo 智能标注服务信息
* @return 影响的行数
*/
int insertIntelligentAnnotationService(IntelligentAnnotationServiceEntity serviceInfo);
int deleteIntelligentAnnotationService(Long serviceId);
/**
* 更新智能标注服务信息
* @param serviceInfo 智能标注服务信息
* @return 影响的行数
*/
int updateIntelligentAnnotationService(IntelligentAnnotationServiceEntity serviceInfo);
/**
* 获取所有未被删除的服务信息列表
* @return 服务信息
*/
List<IntelligentAnnotationServiceEntity> getIntelligentAnnotationService();
/**
* 获取所有未被删除的服务信息列表
* @return 服务信息
*/
IntelligentAnnotationServiceEntity getIntelligentAnnotationServiceById(Long serviceId);
}

View File

@ -0,0 +1,128 @@
package com.bonus.ai.service.Impl.dataset;
import com.bonus.ai.client.intelligentAnnotation.AnnotationRequest;
import com.bonus.ai.client.intelligentAnnotation.AnnotationResult;
import com.bonus.ai.client.intelligentAnnotation.IntelligentAnnotationServiceOkHttp;
import com.bonus.ai.config.MinioConfig;
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.IntelligentAnnotationServiceEntity;
import com.bonus.ai.mapper.AnnotationTaskMapper;
import com.bonus.ai.mapper.DatasetMapper;
import com.bonus.ai.mapper.IntelligentAnnotationMapper;
import com.bonus.ai.service.dataset.IntelligentAnnotationService;
import com.bonus.common.core.web.domain.AjaxResult;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource;
import java.io.File;
import java.util.*;
@Slf4j
@Service
public class IntelligentAnnotationServiceImpl implements IntelligentAnnotationService {
@Resource
private IntelligentAnnotationMapper intelligentAnnotationMapper;
@Resource
IntelligentAnnotationServiceOkHttp intelligentAnnotationClient;
@Resource
private AnnotationTaskMapper annotationTaskMapper;
@Resource
private MinioConfig minioConfig;
@Override
public List<IntelligentAnnotationServiceEntity> getAIAnnotationServiceList() {
return intelligentAnnotationMapper.getIntelligentAnnotationService();
}
@Override
public IntelligentAnnotationServiceEntity getAIAnnotationServiceById(Long serviceId) {
return intelligentAnnotationMapper.getIntelligentAnnotationServiceById(serviceId);
}
@Override
public AjaxResult aiAnnotate(AnnotationTaskEntity taskInfo, List<DataSetBasicFileEntity> fileList, IntelligentAnnotationServiceEntity aiAnnotationServiceInfo) {
try {
if (ObjectUtils.isEmpty(taskInfo) || ObjectUtils.isEmpty(fileList) || fileList.isEmpty() || ObjectUtils.isEmpty(aiAnnotationServiceInfo))
{
return AjaxResult.error("标注任务或智能标注服务不存在,或没有未标注文件");
}
List<String> imageUrls = new ArrayList<>();
String minioUrl =minioConfig.getEndpoint() + File.separator + minioConfig.getBucketName()+File.separator;
for (DataSetBasicFileEntity file : fileList) {
imageUrls.add(minioUrl + file.getFileUrl());
}
String serviceUrl = aiAnnotationServiceInfo.getServiceUrl();
AnnotationRequest request = new AnnotationRequest();
request.setConfThreshold(0.1);
request.setImageUrls(imageUrls);
request.setTargetLabels(ObjectUtils.isEmpty(taskInfo.getLabels()) ? new ArrayList<>() : Arrays.asList(taskInfo.getLabels().split(",")));
AnnotationResult result = intelligentAnnotationClient.intelligentAnnotation(serviceUrl, request);
if (ObjectUtils.isEmpty(result)){
log.error("远程调用智能标注服务,没有返回标注结果");
return AjaxResult.error("智能标注失败");
}
// 获取 annotations 的值
Map<String, List<AnnotationResult.Annotation>> annotations = result.getAnnotations();
for (Map.Entry<String, List<AnnotationResult.Annotation>> entry : annotations.entrySet()) {
String imagePath = entry.getKey();
List<AnnotationResult.Annotation> annotationList = entry.getValue();
// 创建 ObjectMapper 实例
ObjectMapper objectMapper = new ObjectMapper();
System.out.println("Image Path: " + imagePath);
String jsonString = "";
// 遍历每个图片路径下的标注信息列表
if (annotationList != null) {
jsonString = objectMapper.writeValueAsString(annotationList);
log.info("智能标注结果:{} ", jsonString);
} else {
log.info("No annotations for image path, {}", imagePath);
}
Long fileId = findFileIdByFileUrl(fileList, imagePath);
if (!ObjectUtils.isEmpty(fileId)) {
//TODO 将文件的标注信息存数据库
AnnotationTaskAnnotatorEntity annotationTaskAnnotatorEntity = new AnnotationTaskAnnotatorEntity();
annotationTaskAnnotatorEntity.setFileId(fileId);
annotationTaskAnnotatorEntity.setTaskId(taskInfo.getTaskId());
annotationTaskAnnotatorEntity.setAnnotationResource("1");
annotationTaskAnnotatorEntity.setAnnotationResult(jsonString);
annotationTaskMapper.updateAnnotationInfo(annotationTaskAnnotatorEntity);
}
}
}catch (Exception e) {
log.error("智能标注异常,异常原因:{}", e.getMessage());
return AjaxResult.error("智能标注异常");
}
return null;
}
/**
* fileList 中根据 fileUrl 查找对应的 fileId
*
* @param fileList 要查找的文件列表
* @param targetFileUrl 目标文件的URL
* @return 如果找到则返回对应的 fileId否则返回 null
*/
public Long findFileIdByFileUrl(List<DataSetBasicFileEntity> fileList, String targetFileUrl) {
return fileList.stream()
.filter(fileEntity -> targetFileUrl.contains(fileEntity.getFileUrl()))
.map(DataSetBasicFileEntity::getFileId)
.findFirst()
.orElse(null);
}
}

View File

@ -0,0 +1,21 @@
package com.bonus.ai.service.dataset;
import com.bonus.ai.client.intelligentAnnotation.AnnotationRequest;
import com.bonus.ai.domain.DataSetBasicFileEntity;
import com.bonus.ai.domain.dataset.AnnotationTaskEntity;
import com.bonus.ai.domain.dataset.DataSetEntity;
import com.bonus.ai.domain.dataset.IntelligentAnnotationServiceEntity;
import com.bonus.common.core.web.domain.AjaxResult;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
public interface IntelligentAnnotationService {
List<IntelligentAnnotationServiceEntity> getAIAnnotationServiceList();
IntelligentAnnotationServiceEntity getAIAnnotationServiceById(Long serviceId);
AjaxResult aiAnnotate(AnnotationTaskEntity taskInfo,List<DataSetBasicFileEntity> filelist,IntelligentAnnotationServiceEntity aiAnnotationServiceInfo);
}

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bonus.ai.mapper.IntelligentAnnotationMapper">
<!-- 定义 ResultMap -->
<resultMap id="IntelligentAnnotationServiceResultMap" type="com.bonus.ai.domain.dataset.IntelligentAnnotationServiceEntity">
<id property="serviceId" column="intelligent_annotation_service_id"/>
<result property="serviceName" column="intelligent_annotation_service_name"/>
<result property="serviceUrl" column="intelligent_annotation_service_url"/>
<result property="annotationLabels" column="intelligent_annotation_service_labels"/>
<result property="delFlag" column="del_flag"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<!-- 插入 -->
<insert id="insertIntelligentAnnotationService" parameterType="com.bonus.ai.domain.dataset.IntelligentAnnotationServiceEntity" useGeneratedKeys="true" keyProperty="serviceId">
INSERT INTO ai_intelligent_annotation_service
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="serviceName != null and serviceName != ''">intelligent_annotation_service_name,</if>
<if test="serviceUrl != null and serviceUrl != ''">intelligent_annotation_service_url,</if>
<if test="annotationLabels != null and annotationLabels != ''">intelligent_annotation_service_labels,</if>
<if test="delFlag != null">del_flag,</if>
<if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if>
<if test="updateTime != null">update_time,</if>
</trim>
<trim prefix="VALUES (" suffix=")" suffixOverrides=",">
<if test="serviceName != null and serviceName != ''">#{serviceName},</if>
<if test="serviceUrl != null and serviceUrl != ''">#{serviceUrl},</if>
<if test="annotationLabels != null and annotationLabels != ''">#{annotationLabels},</if>
<if test="delFlag != null">#{delFlag},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
</trim>
</insert>
<!-- 更新 -->
<update id="updateIntelligentAnnotationService">
UPDATE ai_intelligent_annotation_service
SET intelligent_annotation_service_name = #{serviceName},
intelligent_annotation_service_url = #{serviceUrl},
intelligent_annotation_service_labels = #{annotationLabels},
del_flag = #{delFlag},
update_by = #{updateBy},
update_time = #{updateTime}
WHERE intelligent_annotation_service_id = #{serviceId}
</update>
<update id="deleteIntelligentAnnotationService">
UPDATE ai_intelligent_annotation_service
SET del_flag = '1'
WHERE intelligent_annotation_service_id = #{serviceId}
</update>
<!-- 按 ID 查询 -->
<select id="getIntelligentAnnotationServiceById" resultMap="IntelligentAnnotationServiceResultMap">
SELECT * FROM ai_intelligent_annotation_service
WHERE intelligent_annotation_service_id = #{serviceId}
</select>
<!-- 查询所有有效记录 -->
<select id="getIntelligentAnnotationService" resultMap="IntelligentAnnotationServiceResultMap">
SELECT * FROM ai_intelligent_annotation_service
WHERE del_flag = '0'
</select>
</mapper>

View File

@ -233,3 +233,41 @@ create table ai_version_file_map
)engine=innodb comment = '版本和文件关联表';
/*==============================================================*/
/* Table: ai_intelligent_annotation_service */
/*==============================================================*/
drop table if exists ai_intelligent_annotation_service;
create table ai_intelligent_annotation_service
(
intelligent_annotation_service_id bigint(20) not null auto_increment,
intelligent_annotation_service_name varchar(128) default '' comment '智能标注服务名称',
intelligent_annotation_service_url varchar(128) default '' comment '智能标注服务url',
intelligent_annotation_service_labels varchar(128) default '' comment '智能标注服务标签',
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(intelligent_annotation_service_id)
)engine=innodb comment = '智能标注服务;
INSERT INTO ai_intelligent_annotation_service (
intelligent_annotation_service_name,
intelligent_annotation_service_url,
intelligent_annotation_service_labels,
del_flag,
create_by,
update_by,
update_time,
create_time
) VALUES (
'丝束缺陷检测', -- 智能标注服务名称
'http://192.168.0.21:8081/annotate', -- 智能标注服务URL
'tiaojuan,zhujiesi,yulinwen', -- 智能标注服务标签
'0', -- 是否删除 (0代表存在)
'admin', -- 创建者
'admin', -- 更新者
NOW(), -- 更新时间
NOW() -- 创建时间
);