在线标注底层api接口

This commit is contained in:
weiweiw 2024-11-21 17:36:13 +08:00
parent a25325c266
commit d8a873995b
8 changed files with 709 additions and 23 deletions

View File

@ -41,8 +41,6 @@
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Mysql Connector -->
<dependency>
<groupId>com.mysql</groupId>

View File

@ -0,0 +1,5 @@
package com.bonus.ai.client;
public class AIConstants {
public static final String AUTHORIZATION_HEADER = "Authorization";
}

View File

@ -0,0 +1,193 @@
package com.bonus.ai.client;
//public class AnnotationParam {
//}
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@Data
public class AnnotationParam {
@JsonProperty("id")
private int id;
@JsonProperty("result")
private List<Result> result;
@JsonProperty("created_username")
private String createdUsername;
@JsonProperty("created_ago")
private String createdAgo;
@JsonProperty("completed_by")
private int completedBy;
@JsonProperty("was_cancelled")
private boolean wasCancelled;
@JsonProperty("ground_truth")
private boolean groundTruth;
@JsonProperty("created_at")
private String createdAt;
@JsonProperty("updated_at")
private String updatedAt;
@JsonProperty("draft_created_at")
private String draftCreatedAt;
@JsonProperty("lead_time")
private String leadTime;
@JsonProperty("import_id")
private String importId;
@JsonProperty("last_action")
private String lastAction;
@JsonProperty("task")
private int task;
@JsonProperty("project")
private int project;
@JsonProperty("updated_by")
private int updatedBy;
@JsonProperty("parent_prediction")
private String parentPrediction;
@JsonProperty("parent_annotation")
private String parentAnnotation;
@JsonProperty("last_created_by")
private String lastCreatedBy;
// Nested classes for Result and Value
@Data
public static class Result {
@JsonProperty("original_width")
private int originalWidth;
@JsonProperty("original_height")
private int originalHeight;
@JsonProperty("image_rotation")
private int imageRotation;
@JsonProperty("from_name")
private String fromName;
@JsonProperty("to_name")
private String toName;
@JsonProperty("type")
private String type;
@JsonProperty("value")
private Value value;
@Data
public static class Value {
@JsonProperty("x")
private double x;
@JsonProperty("y")
private double y;
@JsonProperty("width")
private double width;
@JsonProperty("height")
private double height;
@JsonProperty("rotation")
private int rotation;
@JsonProperty("values")
private Map<String, List<String>> values;
// Getters and setters
}
// Getters and setters
}
// JSON to class conversion
public static AnnotationParam fromJson(String json) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(json, AnnotationParam.class);
}
// Class to JSON conversion
public String toJson() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(this);
}
// Getters and setters for outer class
// Use IDE to generate or add manually
public static void main(String[] args) throws IOException {
String json = "{\n" +
" \"id\": 6,\n" +
" \"result\": [\n" +
" {\n" +
" \"original_width\": 1920,\n" +
" \"original_height\": 1080,\n" +
" \"image_rotation\": 0,\n" +
" \"from_name\": \"bboxes\",\n" +
" \"to_name\": \"image\",\n" +
" \"type\": \"rectanglelabels\",\n" +
" \"value\": {\n" +
" \"x\": 20,\n" +
" \"y\": 30,\n" +
" \"width\": 50,\n" +
" \"height\": 60,\n" +
" \"rotation\": 0,\n" +
" \"values\": {\n" +
" \"rectanglelabels\": [\n" +
" \"Person\"\n" +
" ]\n" +
" }\n" +
" }\n" +
" }\n" +
" ],\n" +
" \"created_username\": \" wang272160587@gmail.com, 1\",\n" +
" \"created_ago\": \"0 minutes\",\n" +
" \"completed_by\": 1,\n" +
" \"was_cancelled\": false,\n" +
" \"ground_truth\": true,\n" +
" \"created_at\": \"2024-11-21T07:52:04.473353Z\",\n" +
" \"updated_at\": \"2024-11-21T07:52:04.473378Z\",\n" +
" \"draft_created_at\": null,\n" +
" \"lead_time\": null,\n" +
" \"import_id\": null,\n" +
" \"last_action\": null,\n" +
" \"task\": 6,\n" +
" \"project\": 4,\n" +
" \"updated_by\": 1,\n" +
" \"parent_prediction\": null,\n" +
" \"parent_annotation\": null,\n" +
" \"last_created_by\": null\n" +
"}";
// JSON to class
AnnotationParam annotation = AnnotationParam.fromJson(json);
System.out.println("Parsed Annotation Object: " + annotation);
System.out.println("result[0].toname:" + annotation.getResult().get(0).getToName());
// Class to JSON
String jsonString = annotation.toJson();
System.out.println("Serialized JSON: " + jsonString);
}
}

