Fix draw detection box.

main
dongjin kim 7 months ago
parent f0d71f58db
commit ce3142a98b

@ -234,14 +234,13 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
}; };
// [수정됨] 다중 파일 업로드 처리 // 다중 파일 업로드 처리
window.uploadPoiImage = function(name, input) { window.uploadPoiImage = function(name, input) {
if (!input.files || input.files.length === 0) return; if (!input.files || input.files.length === 0) return;
const formData = new FormData(); const formData = new FormData();
formData.append('poiName', name); formData.append('poiName', name);
// 선택된 모든 파일을 formData에 추가
for (let i = 0; i < input.files.length; i++) { for (let i = 0; i < input.files.length; i++) {
const file = input.files[i]; const file = input.files[i];
const ext = file.name.split('.').pop().toLowerCase(); const ext = file.name.split('.').pop().toLowerCase();
@ -251,7 +250,6 @@ document.addEventListener('DOMContentLoaded', () => {
input.value = ''; input.value = '';
return; return;
} }
formData.append('poiFile', file); formData.append('poiFile', file);
} }
@ -312,7 +310,7 @@ document.addEventListener('DOMContentLoaded', () => {
} }
// ================================================= // =================================================
// 4. 비디오 & 웹소켓 (기존 기능) // 4. 비디오 & 웹소켓 (좌표 그리기 로직 추가됨)
// ================================================= // =================================================
const uri = "ws://10.10.11.246:8765"; const uri = "ws://10.10.11.246:8765";
const canvasEl = document.getElementById("frame"); const canvasEl = document.getElementById("frame");
@ -329,24 +327,42 @@ document.addEventListener('DOMContentLoaded', () => {
let lastFpsCheckTime = performance.now(); let lastFpsCheckTime = performance.now();
let lastFrameMeta = null; let lastFrameMeta = null;
// [신규] 캔버스 영상의 배율(scale)과 여백(offset)을 저장하여 좌표 변환에 사용
let viewConfig = { r: 1, dx: 0, dy: 0 };
function connect() { function connect() {
const ws = new WebSocket(uri); const ws = new WebSocket(uri);
ws.binaryType = "arraybuffer"; ws.binaryType = "arraybuffer";
ws.onopen = () => { statusEl.textContent = "연결됨"; statusEl.className = "status"; };
ws.onclose = () => { statusEl.textContent = "연결 종료. 재접속..."; statusEl.className = "status err"; setTimeout(connect, 2000); }; ws.onopen = () => {
statusEl.textContent = "연결됨";
statusEl.className = "status";
};
ws.onclose = () => {
statusEl.textContent = "연결 종료. 재접속...";
statusEl.className = "status err";
setTimeout(connect, 2000);
};
ws.onmessage = (event) => { ws.onmessage = (event) => {
const data = event.data; const data = event.data;
// 1. 텍스트 데이터(JSON) 처리
if (typeof data === "string") { if (typeof data === "string") {
// console.log("-----" + data);
try { try {
console.log("-----" + data);
const meta = JSON.parse(data); const meta = JSON.parse(data);
if (meta.type === "frame") { if (meta.type === "frame") {
lastFrameMeta = meta; lastFrameMeta = meta;
document.getElementById("frame-info").textContent = ` | FRAME ${meta.w}x${meta.h}`; document.getElementById("frame-info").textContent = ` | FRAME ${meta.w}x${meta.h}`;
} else if (meta.type === "det") { } else if (meta.type === "det") {
// 탐지 정보가 오면 박스를 그림
showDetections(meta); showDetections(meta);
} }
} catch(e){} } catch(e){ console.error(e); }
// 2. 바이너리 데이터(영상 프레임) 처리
} else if (data instanceof ArrayBuffer && lastFrameMeta) { } else if (data instanceof ArrayBuffer && lastFrameMeta) {
frameCount++; frameCount++;
const now = performance.now(); const now = performance.now();
@ -355,18 +371,28 @@ document.addEventListener('DOMContentLoaded', () => {
frameCount = 0; frameCount = 0;
lastFpsCheckTime = now; lastFpsCheckTime = now;
} }
const blob = new Blob([data], {type: "image/jpeg"}); const blob = new Blob([data], {type: "image/jpeg"});
createImageBitmap(blob).then(bmp => { createImageBitmap(blob).then(bmp => {
// 캔버스 크기에 맞춰 영상 비율(레터박스) 계산
canvasEl.width = canvasEl.clientWidth; canvasEl.width = canvasEl.clientWidth;
canvasEl.height = canvasEl.clientHeight; canvasEl.height = canvasEl.clientHeight;
// r: 축소/확대 비율, dx/dy: 중앙 정렬을 위한 여백
const r = Math.min(canvasEl.width / bmp.width, canvasEl.height / bmp.height); const r = Math.min(canvasEl.width / bmp.width, canvasEl.height / bmp.height);
const dw = bmp.width * r; const dw = bmp.width * r;
const dh = bmp.height * r; const dh = bmp.height * r;
const dx = (canvasEl.width - dw) / 2; const dx = (canvasEl.width - dw) / 2;
const dy = (canvasEl.height - dh) / 2; const dy = (canvasEl.height - dh) / 2;
// 좌표 변환을 위해 현재 뷰 설정 전역 변수에 저장
viewConfig = { r, dx, dy };
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height); ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
ctx.drawImage(bmp, dx, dy, dw, dh); ctx.drawImage(bmp, dx, dy, dw, dh);
bmp.close(); bmp.close();
// 창 크기 변화 시 박스 컨테이너 위치도 동기화
updateBboxContainerPosition(); updateBboxContainerPosition();
}); });
} }
@ -374,50 +400,83 @@ document.addEventListener('DOMContentLoaded', () => {
} }
/** /**
* [수정됨] 상세 탐지 정보 출력 함수 * [수정됨] 탐지 정보를 받아 화면에 Bounding Box를 그리는 함수
* 입력 : {"type": "det", "ch": 0, "seq": 16939, "ts_us": 833298660, "items": [{"x1": ..., "tag": 1, ...}]}
*/ */
function showDetections(meta) { function showDetections(meta) {
bboxContainerEl.innerHTML = ""; // 기존 bbox 초기화 (필요시 여기에 박스 그리기 로직 추가 가능) // 기존 박스들 초기화
bboxContainerEl.innerHTML = "";
// 1. 메타 정보 라인 구성 (ch, seq, ts_us) // 메타 정보 표시 준비
const ch = meta.ch !== undefined ? meta.ch : '-'; const ch = meta.ch !== undefined ? meta.ch : '-';
const seq = meta.seq !== undefined ? meta.seq : '-'; const seq = meta.seq !== undefined ? meta.seq : '-';
const ts = meta.ts_us !== undefined ? meta.ts_us : '-'; const ts = meta.ts_us !== undefined ? meta.ts_us : '-';
let outputLines = [`[META] CH:${ch} | SEQ:${seq} | TS:${ts}`];
let outputLines = [];
outputLines.push(`[META] CH:${ch} | SEQ:${seq} | TS:${ts}`);
// 2. 탐지 객체 리스트 구성
const items = meta.items || []; const items = meta.items || [];
if (items.length === 0) { if (items.length === 0) {
outputLines.push("No detections"); outputLines.push("No detections");
} else { } else {
// 각 탐지 객체에 대해 처리
items.forEach((it, i) => { items.forEach((it, i) => {
// 좌표값 소수점 2자리로 반올림 // 원본 영상 기준 좌표
const x1 = it.x1 ? it.x1.toFixed(2) : 0; const x1 = it.x1 || 0;
const y1 = it.y1 ? it.y1.toFixed(2) : 0; const y1 = it.y1 || 0;
const x2 = it.x2 ? it.x2.toFixed(2) : 0; const x2 = it.x2 || 0;
const y2 = it.y2 ? it.y2.toFixed(2) : 0; const y2 = it.y2 || 0;
const tag = it.tag !== undefined ? it.tag : '-'; const cls = it.cls !== undefined ? it.cls : '?';
const cls = it.cls !== undefined ? it.cls : '-'; const tag = it.tag !== undefined ? it.tag : '';
outputLines.push(`#${i} | Class:${cls} | Tag:${tag} | Box:[${x1}, ${y1}, ${x2}, ${y2}]`); // 우측 정보창에 텍스트 로그 추가
outputLines.push(`#${i} | Class:${cls} | Tag:${tag} | Box:[${x1.toFixed(1)}, ${y1.toFixed(1)}, ${x2.toFixed(1)}, ${y2.toFixed(1)}]`);
// 좌표 변환: 원본 좌표 -> 캔버스 화면 좌표
// 공식: 화면좌표 = 여백 + (원본좌표 * 배율)
const { r, dx, dy } = viewConfig;
const screenX = dx + (x1 * r);
const screenY = dy + (y1 * r);
const screenW = (x2 - x1) * r;
const screenH = (y2 - y1) * r;
// 박스 DOM 요소 생성
const boxDiv = document.createElement('div');
boxDiv.className = 'bbox'; // style.css에 정의된 붉은 박스 스타일 사용
boxDiv.style.left = `${screenX}px`;
boxDiv.style.top = `${screenY}px`;
boxDiv.style.width = `${screenW}px`;
boxDiv.style.height = `${screenH}px`;
// 박스 위 라벨 (선택 사항)
const label = document.createElement('div');
label.style.position = 'absolute';
label.style.top = '-16px';
label.style.left = '0';
label.style.backgroundColor = 'red';
label.style.color = 'white';
label.style.fontSize = '10px';
label.style.padding = '1px 3px';
label.style.whiteSpace = 'nowrap';
label.textContent = `C:${cls} T:${tag}`;
boxDiv.appendChild(label);
bboxContainerEl.appendChild(boxDiv);
}); });
} }
// 3. 화면 출력 // 우측 정보창 업데이트
detListEl.textContent = outputLines.join("\n"); detListEl.textContent = outputLines.join("\n");
} }
function updateBboxContainerPosition() { function updateBboxContainerPosition() {
if (!canvasEl || !bboxContainerEl) return; if (!canvasEl || !bboxContainerEl) return;
// 캔버스의 실제 위치와 크기에 박스 컨테이너를 일치시킴
bboxContainerEl.style.top = canvasEl.offsetTop + 'px'; bboxContainerEl.style.top = canvasEl.offsetTop + 'px';
bboxContainerEl.style.left = canvasEl.offsetLeft + 'px'; bboxContainerEl.style.left = canvasEl.offsetLeft + 'px';
bboxContainerEl.style.width = canvasEl.offsetWidth + 'px'; bboxContainerEl.style.width = canvasEl.offsetWidth + 'px';
bboxContainerEl.style.height = canvasEl.offsetHeight + 'px'; bboxContainerEl.style.height = canvasEl.offsetHeight + 'px';
} }
// 모델 변경 이벤트
if (modelContainerEl) { if (modelContainerEl) {
modelContainerEl.addEventListener('change', (e) => { modelContainerEl.addEventListener('change', (e) => {
if(e.target.name === 'current-model') { if(e.target.name === 'current-model') {
@ -440,6 +499,7 @@ document.addEventListener('DOMContentLoaded', () => {
} }
} }
// 초기 실행
updateModelDisplay(); updateModelDisplay();
if (canvasEl) connect(); if (canvasEl) connect();
window.addEventListener('resize', updateBboxContainerPosition); window.addEventListener('resize', updateBboxContainerPosition);

Loading…
Cancel
Save