From af698f336d0d776feff177d19742faa023ee5e1a Mon Sep 17 00:00:00 2001 From: zhoulexin Date: Fri, 24 Oct 2025 14:30:20 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9A3d=E7=82=B9=E4=BA=91=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 39 +++-- public/css/main.css | 4 + public/js/annotation.js | 32 +++-- public/js/box_editor.js | 50 +++---- public/js/data.js | 31 ++-- public/js/editor.js | 10 +- public/js/fileHttp.js | 3 + public/js/header.js | 74 ++++++++-- public/js/http.js | 9 ++ public/js/image.js | 1 + public/js/lidar.js | 305 +++++++++++++++++++++------------------- public/js/mouse.js | 6 + public/js/request.js | 2 +- public/js/save.js | 41 ++++-- public/js/world.js | 149 +++++++++++++------- 15 files changed, 477 insertions(+), 279 deletions(-) create mode 100644 public/js/fileHttp.js create mode 100644 public/js/http.js diff --git a/index.html b/index.html index 5f15070..62feefd 100644 --- a/index.html +++ b/index.html @@ -1,7 +1,7 @@ - SUSTech POINTS + 标注实训平台 @@ -57,8 +57,9 @@
-
-
+ + - + + + +
+ 返回 +
@@ -1260,17 +1272,17 @@ - + - - + + - - - - - - + + + + + + @@ -1336,7 +1348,6 @@ document.head.appendChild(script); }); } - console.log(window.location.href) try { loadDynamicResources(); } catch (error) { diff --git a/public/css/main.css b/public/css/main.css index f8e4165..9c78a10 100644 --- a/public/css/main.css +++ b/public/css/main.css @@ -123,6 +123,7 @@ canvas { color: inherit; border-width: 0; display: inline-flex; + align-items: center; } #object-category-selector, @@ -510,6 +511,7 @@ dynamic set obj-type css has higher priority than this one. display: inline; padding-left: 5px; padding-right: 5px; + color: #fff; } .menu-button:hover { @@ -616,6 +618,7 @@ dynamic set obj-type css has higher priority than this one. } .ui-button { + cursor: pointer; background-color: var(--widget-background-color); padding-left: 0; padding-right: 0; @@ -625,6 +628,7 @@ dynamic set obj-type css has higher priority than this one. height: 20px; width: 20px; padding: 2px; + margin: 0 5px; } .ui-button:hover { diff --git a/public/js/annotation.js b/public/js/annotation.js index ac67149..1d1b836 100644 --- a/public/js/annotation.js +++ b/public/js/annotation.js @@ -2,7 +2,7 @@ import * as THREE from "./lib/three.module.js"; import { globalObjectCategory } from "./obj_cfg.js"; import { saveWorldList } from "./save.js"; import { intersect } from "./util.js"; -import request from "./request.js"; +import http from "./http.js"; function Annotation(sceneMeta, world, frameInfo) { this.world = world; @@ -130,9 +130,7 @@ function Annotation(sceneMeta, world, frameInfo) { if (b.follows) ann.follows = b.follows; return ann; }); - anns.sort((a, b) => a.obj_id - b.obj_id); - return anns; }; @@ -496,6 +494,7 @@ function Annotation(sceneMeta, world, frameInfo) { // 标注信息 this.load_annotation = function (on_load) { + const microData = window.microApp.getData() if (this.data.cfg.disableLabels) { on_load([]); } else { @@ -506,22 +505,35 @@ function Annotation(sceneMeta, world, frameInfo) { if (this.readyState != 4) return; if (this.status == 200) { - let ann = _self.frameInfo.anno_to_boxes(this.responseText); + // 未标注状态 + if(!microData.labelState || microData.labelState === '0'){ + document.getElementById("changed-mark").style.display = "block"; + document.getElementById("save-submit").style.display = "block"; + } + // 质检状态 + if(microData.taskTag === '0'){ + document.getElementById("save-pass").style.display = "block"; + document.getElementById("save-reject").style.display = "block"; + } + let ann = _self.frameInfo.anno_to_boxes(JSON.parse(this.responseText).data.label); + if (_self.world && _self.world.cameras) { + _self.world.cameras.setAnnotationData(JSON.parse(this.responseText).data); + } + if (_self.frameInfo) { + _self.frameInfo.setAnnotationData(JSON.parse(this.responseText).data); + } on_load(ann); } // end of state change: it can be after some time (async) }; - + let frameId = this.frameInfo.frame?.frameId ?? this.frameInfo.frame xhr.open( "GET", - request + "/load_annotation" + - "?scene=" + - this.frameInfo.scene + - "&frame=" + - this.frameInfo.frame, + `${http.requestHttp}/project/getFrameInfo?frameId=${frameId}`, true ); + xhr.setRequestHeader('authorization', http.token); xhr.send(); } }; diff --git a/public/js/box_editor.js b/public/js/box_editor.js index 774272e..9410e34 100644 --- a/public/js/box_editor.js +++ b/public/js/box_editor.js @@ -513,7 +513,7 @@ function BoxEditorManager( // next/prev call will not update onExit this.onExit = onExit; } - let sceneName = sceneMeta.scene; + let sceneName = sceneMeta.sceneId; this.editingTarget.data = data; this.editingTarget.sceneMeta = sceneMeta; @@ -1349,28 +1349,28 @@ function BoxEditorManager( this.showTrajectory(); }; - this.toolbox.querySelector("#reload").onclick = (e) => { - let selectedEditors = this.activeEditorList(); - this.reloadAnnotation(selectedEditors); - }; + // this.toolbox.querySelector("#reload").onclick = (e) => { + // let selectedEditors = this.activeEditorList(); + // this.reloadAnnotation(selectedEditors); + // }; - this.toolbox.querySelector("#interpolate").onclick = async () => { - //this.boxOp.interpolate_selected_object(this.editingTarget.scene, this.editingTarget.objTrackId, ""); + // this.toolbox.querySelector("#interpolate").onclick = async () => { + // //this.boxOp.interpolate_selected_object(this.editingTarget.scene, this.editingTarget.objTrackId, ""); - let applyIndList = this.activeEditorList().map((e) => true); //all shoud be applied. - this.interpolate(applyIndList); - }; + // let applyIndList = this.activeEditorList().map((e) => true); //all shoud be applied. + // this.interpolate(applyIndList); + // }; - this.toolbox.querySelector("#auto-annotate").onclick = async () => { - let applyIndList = this.activeEditorList().map((e) => true); //all shoud be applied. - this.autoAnnotate(applyIndList); - }; + // this.toolbox.querySelector("#auto-annotate").onclick = async () => { + // let applyIndList = this.activeEditorList().map((e) => true); //all shoud be applied. + // this.autoAnnotate(applyIndList); + // }; - this.toolbox.querySelector("#auto-annotate-translate-only").onclick = - async () => { - let applyIndList = this.activeEditorList().map((e) => true); //all shoud be applied. - this.autoAnnotate(applyIndList, "dontrotate"); - }; + // this.toolbox.querySelector("#auto-annotate-translate-only").onclick = + // async () => { + // let applyIndList = this.activeEditorList().map((e) => true); //all shoud be applied. + // this.autoAnnotate(applyIndList, "dontrotate"); + // }; this.toolbox.querySelector("#exit").onclick = () => { this.hide(); @@ -1380,13 +1380,13 @@ function BoxEditorManager( if (this.onExit) this.onExit(); }; - this.toolbox.querySelector("#next").onclick = () => { - this.nextBatch(); - }; + // this.toolbox.querySelector("#next").onclick = () => { + // this.nextBatch(); + // }; - this.toolbox.querySelector("#prev").onclick = () => { - this.prevBatch(); - }; + // this.toolbox.querySelector("#prev").onclick = () => { + // this.prevBatch(); + // }; this.nextBatch = function () { let maxFrameIndex = this.editingTarget.sceneMeta.frames.length - 1; diff --git a/public/js/data.js b/public/js/data.js index 4104007..cb6adca 100644 --- a/public/js/data.js +++ b/public/js/data.js @@ -2,6 +2,7 @@ import { World } from "./world.js"; import { Debug } from "./debug.js"; import { logger } from "./log.js"; import request from "./request.js"; +import http from "./http.js"; // 请求数据集合 class Data { @@ -10,10 +11,13 @@ class Data { } async readSceneList() { - const req = new Request(request + "/get_all_scene_desc"); + // const req = new Request(request + "/get_all_scene_desc"); + const dataId = window.microApp.getData().dataId + const req = new Request(`${http.requestHttp}/project/getScene?dataId=${dataId}`); let init = { method: "GET", //body: JSON.stringify({"points": data}) + headers: { "authorization": http.token }, }; // we defined the xhr @@ -25,10 +29,18 @@ class Data { return response.json(); } }) - .then((ret) => { - console.log(ret); - this.sceneDescList = ret; - return ret; + .then((res) => { + let obj = {} + if(res.code === 401){ + window.location.href = '/login' + } + if(res.code === 200){ + res.data.forEach(item=>{ + obj[item.sceneName] = item.sceneId + }) + } + this.sceneDescList = obj + return res; }) .catch((reject) => { console.log("error read scene list!"); @@ -50,7 +62,6 @@ class Data { async getWorld(sceneName, frame, on_preload_finished) { // find in list - if (!this.meta[sceneName]) { await this.readSceneMetaData(sceneName); } @@ -424,12 +435,16 @@ class Data { if (this.status == 200) { let sceneMeta = JSON.parse(this.responseText); - self.meta[sceneName] = sceneMeta; + sceneMeta.data.calib = JSON.parse(sceneMeta.data.calib); + self.meta[sceneName] = sceneMeta.data; + console.log(self.meta[sceneName] ) resolve(sceneMeta); } }; - xhr.open("GET", `${request}/scenemeta?scene=${sceneName}`, true); + // xhr.open("GET", `${request}/scenemeta?scene=${sceneName}`, true); + xhr.open("GET", `${http.requestHttp}/project/getSceneInfo?sceneId=${sceneName}`, true); + xhr.setRequestHeader('authorization', http.token); xhr.send(); }); } diff --git a/public/js/editor.js b/public/js/editor.js index e10fa74..fcdb4d1 100644 --- a/public/js/editor.js +++ b/public/js/editor.js @@ -26,6 +26,7 @@ import { globalKeyDownManager } from "./keydown_manager.js"; import { vector_range } from "./util.js"; import { checkScene } from "./error_check.js"; +const microData = window.microApp.getData() function Editor(editorUi, wrapperUi, editorCfg, data, name = "editor") { // create logger before anything else. create_logger( @@ -978,12 +979,12 @@ function Editor(editorUi, wrapperUi, editorCfg, data, name = "editor") { if (!meta) { this.editorUi.querySelector("#frame-selector").innerHTML = ""; - meta = await this.data.readSceneMetaData(sceneName); + let res = await this.data.readSceneMetaData(sceneName); + meta = res.data } - var frame_selector_str = meta.frames .map(function (f) { - return ""; + return ""; }) .reduce(function (x, y) { return x + y; @@ -1328,7 +1329,6 @@ function Editor(editorUi, wrapperUi, editorCfg, data, name = "editor") { this.handleRightClick = function (event) { // select new object - if (!this.data.world) { return; } @@ -2396,7 +2396,7 @@ function Editor(editorUi, wrapperUi, editorCfg, data, name = "editor") { // }; this.on_load_world_finished = function (world) { - document.title = "SUSTech POINTS-" + world.frameInfo.scene; + // document.title = "SUSTech POINTS-" + world.frameInfo.scene; // switch view positoin this.moveAxisHelper(world); this.moveRangeCircle(world); diff --git a/public/js/fileHttp.js b/public/js/fileHttp.js new file mode 100644 index 0000000..0fa18f2 --- /dev/null +++ b/public/js/fileHttp.js @@ -0,0 +1,3 @@ +const fileHttp = window.location.hostname === 'localhost' || + window.location.hostname === '192.168.5.177' ? 'http://192.168.5.10:9100/' : '/img/' +export default fileHttp \ No newline at end of file diff --git a/public/js/header.js b/public/js/header.js index e1ccd52..7d28634 100644 --- a/public/js/header.js +++ b/public/js/header.js @@ -1,5 +1,6 @@ import { CubeRefractionMapping } from "./lib/three.module.js"; import { saveWorldList } from "./save.js"; +import http from "./http.js"; var Header = function ( ui, @@ -44,14 +45,9 @@ var Header = function ( let scene_selector_str = ""; for (let scene in sceneDescList) { if (data.sceneDescList[scene]) - scene_selector_str += - ""; + scene_selector_str += `` else scene_selector_str += ""; @@ -66,7 +62,13 @@ var Header = function ( let curentValue = this.sceneSelectorUi.value; this.data.readSceneList().then((sceneDescList) => { - this.updateSceneList(sceneDescList); + let obj = {} + if(sceneDescList.code === 200){ + sceneDescList.data.forEach(item=>{ + obj[item.sceneName] = item.sceneId + }) + } + this.updateSceneList(obj); this.sceneSelectorUi.value = curentValue; }); }; @@ -187,6 +189,60 @@ var Header = function ( this.ui.querySelector("#save-button").onclick = () => { saveWorldList(this.data.worldList); }; + this.ui.querySelector("#save-submit").onclick = () => { + let confirm = window.confirm("确认提交?") + const microData = window.microApp.getData() + if(confirm){ + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function(){ + if (this.status == 200){ + console.log('提交成功') + window.microApp.dispatch({ type: 'pointCloudBack', time: new Date() }) + } + } + xhr.open( + "POST", + `${http.requestHttp}/project/label`, + true + ); + xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.setRequestHeader('authorization', http.token); + xhr.send(JSON.stringify({ + dataId:microData.dataId, + type:1, + label:'', + taskId:microData.taskId, + })); + } + }; + // 通过 + this.ui.querySelector("#save-pass").onclick = () => { + qc(1) + }; + // 打回 + this.ui.querySelector("#save-reject").onclick = () => { + qc(0) + }; + function qc(type){ + const microData = window.microApp.getData() + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function(){ + if (this.status == 200){ + window.microApp.dispatch({ type: 'pointCloudBack', time: new Date() }) + } + } + xhr.open( + "POST", + `${http.requestHttp}/project/qc?dataId=${microData.dataId}&type=${type}&taskId=${microData.taskId}`, + true + ); + xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.setRequestHeader('authorization', http.token); + xhr.send(); + } + this.ui.querySelector("#back").onclick = () => { + window.microApp.dispatch({ type: 'pointCloudBack', time: new Date() }) + }; }; export { Header }; diff --git a/public/js/http.js b/public/js/http.js new file mode 100644 index 0000000..60b1ed7 --- /dev/null +++ b/public/js/http.js @@ -0,0 +1,9 @@ +const http = { + requestHttp: location.host.indexOf('localhost:8080')!=-1 || + location.host.indexOf('localhost:8081')!=-1 || + location.host.indexOf('192.168.5.177:8080')!=-1 || + location.host.indexOf('192.168.5.177:8081')!=-1 ? + 'http://192.168.5.171:8080' : '/api', + token: `Bearer ${localStorage.getItem('token')}` +} +export default http \ No newline at end of file diff --git a/public/js/image.js b/public/js/image.js index e1be52b..b4a785d 100644 --- a/public/js/image.js +++ b/public/js/image.js @@ -532,6 +532,7 @@ class ImageContext extends MovableView { // active img is set by global, it's not set sometimes. var img = this.world.cameras.getImageByName(this.name); + console.log(img,'img+++++++++++++++++++++') if (img) { svgimage.setAttribute("xlink:href", img.src); } diff --git a/public/js/lidar.js b/public/js/lidar.js index 0d7ae9e..980882f 100644 --- a/public/js/lidar.js +++ b/public/js/lidar.js @@ -63,177 +63,188 @@ function Lidar(sceneMeta, world, frameInfo) { }; this.preload = function (on_preload_finished) { - console.log('载入钱') this.on_preload_finished = on_preload_finished; var loader = new PCDLoader(); var _self = this; - loader.load( - this.frameInfo.get_pcd_path(), - //ok - function (pcd) { - _self.points_parse_time = new Date().getTime(); - console.log( - _self.points_load_time, - _self.frameInfo.scene, - _self.frameInfo.frame, - "parse pionts ", - _self.points_parse_time - _self.create_time, - "ms" - ); - // if (_self.frameInfo.transform_matrix){ - - // var arr = position; - // var num = position.length; - // var ni = 3; - - // for (var i=0; i 0) - geometry.setAttribute( - "position", - new THREE.Float32BufferAttribute(position, 3) - ); - - let normal = pcd.normal; - // normal and colore are note used in av scenes. - if (normal.length > 0) - geometry.setAttribute( - "normal", - new THREE.Float32BufferAttribute(normal, 3) + + // 定义实际的点云加载函数 + const doPcdLoad = function() { + loader.load( + _self.frameInfo.get_pcd_path(), + //ok + function (pcd) { + _self.points_parse_time = new Date().getTime(); + console.log( + _self.points_load_time, + _self.frameInfo.scene, + _self.frameInfo.frame, + "parse pionts ", + _self.points_parse_time - _self.create_time, + "ms" ); + // if (_self.frameInfo.transform_matrix){ - let color = pcd.color; - if (color.length == 0) { - color = []; - - // by default we set all points to same color - for (let i = 0; i < position.length; ++i) { - color.push(_self.data.cfg.point_brightness); - } - - // if enabled intensity we color points by intensity. - if ( - _self.data.cfg.color_points == "intensity" && - pcd.intensity.length > 0 - ) { - // map intensity to color - for (var i = 0; i < pcd.intensity.length; ++i) { - let intensity = pcd.intensity[i]; - intensity *= 8; + // var arr = position; + // var num = position.length; + // var ni = 3; - if (intensity > 1) intensity = 1.0; + // for (var i=0; i 0) + geometry.setAttribute( + "position", + new THREE.Float32BufferAttribute(position, 3) + ); + + let normal = pcd.normal; + // normal and colore are note used in av scenes. + if (normal.length > 0) + geometry.setAttribute( + "normal", + new THREE.Float32BufferAttribute(normal, 3) + ); + + let color = pcd.color; + if (color.length == 0) { + color = []; + + // by default we set all points to same color + for (let i = 0; i < position.length; ++i) { + color.push(_self.data.cfg.point_brightness); + } - /* - - if ( color.length > 0 ) { - material.vertexColors = color; - } else { - //material.color.setHex(0xffffff); - material.color.r = 0.6; - material.color.g = 0.6; - material.color.b = 0.6; - } - */ + // if enabled intensity we color points by intensity. + if ( + _self.data.cfg.color_points == "intensity" && + pcd.intensity.length > 0 + ) { + // map intensity to color + for (var i = 0; i < pcd.intensity.length; ++i) { + let intensity = pcd.intensity[i]; + intensity *= 8; - //material.size = 2; - material.sizeAttenuation = false; + if (intensity > 1) intensity = 1.0; - // build mesh + //color.push( 2 * Math.abs(0.5-intensity)); - var mesh = new THREE.Points(geometry, material); - mesh.name = "pcd"; + color[i * 3] = intensity; + color[i * 3 + 1] = intensity; + color[i * 3 + 2] = 1 - intensity; + } + } - //return mesh; - // add to parent. - _self.world.webglGroup.add(mesh); + // save color, in case color needs to be restored. + pcd.color = color; + } - _self.points = mesh; - _self.pcd = pcd; - //_self.points_backup = mesh; + geometry.setAttribute( + "color", + new THREE.Float32BufferAttribute(color, 3) + ); - _self.build_points_index(); - _self.points_load_time = new Date().getTime(); + geometry.computeBoundingSphere(); + // build material + + var material = new THREE.PointsMaterial({ + size: _self.data.cfg.point_size, + vertexColors: THREE.VertexColors, + }); + + /* + + if ( color.length > 0 ) { + material.vertexColors = color; + } else { + //material.color.setHex(0xffffff); + material.color.r = 0.6; + material.color.g = 0.6; + material.color.b = 0.6; + } + */ + + //material.size = 2; + material.sizeAttenuation = false; + + // build mesh + + var mesh = new THREE.Points(geometry, material); + mesh.name = "pcd"; + + //return mesh; + // add to parent. + _self.world.webglGroup.add(mesh); + + _self.points = mesh; + _self.pcd = pcd; + //_self.points_backup = mesh; + + _self.build_points_index(); + _self.points_load_time = new Date().getTime(); + + console.log( + _self.points_load_time, + _self.frameInfo.scene, + _self.frameInfo.frame, + "loaded pionts ", + _self.points_load_time - _self.create_time, + "ms" + ); - console.log( - _self.points_load_time, - _self.frameInfo.scene, - _self.frameInfo.frame, - "loaded pionts ", - _self.points_load_time - _self.create_time, - "ms" - ); + _self._afterPreload(); + }, - _self._afterPreload(); - }, + // on progress, + function () {}, - // on progress, - function () {}, + // on error + function () { + //error + console.log("load pcd failed."); + _self._afterPreload(); + }, - // on error - function () { - //error - console.log("load pcd failed."); - _self._afterPreload(); - }, + // on file loaded + function () { + _self.points_readfile_time = new Date().getTime(); + console.log( + _self.points_load_time, + _self.frameInfo.scene, + _self.frameInfo.frame, + "read file ", + _self.points_readfile_time - _self.create_time, + "ms" + ); + } + ); + }; - // on file loaded - function () { - _self.points_readfile_time = new Date().getTime(); - console.log( - _self.points_load_time, - _self.frameInfo.scene, - _self.frameInfo.frame, - "read file ", - _self.points_readfile_time - _self.create_time, - "ms" - ); - } - ); + // 如果已经有注释数据,立即加载点云 + if (this.frameInfo.annotationData !== null) { + doPcdLoad(); + } else { + // 否则保存回调,等待注释数据到达后再执行 + this.frameInfo.loadCallback = doPcdLoad; + } }; this.deleteAll = function () { diff --git a/public/js/mouse.js b/public/js/mouse.js index 8862169..2db5007 100644 --- a/public/js/mouse.js +++ b/public/js/mouse.js @@ -1,5 +1,6 @@ import * as THREE from "./lib/three.module.js"; +const microData = window.microApp.getData() function Mouse( view, op_state, @@ -24,6 +25,10 @@ function Mouse( this.domElement.addEventListener( "mousedown", (e) => { + // 已标注或者质检中不可编辑 + if(microData.labelState === '1' || microData.taskTag === '0'){ + return + } this.onMouseDown(e); }, true @@ -85,6 +90,7 @@ function Mouse( }; this.onMouseDown = function (event) { + in_select_mode = false; if (event.which == 3) { diff --git a/public/js/request.js b/public/js/request.js index 23b0191..ef79647 100644 --- a/public/js/request.js +++ b/public/js/request.js @@ -1,4 +1,4 @@ -const request = location.host.indexOf('localhost:8080')!=-1 ? +const request = location.host.indexOf('localhost:8080')!=-1 || location.host.indexOf('192.168.5.177:8080')!=-1 ? '/3d-point-cloud' : location.host.indexOf('localhost:8081')!=-1 || location.host.indexOf('192.168.5.177:8081')!=-1 ? '' : '/3Dpoints' diff --git a/public/js/save.js b/public/js/save.js index ca03855..0dfd8f9 100644 --- a/public/js/save.js +++ b/public/js/save.js @@ -2,6 +2,7 @@ import { Editor } from "./editor.js"; import { checkScene } from "./error_check.js"; import { logger } from "./log.js"; import request from "./request.js"; +import http from "./http.js"; function reloadWorldList(worldList, done) { var xhr = new XMLHttpRequest(); @@ -86,23 +87,28 @@ function doSaveWorldList(worldList, done) { } } - console.log(worldList.length, "frames"); let ann = worldList.map((w) => { return { - scene: w.frameInfo.scene, - frame: w.frameInfo.frame, - annotation: w.annotation.toBoxAnnotations(), + frameId: w.frameInfo.frame?.frameId??w.frameInfo.frame, + label: JSON.stringify(w.annotation.toBoxAnnotations()), }; }); - + console.log(ann,'ann') var xhr = new XMLHttpRequest(); - xhr.open("POST", request + "/saveworldlist", true); - xhr.setRequestHeader("Content-Type", "application/json"); + xhr.open("POST", http.requestHttp + "/project/frameSave", true); + xhr.setRequestHeader("Content-Type","application/json") + xhr.setRequestHeader('authorization', http.token); xhr.onreadystatechange = function () { if (this.readyState != 4) return; if (this.status == 200) { + document.getElementById('saveSuccess').style.display = "block"; + document.getElementById('saveSuccess').innerText = "保存成功!"; + let timeout = setTimeout(() => { + document.getElementById('saveSuccess').style.display = "none"; + clearTimeout(timeout); + }, 3000); worldList.forEach((w) => { w.annotation.resetModified(); }); @@ -123,13 +129,22 @@ function doSaveWorldList(worldList, done) { `save failed, status : ${this.status}` ); } - - // end of state change: it can be after some time (async) }; - - var b = JSON.stringify(ann); - //console.log(b); - xhr.send(b); + const uniqueArray = deduplicatePreferString(ann) + xhr.send(JSON.stringify({frameSaveDTOList:uniqueArray})); +} +function deduplicatePreferString(array) { + const map = new Map(); + + array.forEach(item => { + const key = String(item.id); + // 如果还没有这个key,或者已存在的不是string类型而当前的是string类型 + if (!map.has(key) || (typeof map.get(key).id !== 'string' && typeof item.id === 'string')) { + map.set(key, item); + } + }); + + return Array.from(map.values()); } // function saveWorld(world, done){ diff --git a/public/js/world.js b/public/js/world.js index c00af68..0b454ef 100644 --- a/public/js/world.js +++ b/public/js/world.js @@ -7,6 +7,7 @@ import { Annotation } from "./annotation.js"; import { EgoPose } from "./ego_pose.js"; import { logger } from "./log.js"; import request from "./request.js"; +import fileHttp from "./fileHttp.js"; import { euler_angle_to_rotate_matrix, euler_angle_to_rotate_matrix_3by3, @@ -21,12 +22,14 @@ function FrameInfo(data, sceneMeta, sceneName, frame) { this.dir = ""; this.scene = sceneName; this.frame = frame; + this.annotationData = null; this.pcd_ext = ""; + this.loadCallback = null; (this.frame_index = this.sceneMeta.frames.findIndex(function (x) { return x == frame; })), (this.transform_matrix = this.sceneMeta.point_transform_matrix), - (this.annotation_format = this.sceneMeta.boxtype), //xyz(24 number), csr(center, scale, rotation, 9 number) + (this.annotation_format = this.sceneMeta.boxType), //xyz(24 number), csr(center, scale, rotation, 9 number) // this.set = function(scene, frame_index, frame, transform_matrix, annotation_format){ // this.scene = scene; // this.frame = frame; @@ -36,15 +39,18 @@ function FrameInfo(data, sceneMeta, sceneName, frame) { // }; (this.get_pcd_path = function () { + if (this.annotationData) { + return fileHttp + this.annotationData.lidarPcdPath; + } return ( - request + "/data/" + this.scene + "/lidar/" + this.frame + this.sceneMeta.lidar_ext + request + "/data/" + this.scene + "/lidar/" + this.frame + this.sceneMeta.lidarExt ); }); this.get_radar_path = function (name) { - return `${request}/data/${this.scene}/radar/${name}/${this.frame}${this.sceneMeta.radar_ext}`; + return `${request}/data/${this.scene}/radar/${name}/${this.frame}${this.sceneMeta.radarExt}`; }; this.get_aux_lidar_path = function (name) { - return `${request}/data/${this.scene}/aux_lidar/${name}/${this.frame}${this.sceneMeta.radar_ext}`; + return `${request}/data/${this.scene}/aux_lidar/${name}/${this.frame}${this.sceneMeta.radarExt}`; }; this.get_anno_path = function () { @@ -55,6 +61,17 @@ function FrameInfo(data, sceneMeta, sceneName, frame) { } }; + // 设置注释数据并触发回调 + this.setAnnotationData = function(data) { + this.annotationData = data; + + // 如果已经有加载回调但尚未执行,则执行它 + if (this.loadCallback && !this.loadCallback.executed) { + this.loadCallback.executed = true; + this.loadCallback(); + } + }; + this.anno_to_boxes = function (text) { var _self = this; if (this.annotation_format == "psr") { @@ -166,6 +183,11 @@ function FrameInfo(data, sceneMeta, sceneName, frame) { function Images(sceneMeta, sceneName, frame) { this.loaded = function () { + // 如果没有相机名称,则认为已加载完成 + if (!this.names || this.names.length === 0) { + return true; + } + for (var n in this.names) { if (!this.loaded_flag[this.names[n]]) return false; } @@ -175,61 +197,94 @@ function Images(sceneMeta, sceneName, frame) { this.names = sceneMeta.camera; //["image","left","right"], this.loaded_flag = {}; - // this.active_name = ""; - // this.active_image = function(){ - // return this.content[this.active_name]; - // }; + this.content = {}; + this.on_all_loaded = null; + this.annotationData = null; + this.sceneName = sceneName; + this.frame = frame; + this.sceneMeta = sceneMeta; + this.loadCallback = null; + this.active_name = null; + this.getImageByName = function (name) { return this.content[name]; }; - // this.activate = function(name){ - // this.active_name = name; - // }; - - this.content = {}; - this.on_all_loaded = null; + // 设置注释数据并触发图像加载 + this.setAnnotationData = function(data) { + this.annotationData = data; + + // 如果已经有加载回调但尚未执行,则执行它 + if (this.loadCallback && !this.loadCallback.executed) { + this.loadCallback.executed = true; + this.loadCallback(); + } + }; - (this.load = function (on_all_loaded, active_name) { + this.load = function (on_all_loaded, active_name) { this.on_all_loaded = on_all_loaded; - - // if global camera not set, use first camera as default. - // if (active_name.length > 0) - // this.active_name = active_name; - // else if (this.names && this.names.length>0) - // this.active_name = this.names[0]; + this.active_name = active_name; var _self = this; + + // 定义实际的图像加载函数 + const doImageLoad = function() { + // 如果 global camera 未设置,使用第一个相机作为默认 + // if (active_name.length > 0) + // this.active_name = active_name; + // else if (this.names && this.names.length>0) + // this.active_name = this.names[0]; + + if (_self.names) { + _self.names.forEach(function (cam) { + _self.content[cam] = new Image(); + _self.content[cam].onload = function () { + _self.loaded_flag[cam] = true; + _self.on_image_loaded(); + }; + _self.content[cam].onerror = function () { + _self.loaded_flag[cam] = true; + _self.on_image_loaded(); + }; + + // 使用注释数据构造图像路径(如果可用) + if (_self.annotationData) { + _self.content[cam].src = fileHttp + _self.annotationData[`${cam}ImgPath`] + } else { + // 回退到原来的路径 + _self.content[cam].src = + "data/example/camera/" + + cam + + "/000965" + + _self.sceneMeta.cameraExt; + } + + console.log("image set", _self.content[cam].src); + }); + } else { + // 没有相机名称,直接调用完成回调 + if (_self.on_all_loaded) { + _self.on_all_loaded(); + } + } + }; - if (this.names) { - this.names.forEach(function (cam) { - _self.content[cam] = new Image(); - _self.content[cam].onload = function () { - _self.loaded_flag[cam] = true; - _self.on_image_loaded(); - }; - _self.content[cam].onerror = function () { - _self.loaded_flag[cam] = true; - _self.on_image_loaded(); - }; - - _self.content[cam].src = - "data/" + - sceneName + - "/camera/" + - cam + - "/" + - frame + - sceneMeta.camera_ext; - console.log("image set"); - }); + // 如果已经有注释数据,立即加载图像 + if (this.annotationData) { + doImageLoad(); + } else { + // 否则保存回调,等待注释数据到达后再执行 + this.loadCallback = doImageLoad; } - }), - (this.on_image_loaded = function () { - if (this.loaded()) { + }; + + this.on_image_loaded = function () { + if (this.loaded()) { + if (this.on_all_loaded) { this.on_all_loaded(); } - }); + } + }; } function World(data, sceneName, frame, coordinatesOffset, on_preload_finished) {