카메라 스트림 분석 - dingdongdengdong/astra_ws GitHub Wiki
사용자님께서 index.js 파일을 제공해주셨습니다. 이 파일을 통해 웹 페이지에서 카메라 스트림이 어떻게 처리되고 표시되는지, 그리고 원격 조종 카메라가 어떤 것인지 더 명확하게 이해할 수 있습니다.
index.js 파일에서 start() 함수 내의 pc.addEventListener('track', ...) 부분을 보면, WebRTC peer connection을 통해 수신되는 비디오 트랙을 처리하는 로직이 있습니다:
pc.addEventListener('track', function (evt) {
if (evt.track.kind === 'video') {
if (evt.transceiver.mid === '0') {
document.getElementById('video-head').srcObject = new MediaStream([evt.track]);
document.getElementById('video-head').play();
} else if (evt.transceiver.mid === '1') {
document.getElementById('video-wrist-left').srcObject = new MediaStream([evt.track]);
document.getElementById('video-wrist-left').play();
} else if (evt.transceiver.mid === '2') {
document.getElementById('video-wrist-right').srcObject = new MediaStream([evt.track]);
document.getElementById('video-wrist-right').play();
} else {
toastr.error("Unsupported mid")
}
}
});이 코드는 다음과 같이 동작합니다:
-
evt.transceiver.mid === '0':mid가 '0'인 비디오 트랙은video-headHTML 요소에 연결됩니다. -
evt.transceiver.mid === '1':mid가 '1'인 비디오 트랙은video-wrist-leftHTML 요소에 연결됩니다. -
evt.transceiver.mid === '2':mid가 '2'인 비디오 트랙은video-wrist-rightHTML 요소에 연결됩니다.
webserver.py에서 pc.addTransceiver를 호출할 때, head 트랙에 mid: 0, wrist_left 트랙에 mid: 1, wrist_right 트랙에 mid: 2가 할당됩니다. 따라서 index.js는 webserver.py가 보내는 비디오 스트림을 올바른 <video> 태그에 매핑하는 역할을 합니다.
index.js 파일에서 사용자님의 "내 원격 조종 카메라" (컴퓨터 웹캠)와 직접적으로 관련된 부분은 capture() 함수와 canvas-imshow 요소입니다.
index.html에서 Start Capture 버튼은 capture() 함수를 호출합니다. index.js의 capture() 함수는 다음과 같은 중요한 역할을 합니다:
async function capture() {
// ... (생략) ...
const captureMediaStream = await navigator.mediaDevices.getUserMedia({ video: {
facingMode: "environment",
width: { exact: 640 },
height: { exact: 360 },
} });
const captureVideo = document.createElement('video');
captureVideo.srcObject = captureMediaStream;
await captureVideo.play();
// ... (생략) ...
const interval = setInterval(() => {
// ... (생략) ...
try {
if (typeof(pedalChannel) === 'undefined') {
return;
}
if (typeof(handChannel) === 'undefined') {
return;
}
const res = process_image(captureVideo, cv); // OpenCV.js를 통해 이미지 처리
if (res === null) {
return;
}
const {
camera_matrix_data,
distortion_coefficients_data,
corners_data,
ids_data,
canvas_image_data,
error_message,
detection_timing_ms,
} = res;
if (corners_data !== null && ids_data !== null) {
handCommTarget.dispatchEvent(new CustomEvent('message', { detail: [
camera_matrix_data,
distortion_coefficients_data,
corners_data,
ids_data
]}));
// ... (생략) ...
handChannel.send(JSON.stringify([
camera_matrix_data,
distortion_coefficients_data,
corners_data,
ids_data
])); // handChannel을 통해 데이터 전송
}
// ... (생략) ...
cv.imshow('canvas-imshow', canvas_image_data); // 캔버스에 이미지 표시
// ... (생략) ...
}
// ... (생략) ...
}, 30); // 약 30FPS로 처리
}핵심 내용:
-
navigator.mediaDevices.getUserMedia({ video: ... }): 이 부분이 웹 브라우저가 사용자 컴퓨터의 웹캠(video0에 해당)에 접근하여 비디오 스트림을 가져오는 코드입니다. -
process_image(captureVideo, cv): 캡처된 웹캠 비디오(captureVideo)를OpenCV.js(cv)를 사용하여 로컬에서 처리합니다. 이 처리 과정에서 ARUCO 마커(corners_data,ids_data) 등을 감지하여 원격 조종을 위한 데이터를 추출합니다. -
cv.imshow('canvas-imshow', canvas_image_data): 처리된 이미지는canvas-imshow라는 ID를 가진<canvas>요소에 표시됩니다. 이것이 바로 웹 페이지의 "Capture Playback" 섹션에 나타나는 영상입니다. -
handChannel.send(JSON.stringify(...)):corners_data와ids_data(ARUCO 마커 정보)가 감지되면, 이 데이터는handChannel이라는 DataChannel을 통해webserver.py로 전송됩니다. 이는 사용자님의 손 움직임이 로봇에 제어 신호로 전달되는 방식입니다.
"총 4개의 카메라 데이터 중 맨 오른쪽 위에 있는 카메라 데이터가 내 원격조종 카메라입니다"라고 말씀하신 부분과 index.js를 종합해 볼 때, 다음과 같습니다:
-
원격 조종 카메라 (사용자 컴퓨터 웹캠):
index.html의 "Capture Playback" 섹션 아래canvas-imshow에 표시되는 영상입니다. 이 카메라는navigator.mediaDevices.getUserMedia를 통해 브라우저가 직접 접근하며, 그 영상은 로컬에서 OpenCV.js로 처리되어 손 제스처를 위한hand데이터 채널을 통해 로봇으로 제어 신호를 보냅니다. 이 영상은webserver.py를 통해 스트리밍되는 다른 3개의 로봇 카메라와는 별개입니다. -
로봇에 달린 3개의 카메라: 이 카메라들은
webserver.py를 통해 웹으로 스트리밍되어video-head,video-wrist-left,video-wrist-right<video>태그에 각각 표시됩니다.video-head는 메인 화면에,video-wrist-left와video-wrist-right는 그 아래에 나란히 표시될 것입니다.
따라서, teleop_web_node.py를 통해 실행되는 전체 시스템은 video_head (ROS 토픽 cam_head/image_raw에서 오는 데이터)를 웹으로 스트리밍하는 것과 동시에, 웹 페이지에서 로컬 웹캠을 사용하여 손 제스처를 인식하고 이를 hand 데이터 채널을 통해 로봇으로 전송하는 두 가지 형태의 카메라 활용을 병행하고 있습니다.