C++/OpenCV 이용한 카메라 연결

대상: C++/OpenCV로 웹캠(USB 카메라)을 열어서 영상 스트림을 띄워보고 싶은 사람
환경: Ubuntu 20.04 / 22.04, OpenCV 4.x, CMake 기반 C++ 프로젝트 (g++ 사용)


1. 문제/주제 요약

  • USB 카메라(웹캠)를 C++에서 열어서
    1. 카메라 장치를 열고
    2. 프레임을 받아서
    3. OpenCV imshow()로 화면에 표시하는
  • 최소 예제 + 빌드 방법까지 한 번에 정리한 글입니다.

“카메라 잘 붙어 있는지 확인”용으로 쓰기 좋은 코드라고 보면 됩니다.


2. 원인/배경 설명

실무에서 카메라를 쓸 때 보통 이런 흐름으로 갑니다.

  1. 먼저 OS 레벨에서 장치 인식 (/dev/video0 등)
  2. 라이브러리(OpenCV)의 VideoCapture로 카메라 장치 열기
  3. 프레임 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() 호출 여부 등 실무에서 자주 막히는 포인트도
    함께 정리했으니, 카메라가 안 뜰 때 이 글의 체크리스트를 한 번씩 확인해 보면 좋습니다.

댓글 남기기