View File

@ -0,0 +1,83 @@
package com.bonus.ai.client;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
@Component
public class OnlineAnnotationFallbackFactory implements FallbackFactory<OnlineAnnotationService> {
private static final Logger log = LoggerFactory.getLogger(OnlineAnnotationFallbackFactory.class);
@Override
public OnlineAnnotationService create(Throwable throwable) {
log.error("系统服务调用失败:{}", throwable.getMessage());
return new OnlineAnnotationService() {
@Override
public String createProject(ProjectParam projectParam, String authorization) {
return null;
}
@Override
public String getProjectById(String id, String authorization) {
return "";
}
@Override
public boolean deleteProjectById(String id, String authorization) {
return false;
}
@Override
public String updateProject(ProjectParam projectParam, String authorization) {
return "";
}
@Override
public String createTask(TaskParam task, String authorization) {
return null;
}
@Override
public String getTaskById(String id, String authorization) {
return "";
}
@Override
public boolean deleteTaskById(String id, String authorization) {
return false;
}
@Override
public String updateTask(TaskParam task, String authorization) {
return "";
}
@Override
public String createAnnotation(AnnotationParam annotationParam, String authorization) {
return "";
}
@Override
public String getAnnotationById(String id, String authorization) {
return "";
}
@Override
public boolean deleteAnnotationById(String id, String authorization) {
return false;
}
@Override
public String updateAnnotation(AnnotationParam annotationParam, String authorization) {
return "";
}
};
}
}

View File

@ -0,0 +1,72 @@
package com.bonus.ai.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
@FeignClient(name = "onlineAnnotationService", url = "${label-studio.url}")
public interface OnlineAnnotationService {
/**
* project <------> 人工智能数据中心的标注任务
* project 增删改查
*/
@PostMapping("/api/projects/")
public String createProject(@RequestBody ProjectParam projectParam, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization);
@GetMapping("/api/projects/{id}/")
public String getProjectById(@PathVariable("id") String id, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization);
@DeleteMapping("/api/projects/{id}/")
public boolean deleteProjectById(@PathVariable("id") String id, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization);
@PatchMapping("/api/projects/")
public String updateProject(@RequestBody ProjectParam projectParam, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization);
/**
* task <------> 人工智能数据中心的具体的标注文件
* task 增删改查
*/
@PostMapping("/api/tasks/")
public String createTask(@RequestBody TaskParam task, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization);
@GetMapping("/api/tasks/{id}/")
public String getTaskById(@PathVariable("id") String id, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization);
@DeleteMapping("/api/tasks/{id}/")
public boolean deleteTaskById(@PathVariable("id") String id, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization);
@PatchMapping("/api/tasks/")
public String updateTask(@RequestBody TaskParam task, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization);
/**
* annotation <------> 人工智能数据中心的标注结果
* annotation 增删改查
*/
@PostMapping("/api/tasks/{id}/annotations/")
public String createAnnotation(@RequestBody AnnotationParam annotationParam, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization);
@GetMapping("/api/annotations/{id}")
public String getAnnotationById(@PathVariable("id") String id, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization);
@DeleteMapping("/api/annotations/{id}")
public boolean deleteAnnotationById(@PathVariable("id") String id, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization);
@PatchMapping("/api/tasks/{id}/annotations/")
public String updateAnnotation(@RequestBody AnnotationParam annotationParam, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization);
/**
* 导出指定project的所有标注结果
*/
// @GetMapping("/api/project/{id}/export")
// public String exportAnnotationByProjectId(@PathVariable("id") String id, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization);
//
// @GetMapping("/api/annotations/{id}")
// public String exportAnnotationByTaskIds(@PathVariable("id") String id, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization);
}

