대상: C++/OpenCV로 웹캠(USB 카메라)을 열어서 영상 스트림을 띄워보고 싶은 사람
환경: Ubuntu 20.04 / 22.04, OpenCV 4.x, CMake 기반 C++ 프로젝트 (g++ 사용)
1. 문제/주제 요약
- USB 카메라(웹캠)를 C++에서 열어서
- 카메라 장치를 열고
- 프레임을 받아서
- OpenCV
imshow()로 화면에 표시하는
- 최소 예제 + 빌드 방법까지 한 번에 정리한 글입니다.
“카메라 잘 붙어 있는지 확인”용으로 쓰기 좋은 코드라고 보면 됩니다.
2. 원인/배경 설명
실무에서 카메라를 쓸 때 보통 이런 흐름으로 갑니다.
- 먼저 OS 레벨에서 장치 인식 (
/dev/video0등) - 라이브러리(OpenCV)의 VideoCapture로 카메라 장치 열기
- 프레임 grab → 화면 표시 (또는 파일/네트워크 전송 등)
여기서 가장 많이 막히는 포인트는:
- OpenCV가 카메라 장치를 못 찾거나(index 잘못 선택)
- 권한 문제로
/dev/video0에 접근이 안 되거나 - 프레임 루프에서
imshow()관련 이벤트 처리를 잘못해서 화면이 멈추는 경우
이 글에서는 가장 기본적인 “USB 카메라 1개 + 디폴트 설정” 상황을 기준으로
처음부터 끝까지 동작하는 예제를 만듭니다.
3. 해결 순서 (설치 → 코드 → 빌드 → 실행)
3-1. OpenCV 설치 (Ubuntu)
패키지 버전으로 충분하다면:
sudo apt update
sudo apt install -y libopencv-dev
설치 확인:
pkg-config --modversion opencv4
버전이 출력되면 설치 OK.
3-2. 카메라 장치 확인
먼저 OS에서 카메라가 잘 잡히는지 확인합니다.
ls /dev/video*
일반적으로 첫 번째 웹캠은 /dev/video0 입니다.
여러 개가 있다면 /dev/video1, /dev/video2 … 가 붙습니다.
테스트로 v4l2-ctl을 써보고 싶다면:
sudo apt install -y v4l-utils
v4l2-ctl --list-devices
v4l2-ctl --list-formats-ext -d /dev/video0
3-3. C++ 예제 코드 작성
main.cpp 파일을 하나 만들고 다음 내용을 넣습니다.
#include <iostream>
#include <opencv2/opencv.hpp>
int main(int argc, char** argv)
{
// 1. 카메라 인덱스 선택 (기본: 0)
int camera_index = 0;
if (argc > 1) {
camera_index = std::stoi(argv[1]);
}
// 2. VideoCapture 객체 생성 및 카메라 열기
cv::VideoCapture cap(camera_index);
if (!cap.isOpened()) {
std::cerr << "Error: Cannot open camera with index " << camera_index << std::endl;
return -1;
}
// (선택) 캡처 해상도 설정
cap.set(cv::CAP_PROP_FRAME_WIDTH, 1280);
cap.set(cv::CAP_PROP_FRAME_HEIGHT, 720);
std::cout << "Camera " << camera_index << " opened successfully." << std::endl;
// 3. 프레임 읽어서 화면 표시
const std::string window_name = "Camera Preview";
cv::namedWindow(window_name, cv::WINDOW_AUTOSIZE);
while (true) {
cv::Mat frame;
bool ret = cap.read(frame); // 또는 cap >> frame;
if (!ret || frame.empty()) {
std::cerr << "Warning: Failed to grab frame." << std::endl;
break;
}
// 4. 화면에 표시
cv::imshow(window_name, frame);
// 5. 키 입력 대기 (1ms) - 'q' 키 누르면 종료
char key = static_cast<char>(cv::waitKey(1));
if (key == 'q' || key == 27) { // 'q' 또는 ESC
std::cout << "Exit key pressed." << std::endl;
break;
}
}
// 6. 리소스 해제
cap.release();
cv::destroyAllWindows();
return 0;
}
코드 설명 요약
cv::VideoCapture cap(camera_index);camera_index = 0→/dev/video0에 연결되는 경우가 대부분
cap.isOpened()로 카메라 열기 성공 여부 확인cap.read(frame)또는cap >> frame으로 프레임 읽기cv::imshow()+cv::waitKey(1)로 매 프레임 화면 갱신q또는ESC누르면 루프 탈출 → 깨끗하게 종료
3-4. CMakeLists.txt 작성
OpenCV + CMake 조합으로 빌드할 수 있도록 CMakeLists.txt를 만듭니다.
cmake_minimum_required(VERSION 3.10)
project(opencv_camera_example)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(OpenCV REQUIRED)
add_executable(opencv_camera_example main.cpp)
target_include_directories(opencv_camera_example PRIVATE ${OpenCV_INCLUDE_DIRS})
target_link_libraries(opencv_camera_example PRIVATE ${OpenCV_LIBS})
프로젝트 구조 예시:
opencv_camera_example/
├── CMakeLists.txt
└── main.cpp
3-5. 빌드 & 실행
빌드 디렉토리를 만들어서 CMake 빌드:
cd opencv_camera_example
mkdir build && cd build
cmake ..
make -j$(nproc)
빌드가 성공하면 opencv_camera_example 실행 파일이 생깁니다.
./opencv_camera_example
- 기본적으로 카메라 인덱스 0 을 엽니다.
q또는ESC누르면 프로그램 종료.
여러 카메라가 있을 경우, 인자를 줘서 다른 인덱스를 열 수 있습니다.
./opencv_camera_example 1 # /dev/video1 열기 (환경에 따라 다름)
4. 추가 팁 / 자주 하는 실수
4-1. /dev/video0 권한 문제
실행했을 때 아래와 비슷하게 나오는 경우:
Error: Cannot open camera with index 0- 또는
Permission denied관련 메시지
대부분 권한 문제일 수 있습니다.
ls -l /dev/video0
여기서 owner/group, 권한을 보고, 일반 사용자가 접근 가능하도록video 그룹에 본인을 추가해주는 식으로 해결할 수 있습니다.
sudo usermod -aG video $USER
# 로그아웃 후 다시 로그인 필요
테스트용으로는 아래처럼 한 번만 권한을 풀어서 확인할 수도 있습니다.
sudo chmod a+rw /dev/video0
(실제 서비스 환경에는 추천하지 않음)
4-2. waitKey()를 빼먹으면?
imshow()만 호출하고 cv::waitKey()를 호출하지 않으면,
- 창이 안 뜨거나
- 뜨더라도 화면이 갱신되지 않고 멈춰 보이는
현상이 생깁니다.
항상 루프에서 waitKey()를 호출해야 이벤트(화면 갱신, 키 입력)가 처리됩니다.
실시간 카메라 프리뷰라면 보통 waitKey(1) 또는 waitKey(10) 정도면 충분합니다.
4-3. 해상도/프레임 설정이 안 먹는 것처럼 보일 때
cap.set(cv::CAP_PROP_FRAME_WIDTH, 1280);
cap.set(cv::CAP_PROP_FRAME_HEIGHT, 720);
처럼 해상도를 설정했는데도 실제로는 다른 해상도로 들어오는 경우가 있습니다.
이는 카메라 장치가 해당 해상도를 지원하지 않거나, 드라이버에서 조정했기 때문입니다.
실제로 반영된 값을 확인해보고 싶다면:
double w = cap.get(cv::CAP_PROP_FRAME_WIDTH);
double h = cap.get(cv::CAP_PROP_FRAME_HEIGHT);
std::cout << "Actual resolution: " << w << " x " << h << std::endl;
처럼 출력해서 확인하면 됩니다.
4-4. 프레임 지연 / CPU 사용량
waitKey(1)를 너무 작게 주면 CPU 사용률이 올라갈 수 있습니다.- 필요에 따라
waitKey(10)혹은 30fps이면waitKey(33)로 조절해도 됩니다.
또는,
- 프레임 처리(예: 딥러닝 추론)를 같이 할 경우, 처리 시간을 고려해서
루프 설계를 해야 합니다. (여기서는 단순 프리뷰만 다룸)
4-5. ROS/ROS2와 같이 쓸 때
이 코드는 ROS(2) 카메라 노드 만들기 전, 장치 테스트용으로 쓰기 좋습니다.
- 먼저 이 예제로
/dev/videoX가 잘 열리는지 확인 - 이후에 ROS/ROS2 노드 안에서
cv::VideoCapture를 그대로 써도 되고,image_transport/sensor_msgs::Image변환을 추가해서 토픽으로 올려도 됩니다.
(ROS/ROS2 연계까지 자세히 보고 싶다면, 그걸 주제로 다시 요청해 주세요.)
5. 정리
- Ubuntu + C++ + OpenCV 환경에서 USB 카메라 열기 → 프레임 읽기 → 화면 표시까지
동작하는 최소 예제와 CMake 빌드 방법을 정리했습니다. - 핵심은
cv::VideoCapture로 카메라 인덱스를 열고, 루프에서read()+imshow()+waitKey()를
적절히 사용해주는 것입니다. /dev/video0권한, 해상도 지원 여부,waitKey()호출 여부 등 실무에서 자주 막히는 포인트도
함께 정리했으니, 카메라가 안 뜰 때 이 글의 체크리스트를 한 번씩 확인해 보면 좋습니다.