카메라 스트림 분석 - 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-head
HTML 요소에 연결됩니다. -
evt.transceiver.mid === '1'
:mid
가 '1'인 비디오 트랙은video-wrist-left
HTML 요소에 연결됩니다. -
evt.transceiver.mid === '2'
:mid
가 '2'인 비디오 트랙은video-wrist-right
HTML 요소에 연결됩니다.
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
데이터 채널을 통해 로봇으로 전송하는 두 가지 형태의 카메라 활용을 병행하고 있습니다.