본문으로 건너뛰기

USB 카메라 사용하기

nodes::UsbCameraInput은 MJPEG를 보내는 USB(UVC) 웹캠을 읽고 카메라의 JPEG 프레임을 출력합니다. 카메라의 고정 ID를 전달하세요. UsbCameraInputOptions는 (MIPI 카메라의) CameraInputOptions와 같은 필드 이름과 기본값을 사용하지만, camera_name은 필수이고 format은 없습니다.

USB camera -> UsbCameraInput (JPEG) -+-> VideoSender passthrough (MJPEG)
`-> SimaDecode (NV12 or NV16) -> model

대부분의 웹캠은 4:2:2 JPEG를 보내며, 이는 NV16으로 디코딩됩니다. usb_camera_info()는 카메라의 JPEG가 디코딩되는 레이아웃을 알려 줍니다. 이 값을 SimaDecode의 out_format으로 사용하고, NV16을 반환하면 모델의 preprocess.color_convert.input_format을 PreprocessColorFormat::NV16으로 설정하세요. 그러면 프레임이 변환 단계 없이 모델에 전달됩니다.

Neat가 지원하는 범위​

  • Platform 3.0에서 Modalix DevKit USB 포트에 연결한 UVC 카메라.
  • 4:2:0 또는 4:2:2 크로마 샘플링의 MJPEG 캡처.
  • 카메라가 MJPEG용으로 제공하는 정확한 너비, 높이, 프레임 속도. Neat는 크기를 조정하거나 가까운 모드를 고르지 않습니다.

H.264 전용 카메라, 원시 YUYV/NV12 캡처, 깊이 또는 IR 스트림, 노출 같은 카메라 제어는 이 경로에 포함되지 않습니다.

카메라 ID 찾기​

각 USB 카메라에는 USB 포트와 공급업체 ID, 제품 ID로 만든 고정 ID가 있으며, 형식은 libcamera와 같습니다.

/base/pci@7000000000-3.1:1.0-046d:08e5

ID는 재부팅하거나 연결 순서가 바뀌어도 그대로입니다. 카메라를 다른 USB 포트로 옮기면 바뀌므로, 서로 다른 포트에 연결한 같은 카메라 두 대는 ID가 다릅니다. 연결된 카메라를 나열합니다.

for (const std::string& id : simaai::neat::usb_camera_ids()) {
std::cout << id << "\n";
}

카메라가 제공하는 MJPEG 모드를 보려면 DevKit에서 형식 목록을 확인합니다. 장치 번호는 이 확인에만 쓰이며, Neat는 ID로 장치를 찾습니다.

devkit$v4l2-ctl --list-devicesv4l2-ctl -d /dev/video1 --list-formats-ext

그래프 만들기​

#include <neat.h>

namespace neat = simaai::neat;

neat::UsbCameraInputOptions camera;
camera.camera_name = "/base/pci@7000000000-3.1:1.0-046d:08e5";
camera.width = 1920;
camera.height = 1080;
camera.framerate_num = 30;
camera.framerate_den = 1;
camera.buffer_name = "camera0";

neat::Graph graph("usb_camera");
graph.add(neat::nodes::UsbCameraInput(camera));
graph.add(neat::nodes::Output());

neat::Run run = graph.build();
std::optional<neat::Sample> frame = run.pull(/*timeout_ms=*/5000);

노드는 카메라의 JPEG 프레임을 출력합니다. 모델을 실행하려면 다음 절처럼 SimaDecode로 디코딩하세요.

UsbCameraInput은 노드를 만들 때 모드를 확인하고 첫 프레임의 크로마 샘플링을 읽기 위해 카메라를 한 번 엽니다. 먼저 카메라를 사용하는 다른 프로그램을 닫으세요. usb_camera_info(camera)는 노드를 만들지 않고 같은 확인을 수행하며, 캡처 장치와 decoded_format(NV12 또는 NV16)을 반환합니다.

decoded_format도 필요하면 다음 절처럼 usb_camera_info를 한 번 호출하고 그 결과를 UsbCameraInput(camera, info)에 전달하세요. 그러면 노드는 카메라를 다시 열지 않고 그 확인 결과를 재사용하며, 장치가 여전히 해당 카메라에 속하는지 sysfs로만 확인합니다. 두 호출에는 같은 옵션을 전달하세요.

카메라를 뽑았거나 다른 포트로 옮긴 뒤에는 UsbCameraInput을 다시 호출하세요. 이전에 만든 그래프는 빌드할 때 실패하므로, 예전 장치 번호를 넘겨받은 다른 카메라를 여는 일은 없습니다.

카메라의 JPEG 전달하기​

카메라 영상을 뷰어로 보내면서 동시에 모델을 실행하려면, 이 노드를 JPEG를 다시 인코딩하지 않고 RTP/JPEG로 보내는 VideoSender 패스스루와, out_format이 카메라의 decoded_format인 SimaDecode에 연결합니다.

const neat::UsbCameraInfo info = neat::usb_camera_info(camera); // opens the camera once

neat::SimaDecodeOptions decode;
decode.type = neat::SimaDecodeType::MJPEG;
decode.out_format = info.decoded_format; // NV12 or NV16
decode.dec_width = 1920;
decode.dec_height = 1080;
decode.dec_fps = 30;

auto sender = neat::nodes::groups::VideoSenderOptions::Passthrough(
neat::nodes::groups::RtspCodec::MJPEG);
sender.host = "127.0.0.1";

neat::GraphLinkOptions video_link;
video_link.policy = neat::GraphLinkPolicy::RealtimeLatestByStream;

neat::Graph source;
source.add(neat::nodes::UsbCameraInput(camera, info)); // reuses info, no second open
neat::Graph decoder;
decoder.add(neat::nodes::SimaDecode(decode));

neat::Graph graph("usb_camera_forward");
graph.connect(source, decoder);
graph.connect(decoder, detector); // your model Graph
graph.connect(source, neat::nodes::groups::VideoSender(sender), video_link);

카메라는 하드웨어 디코더가 그 자리에서 읽는 CMA 버퍼에 캡처하므로, 캡처와 디코딩 사이에서 프레임이 복사되지 않습니다. 커널 UVC 드라이버는 여전히 USB 패킷을 각 프레임 버퍼로 복사합니다. 송신기에는 비디오 보내기의 RTP/JPEG 제한이 적용됩니다.

메모리와 버려지는 프레임​

옵션효과
queue_depth, leaky_queue카메라 뒤의 작은 leaky 큐가 그래프가 뒤처질 때 오래된 프레임을 버립니다. 카메라의 캡처 풀은 queue_depth + 6개의 버퍼를 가지며 최대 64개이므로, queue_depth는 최대 58입니다.

웹캠은 JPEG 종료 마커 뒤에 바이트를 덧붙이는 경우가 많고, 일부 프레임은 불완전하게 도착하거나 USB 드라이버가 손상으로 표시합니다. UsbCameraInput은 드라이버가 손상으로 표시한 프레임을 출력 전에 버리므로, VideoSender 패스스루와 디코더 모두 이 프레임을 받지 않습니다. 디코더는 각 프레임을 종료 마커까지만 디코딩하고, 불완전하거나 손상된 프레임은 멈추지 않고 버립니다. 디코더 요소는 이를 dropped-count 속성에 셉니다.

문제 해결​

메시지조치
no USB camera with ID ...; connected USB camera IDs: ...목록에 있는 ID 중 하나를 사용하세요. 카메라를 다른 포트로 옮기면 ID가 바뀝니다.
no USB cameras are connected케이블과 lsusb를 확인하세요.
has no MJPEG mode 1920x1080 at 30/1 fps; its MJPEG modes are: ...목록에 있는 모드를 고르세요. 일부 카메라는 높은 해상도를 낮은 프레임 속도에서만 제공합니다.
is in use by another process카메라를 열어 둔 다른 프로그램을 종료하세요.
sent no usable MJPEG frame카메라가 Neat가 디코딩할 수 없는 JPEG(예: 4:4:4 샘플링)를 보내거나 프레임을 전혀 보내지 않습니다.
is no longer /dev/videoN그래프를 만든 뒤 카메라를 뽑았거나 옮겼거나 번호가 바뀌었습니다. UsbCameraInput을 다시 호출하세요.
is not at '/dev/videoN' as its UsbCameraInfo saysusb_camera_info를 호출한 뒤 카메라를 뽑았거나 옮겼거나 번호가 바뀌었거나, 정보가 다른 카메라의 것입니다. usb_camera_info를 다시 호출하고 새 정보를 전달하세요.