How to dynamically update the content of a task?
label-studio-frontend: 1.4.0。
I am using React , However, I don't know how to dynamically update annotations.result and task.data.image using axios. I look forward to your reply.
@hlomzik @makseq Please help me.
@hlomzik you mentioned that using assignTask + other functions should allow to solve the issue.
Anyway, there is no evidence / example of how to do It.
Could someone provide a practical example starting from this?
I make a simple sample to use React / label-studio-frontend: 1.4.0 to dynamically update annotations.result and task.data.image by fetch (similar as axios), you need your own backend server and database, but the frontend logic is same.
At the begining I show a typical bounding box annotation structure, you need to get your annotation from database and load it into "initLabelStudio" function below. In "onSubmitAnnotation", "onUpdateAnnotation", "onSkipTask" I show how to send the annotation to backend.
I use two arrow function "switchPrev" and "switchNext" to init / destroy labelstudio to achieve switching between tasks (images)
I am not good at React, but hope this work can give you some help.
/* a typical annotation for bounding box { "from_name":"tag", "id":"rXzQ2hWsi7", "image_rotation":0, "origin":"manual", "origin_height":449, "origin_width":1200, "to_name":"img", "type":"rectanglelabels", "value": { "rectanglelabels":["bank"], "rotation":0, "width":66.13333333333333, "height":57.651245551601434, "x":9.466666666666667, "y":21.352313167259787 } } */
export default function DatasetAnnotationPage() {
const navigate = useNavigate();
const {state} = useLocation();
const { dataset_id } = state;
const [activeIndex, setActiveIndex] = useState(0);
const [configXML, setConfigXML] = useState('');
const [annotations, setAnnotations, refAnnotations] = useState([]);
const [uploadIds, setUploadIds, refUploadIds] = useState([]);
const [taskImages, setTaskImages, refTaskImages] = useState([]);
const [labelList, setLabelList, refLabelList] = useState([]);
const [refreshFlag, setRefreshFlag] = useState(false);
const rootRef = useRef();
const lsf = useRef(null);
const initLabelStudio = useCallback((index,config_xml,annotation_list) => {
var userName= localStorage.getItem("userName");
return new LabelStudio(rootRef.current, {
config: config_xml,
interfaces: [
"panel",
"skip",
"update",
"submit",
"controls",
"side-column",
'topbar',
'annotations:tabs',
"annotations:menu",
"annotations:add-new",
"predictions:menu",
],
user: {
pk: 1,
firstName: userName,
lastName: ""
},
task: {
annotations: annotation_list,
predictions: [],
id: index+1,
data: {
image: refTaskImages.current[index]
}
},
onLabelStudioLoad: function(LS) {
var c = LS.annotationStore.addAnnotation({
userGenerate: true
});
LS.annotationStore.selectAnnotation(c.id);
},
onSubmitAnnotation: function (LS, annotation) {
// retrive an annotation and send it to backend (save in database)
var annotation_list = annotation.serializeAnnotation();
console.log(annotation_list);
if(annotation_list.length>0){
var annotation_form = new FormData();
annotation_form.append("upload_id",refUploadIds.current[index]);
var annotation_submit_list = [];
for(var i=0; i<annotation_list.length; i++){
var annotation = annotation_list[i];
console.log("annotation.id: ",annotation.id);
annotation_form.append("original_height",annotation.original_height);
annotation_form.append("original_width",annotation.original_width);
annotation_form.append("from_name",annotation.from_name);
annotation_form.append("to_name",annotation.to_name);
annotation_form.append("id",annotation.id);
annotation_form.append("type",annotation.type);
var annotation_type = annotation.type;
annotation_form.append("label",annotation.value[annotation.type][0]);
annotation_form.append("x",annotation.value.x);
annotation_form.append("y",annotation.value.y);
annotation_form.append("width",annotation.value.width);
annotation_form.append("height",annotation.value.height);
var value = {
"rotation": 0,
"width": annotation.value.width,
"height": annotation.value.height,
"x": annotation.value.x,
"y": annotation.value.y
}
value[annotation_type] = [annotation.value[annotation_type][0]]
var annotation_item = {
"from_name": annotation.from_name,
"id": annotation.id,
"source": "$image",
"to_name": annotation.to_name,
"type": annotation_type,
"value": value
}
annotation_submit_list.push(annotation_item);
}
var annotation_submit = {
"id": "1001",
"result": annotation_submit_list
}
var annotation_submit_url = SLP_UI_url+"/annotation/submit";
fetch(annotation_submit_url, {
method: 'POST',
body: annotation_form
})
.then(response => response.json())
.then(data => {
var result = data.result;
if(result == "success"){
var insert_rowcount = data.insert_rowcount;
var new_annotation = Object.assign({},refAnnotations.current);
new_annotation[refUploadIds.current[index]] = annotation_submit;
setAnnotations(new_annotation);
alert("Successfully submit "+insert_rowcount+" annotations.");
}
})
.catch(error => console.error(error));
}else{
alert("Warning: submitted an empty annotation.");
}
},
onUpdateAnnotation: function (LS, annotation) {
// retrive an annotation and send it to backend (save in database)
console.log(annotation.serializeAnnotation());
var annotation_list = annotation.serializeAnnotation();
if(annotation_list.length>0){
console.log(annotation_list.length);
console.log(annotations);
var annotation_form = new FormData();
annotation_form.append("upload_id",refUploadIds.current[index]);
annotation_form.append("annotation_number",annotation_list.length);
var annotation_submit_list = [];
for(var i=0; i<annotation_list.length; i++){
var annotation = annotation_list[i];
console.log("annotation.id: ",annotation.id);
annotation_form.append("original_height",annotation.original_height);
annotation_form.append("original_width",annotation.original_width);
annotation_form.append("from_name",annotation.from_name);
annotation_form.append("to_name",annotation.to_name);
annotation_form.append("id",annotation.id);
annotation_form.append("type",annotation.type);
var annotation_type = annotation.type;
annotation_form.append("label",annotation.value[annotation.type][0]);
annotation_form.append("x",annotation.value.x);
annotation_form.append("y",annotation.value.y);
annotation_form.append("width",annotation.value.width);
annotation_form.append("height",annotation.value.height);
var value = {
"rotation": 0,
"width": annotation.value.width,
"height": annotation.value.height,
"x": annotation.value.x,
"y": annotation.value.y
}
value[annotation_type] = [annotation.value[annotation_type][0]]
var annotation_item = {
"from_name": annotation.from_name,
"id": annotation.id,
"source": "$image",
"to_name": annotation.to_name,
"type": annotation_type,
"value": value
}
annotation_submit_list.push(annotation_item);
}
var annotation_submit = {
"id": "1001",
"result": annotation_submit_list
}
console.log("annotation_submit: ",annotation_submit);
var annotation_submit_url = SLP_UI_url+"/annotation/update";
fetch(annotation_submit_url, {
method: 'POST',
body: annotation_form
})
.then(response => response.json())
.then(data => {
var result = data.result;
if(result == "success"){
console.log(data);
var insert_rowcount = data.insert_rowcount;
var delete_rowcount = data.delete_rowcount;
var new_annotation = Object.assign({},refAnnotations.current);
new_annotation[refUploadIds.current[index]] = annotation_submit;
setAnnotations(new_annotation);
alert("Successfully delete "+delete_rowcount+" annotations and insert "+insert_rowcount+" annotations.");
}
})
.catch(error => console.error(error));
}else{
var annotation_form = new FormData();
annotation_form.append("upload_id",refUploadIds.current[index]);
annotation_form.append("annotation_number",annotation_list.length);
var annotation_submit_url = SLP_UI_url+"/annotation/update";
fetch(annotation_submit_url, {
method: 'POST',
body: annotation_form
})
.then(response => response.json())
.then(data => {
var result = data.result;
if(result == "success"){
console.log(data);
var insert_rowcount = data.insert_rowcount;
var delete_rowcount = data.delete_rowcount;
var new_annotation = Object.assign({},refAnnotations.current);
new_annotation[refUploadIds.current[index]] = {};
setAnnotations(new_annotation);
alert("Successfully delete "+delete_rowcount+" annotations and insert "+insert_rowcount+" annotations.");
}
})
.catch(error => console.error(error));
}
// setSelected(!selected);
},
onSkipTask: function(){
if(refUploadIds.current.length == 1){
alert("This is the last image in dataset, please import some more before you can skip this image.");
}else{
console.log("start skip task");
var upload_form = new FormData();
upload_form.append("upload_id",refUploadIds.current[index]);
var task_skip_url = SLP_UI_url+"/task/skip";
fetch(task_skip_url, {
method: 'POST',
body: upload_form
})
.then(response => response.json())
.then(data => {
var result = data.result;
if(result == "success"){
console.log(data);
// var insert_rowcount = data.insert_rowcount;
var skip_upload_id = data.upload_id;
alert("Successfully skip task "+skip_upload_id);
setRefreshFlag(!refreshFlag);
}
})
.catch(error => console.error(error));
}
}
});
})
const switchPrev = () => {
lsf.current.destroy();
var upload_id = refUploadIds.current[activeIndex-1];
var annotation = refAnnotations.current[upload_id];
if(Object.keys(annotation).length == 0){
// no annotation
lsf.current = initLabelStudio(activeIndex-1,configXML,[]);
}else{
lsf.current = initLabelStudio(activeIndex-1,configXML,[annotation]);
}
setActiveIndex(activeIndex-1);
}
const switchNext = () => {
lsf.current.destroy();
var upload_id = refUploadIds.current[activeIndex+1];
var annotation = refAnnotations.current[upload_id];
if(Object.keys(annotation).length == 0){
// no annotation
lsf.current = initLabelStudio(activeIndex+1,configXML,[]);
}else{
lsf.current = initLabelStudio(activeIndex+1,configXML,[annotation]);
}
setActiveIndex(activeIndex+1);
}
useEffect(() => {
var load_dataset_form = new FormData();
load_dataset_form.append("datasetId",dataset_id);
var dataset_query_url = SLP_UI_url+"/dataset/annotations/query";
fetch(dataset_query_url, {
method: 'POST',
body: load_dataset_form
})
.then(response => response.json())
.then(data => {
var result = data.result;
if(result == "success"){
var dataset_annotations_result = data.dataset_annotations_result;
var upload_ids = dataset_annotations_result.upload_ids;
var taskImages = dataset_annotations_result.taskImages.map((item,index)=>{
return item.replace(annotation_upload_path, apache_url);
});
var annotations = dataset_annotations_result.annotations;
var labels_list = dataset_annotations_result.labels_str.split(",");
setUploadIds(upload_ids);
setTaskImages(taskImages);
setAnnotations(annotations);
setLabelList(labels_list);
var config_xml = '<View>';
config_xml += '<Image name="img" value="$image"></Image>';
config_xml += '<RectangleLabels name="tag" toName="img">';
for(var i=0; i<labels_list.length; i++){
config_xml += '<Label value="'+labels_list[i]+'"></Label>>';
}
config_xml += '</RectangleLabels>';
config_xml += '</View>';
setConfigXML(config_xml);
var upload_init_id = upload_ids[0];
var annotation = annotations[upload_init_id];
if(Object.keys(annotation).length == 0){
// no annotation
if(lsf.current!=null){
lsf.current.destroy();
}
lsf.current = initLabelStudio(0,config_xml,[]);
setActiveIndex(0);
}else{
if(lsf.current!=null){
lsf.current.destroy();
}
lsf.current = initLabelStudio(0,config_xml,[annotation]);
setActiveIndex(0);
}
}else{
var error_msg = data.error;
alert(error_msg);
}
})
.catch(error => console.error(error));
},[refreshFlag]);
return (
<div className={`${styles.page} flex-col`}>
<CommonHeader subtitle="模型数据/标注数据"/>
<div className={`${styles['main_content']} flex-row`}>
<CommonMenu activePage={4}/>
<div className={`${styles['dataAnnotation_content']} flex-col`}>
<div className={`${styles['label_studio_switch']}`}>
{
activeIndex!=0?
<div className={`${styles['label_studio_switch_prev']}`} onClick={switchPrev}><TbPlayerTrackPrevFilled size={30} color='white' className={`${styles['label_studio_switch_icon']}`}/></div>
:
<div className={`${styles['label_studio_switch_prev_disable']}`}><TbPlayerTrackPrevFilled size={30} color='white' className={`${styles['label_studio_switch_icon']}`}/></div>
}
{
activeIndex!=(refUploadIds.current.length-1)?
<div className={`${styles['label_studio_switch_next']}`} onClick={switchNext}><TbPlayerTrackNextFilled size={30} color='white' className={`${styles['label_studio_switch_icon']}`}/></div>
:
<div className={`${styles['label_studio_switch_next_disable']}`}><TbPlayerTrackNextFilled size={30} color='white' className={`${styles['label_studio_switch_icon']}`}/></div>
}
</div>
<div className={`${styles['label_studio_import_btn']}`}>Import</div>
<div className={`${styles['label_studio_export_btn']}`} onClick={exportDataset}>Export</div>
<div className={`${styles['label_studio_container']}`} id="label-studio" ref={rootRef}></div>
</div>
</div>
</div>
);
}