View File

@ -0,0 +1,207 @@
package com.bonus.ai.client;
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;
@Data
public class ProjectParam {
private int id;
private String title;
private String description;
@JsonProperty("label_config")
private String labelConfig;
@JsonProperty("expert_instruction")
private String expertInstruction;
@JsonProperty("show_instruction")
private boolean showInstruction;
@JsonProperty("show_skip_button")
private boolean showSkipButton;
@JsonProperty("enable_empty_annotation")
private boolean enableEmptyAnnotation;
@JsonProperty("show_annotation_history")
private boolean showAnnotationHistory;
private int organization;
private String color;
@JsonProperty("maximum_annotations")
private int maximumAnnotations;
@JsonProperty("is_published")
private boolean isPublished;
@JsonProperty("model_version")
private String modelVersion;
@JsonProperty("is_draft")
private boolean isDraft;
@JsonProperty("created_by")
private CreatedBy createdBy;
@JsonProperty("created_at")
private String createdAt;
@JsonProperty("min_annotations_to_start_training")
private int minAnnotationsToStartTraining;
@JsonProperty("start_training_on_annotation_update")
private boolean startTrainingOnAnnotationUpdate;
@JsonProperty("show_collab_predictions")
private boolean showCollabPredictions;
@JsonProperty("num_tasks_with_annotations")
private int numTasksWithAnnotations;
@JsonProperty("task_number")
private int taskNumber;
@JsonProperty("useful_annotation_number")
private int usefulAnnotationNumber;
@JsonProperty("ground_truth_number")
private int groundTruthNumber;
@JsonProperty("skipped_annotations_number")
private int skippedAnnotationsNumber;
@JsonProperty("total_annotations_number")
private int totalAnnotationsNumber;
@JsonProperty("total_predictions_number")
private int totalPredictionsNumber;
private String sampling;
@JsonProperty("show_ground_truth_first")
private boolean showGroundTruthFirst;
@JsonProperty("show_overlap_first")
private boolean showOverlapFirst;
@JsonProperty("overlap_cohort_percentage")
private int overlapCohortPercentage;
@JsonProperty("task_data_login")
private String taskDataLogin;
@JsonProperty("task_data_password")
private String taskDataPassword;
@JsonProperty("control_weights")
private Map<String, ControlWeight> controlWeights;
@JsonProperty("parsed_label_config")
private ParsedLabelConfig parsedLabelConfig;
@JsonProperty("evaluate_predictions_automatically")
private boolean evaluatePredictionsAutomatically;
@JsonProperty("config_has_control_tags")
private boolean configHasControlTags;
@JsonProperty("skip_queue")
private String skipQueue;
@JsonProperty("reveal_preannotations_interactively")
private boolean revealPreannotationsInteractively;
@JsonProperty("pinned_at")
private String pinnedAt;
@JsonProperty("finished_task_number")
private int finishedTaskNumber;
@JsonProperty("queue_total")
private int queueTotal;
@JsonProperty("queue_done")
private int queueDone;
// Getters and Setters for all fields
@Data
public static class CreatedBy {
private int id;
@JsonProperty("first_name")
private String firstName;
@JsonProperty("last_name")
private String lastName;
private String email;
private String avatar;
// Getters and Setters
}
@Data
public static class ControlWeight {
private double overall;
private String type;
private Map<String, Double> labels;
// Getters and Setters
}
@Data
public static class ParsedLabelConfig {
private Map<String, LabelConfig> label;
// Getters and Setters
}
@Data
public static class LabelConfig {
private String type;
@JsonProperty("to_name")
private List<String> toName;
private List<Input> inputs;
private List<String> labels;
@JsonProperty("labels_attrs")
private Map<String, LabelAttributes> labelsAttrs;
// Getters and Setters
}
@Data
public static class Input {
private String type;
private String value;
private String valueType;
// Getters and Setters
}
@Data
public static class LabelAttributes {
private String value;
private String background;
// Getters and Setters
}
public static ProjectParam fromJson(String json) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(json, ProjectParam.class);
}
public String toJson() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(this);
}
public static void main(String[] args) throws JsonProcessingException {
String json = "{\n" +
" \"data\": {\n" +
" \"image\": \"https://example.com/image.jpg\",\n" +
" \"text\": \"Hello, AI!\"\n" +
" },\n" +
" \"id\": 1,\n" +
" \"meta\": {\n" +
" \"key\": \"value\"\n" +
" },\n" +
" \"created_at\": \"2024-06-18T23:45:46Z\",\n" +
" \"updated_at\": \"2024-06-18T23:45:46Z\",\n" +
" \"is_labeled\": false,\n" +
" \"overlap\": 1,\n" +
" \"inner_id\": 1,\n" +
" \"total_annotations\": 0,\n" +
" \"cancelled_annotations\": 0,\n" +
" \"total_predictions\": 0,\n" +
" \"comment_count\": 0,\n" +
" \"unresolved_comment_count\": 0,\n" +
" \"last_comment_updated_at\": \"2024-01-15T09:30:00Z\",\n" +
" \"project\": 1,\n" +
" \"updated_by\": [\n" +
" {\n" +
" \"user_id\": 1\n" +
" }\n" +
" ],\n" +
" \"file_upload\": \"42d46c4c-my-pic.jpeg\",\n" +
" \"comment_authors\": [\n" +
" 1\n" +
" ]\n" +
"}";
System.out.println(json);
TaskParam record = TaskParam.fromJson(json);
System.out.println("Create At: " + record.getData());
System.out.println("Create At: " + record.getCreatedAt());
System.out.println("Image URL: " + record.getData().getImage());
}
}

View File

@ -0,0 +1,119 @@
package com.bonus.ai.client;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.Data;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Map;
//@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class TaskParam {
private Data data;
private int id;
private Map<String, String> meta;
@JsonProperty("created_at")
private String createdAt;
// private ZonedDateTime createdAt;
@JsonProperty("updated_at")
private String updatedAt;
// private ZonedDateTime updatedAt;
@JsonProperty("is_labeled")
private boolean isLabeled;
private int overlap;
@JsonProperty("inner_id")
private int innerId;
@JsonProperty("total_annotations")
private int totalAnnotations;
@JsonProperty("cancelled_annotations")
private int cancelledAnnotations;
@JsonProperty("total_predictions")
private int totalPredictions;
@JsonProperty("comment_count")
private int commentCount;
@JsonProperty("unresolved_comment_count")
private int unresolvedCommentCount;
@JsonProperty("last_comment_updated_at")
private String lastCommentUpdatedAt;
private int project;
@JsonProperty("updated_by")
private List<UpdatedBy> updatedBy;
@JsonProperty("file_upload")
private String fileUpload;
@JsonProperty("comment_authors")
private List<Integer> commentAuthors;
// Getters and Setters for all fields
@lombok.Data
public static class Data {
private String image;
private String text;
}
public static class UpdatedBy {
@JsonProperty("user_id")
private int userId;
// Getters and Setters
}
public static TaskParam fromJson(String json) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
// 注册 Java 时间模块
mapper.registerModule(new JavaTimeModule());
return mapper.readValue(json, TaskParam.class);
}
public String toJson() throws Exception {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(this);
}
public static void main(String[] args) throws JsonProcessingException {
String json = "{\n" +
" \"data\": {\n" +
" \"image\": \"https://example.com/image.jpg\",\n" +
" \"text\": \"Hello, AI!\"\n" +
" },\n" +
" \"id\": 1,\n" +
" \"meta\": {\n" +
" \"key\": \"value\"\n" +
" },\n" +
" \"created_at\": \"2024-06-18T23:45:46Z\",\n" +
" \"updated_at\": \"2024-06-18T23:45:46Z\",\n" +
" \"is_labeled\": false,\n" +
" \"overlap\": 1,\n" +
" \"inner_id\": 1,\n" +
" \"total_annotations\": 0,\n" +
" \"cancelled_annotations\": 0,\n" +
" \"total_predictions\": 0,\n" +
" \"comment_count\": 0,\n" +
" \"unresolved_comment_count\": 0,\n" +
" \"last_comment_updated_at\": \"2024-01-15T09:30:00Z\",\n" +
" \"project\": 1,\n" +
" \"updated_by\": [\n" +
" {\n" +
" \"user_id\": 1\n" +
" }\n" +
" ],\n" +
" \"file_upload\": \"42d46c4c-my-pic.jpeg\",\n" +
" \"comment_authors\": [\n" +
" 1\n" +
" ]\n" +
"}";
System.out.println(json);
TaskParam record = TaskParam.fromJson(json);
System.out.println("Record ID: " + record.getId());
System.out.println("Image URL: " + record.getData().getImage());
}
}

View File

@ -1,5 +1,6 @@
package com.bonus.ai.service.Impl.dataset;
import com.bonus.ai.client.OnlineAnnotationService;
import com.bonus.ai.config.MinioConfig;
import com.bonus.ai.config.OnlineAnnotateConfig;
import com.bonus.ai.domain.dataset.AnnotationSubTaskEntity;
@ -8,7 +9,6 @@ import com.bonus.ai.domain.dataset.DataSetEntity;
import com.bonus.ai.service.dataset.AnnotationTaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@ -16,36 +16,35 @@ import javax.annotation.Resource;
@Service
public class AnnotationTaskServiceImpl implements AnnotationTaskService {
@Resource
private RestTemplate restTemplate;
OnlineAnnotationService onlineAnnotationService;
@Resource
private OnlineAnnotateConfig onlineAnnotateConfig;
/**
* 手工标注
*/
@Override
public void manualAnnotate(AnnotationSubTaskEntity subTask) {
}
/**
* AI标注
* 创建标注任务
*/
@Override
public void aiAnnotate(AnnotationSubTaskEntity subTask) {
public int createTask(AnnotationTaskEntity task) {
return 0;
}
/**
* 获取任务列表
* 获取任务列表分配给我的标注任务我创建的标注任务
*/
@Override
public DataSetEntity getTaskList(String type) {
return null;
}
/**
* 获取任务列表分配给我的标注任务我创建的标注任务
*/
/**
* 更新任务
*/
@ -54,13 +53,6 @@ public class AnnotationTaskServiceImpl implements AnnotationTaskService {
return 0;
}
/**
* 创建标注任务
*/
@Override
public int createTask(AnnotationTaskEntity task) {
return 0;
}
/**
* 根据taskid 删除任务
@ -93,4 +85,21 @@ public class AnnotationTaskServiceImpl implements AnnotationTaskService {
return false;
}
/**
* 手工标注
*/
@Override
public void manualAnnotate(AnnotationSubTaskEntity subTask) {
}
/**
* AI标注
*/
@Override
public void aiAnnotate(AnnotationSubTaskEntity subTask) {
}
}