Python, C++에서의 OpenCV의 FPS 차이

2024. 12. 20. 18:55Memorizing/Qt

문제 상황

Python, PyQT에서 dlib을 사용해서 얼굴 인식을 했을 때, fps가 30 정도 나오는데, C++, QT에서 dlib을 사용해서 얼굴 인식을 진행하면 fps가 5-6정도 나오는 문제가 있었음. 분명 동일한 코드인데?

그래서 우선 dlib을 사용하지 않은 경우 fps를 측정해보았다.

Python, PyQT에서 fps를 측정한 경우
C++, QT에서 fps를 측정한 경우

문제를 간단하게 생각하기 위해 아래와 같은 코드를 작성하였다.

#include <opencv2/opencv.hpp>
#include <iostream>
#include <chrono>

int main() {
    // 화면 캡쳐를 위한 VideoCapture 객체
    cv::VideoCapture cap(0);  // 0은 기본 카메라 장치, 화면 캡처 시에는 다른 방법이 필요할 수 있습니다

    if (!cap.isOpened()) {
        std::cerr << "카메라를 열 수 없습니다!" << std::endl;
        return -1;
    }

    // FPS 측정을 위한 변수 설정
    auto lastTime = std::chrono::high_resolution_clock::now();
    int frameCount = 0;

    // 화면을 캡쳐하고, FPS를 측정
    while (true) {
        cv::Mat frame;
        cap >> frame;  // 화면 캡쳐
        cv::resize(frame, frame, cv::Size(320, 240));

        if (frame.empty()) {
            std::cerr << "프레임을 읽을 수 없습니다!" << std::endl;
            break;
        }

        frameCount++;

        // FPS 계산
        auto currentTime = std::chrono::high_resolution_clock::now();
        std::chrono::duration<double> elapsed = currentTime - lastTime;
        if (elapsed.count() >= 1.0) {
            double fps = frameCount / elapsed.count();
            std::cout << "FPS: " << fps << std::endl;
            frameCount = 0;
            lastTime = currentTime;
        }

        // 화면에 캡쳐된 이미지 표시
        cv::imshow("Screen Capture", frame);

        // 키 입력이 'q'이면 종료
        if (cv::waitKey(1) == 'q') {
            break;
        }
    }

    // 캡쳐 종료 후 리소스 해제
    cap.release();
    cv::destroyAllWindows();

    return 0;
}
import cv2
import time

# 화면 캡쳐를 위한 VideoCapture 객체 (0은 기본 카메라)
cap = cv2.VideoCapture(0)

if not cap.isOpened():
    print("카메라를 열 수 없습니다!")
    exit()

# FPS 측정을 위한 변수 설정
frame_count = 0
last_time = time.time()

while True:
    ret, frame = cap.read()

    if not ret:
        print("프레임을 읽을 수 없습니다!")
        break

    frame_count += 1

    # FPS 계산
    current_time = time.time()
    elapsed_time = current_time - last_time

    if elapsed_time >= 1.0:
        fps = frame_count / elapsed_time
        print(f"FPS: {fps:.2f}")
        frame_count = 0
        last_time = current_time

    # 화면에 캡쳐된 이미지 표시
    cv2.imshow("Screen Capture", frame)

    # 'q'를 누르면 종료
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# 캡쳐 종료 후 리소스 해제
cap.release()
cv2.destroyAllWindows()

위와 같이 작성했을 때 동일한 문제가 발생한다는 것을 발견했다. 캡쳐를 해올 때, 너무 큰 이미지를 가져와서 프로그램이 늦어지는 건가 싶어서 캡쳐해오는 이미지의 크기를 아래과 같이 변경해주었다.

int main() {
    // 화면 캡쳐를 위한 VideoCapture 객체
    cv::VideoCapture cap(0);  // 0은 기본 카메라 장치, 화면 캡처 시에는 다른 방법이 필요할 수 있습니다

    if (!cap.isOpened()) {
        std::cerr << "카메라를 열 수 없습니다!" << std::endl;
        return -1;
    }

    int frameWidth = 320;
    int frameHeight = 240;
    cap.set(cv::CAP_PROP_FRAME_WIDTH, frameWidth);
    cap.set(cv::CAP_PROP_FRAME_HEIGHT, frameHeight);
    
    ...

캡쳐를 해올 때, 이미지를 작은 사이즈로 고정해오니 fps가 정상적으로 돌아온다는 것을 확인했다.

하지만 의문은 왜 파이썬은 적당한 크기로 잘 읽어오느냐는 문제..

놀라운 사실을 발견했는데, 기본적으로 파이썬에서 OpenCV를 사용하는 경우, width는 640으로 height는 480으로 읽어온다.. 아래의 코드로 확인 가능했다.

cap = cv2.VideoCapture(0)

if not cap.isOpened():
    print("카메라를 열 수 없습니다!")
    exit()

# FPS 측정을 위한 변수 설정
frame_count = 0
last_time = time.time()

while True:
    ret, frame = cap.read()
    print(f"width: {cap.get(cv2.CAP_PROP_FRAME_WIDTH)}, height: {cap.get(cv2.CAP_PROP_FRAME_HEIGHT)}")

결론

C++, OpenCV에서 프레임을 얻어오면, 따로 설정해주지 않으면 카메라의 해상도 그대로 읽어오는데, Python, OpenCV에서 프레임을 얻어오면 따로 설정해주지 않아도 기본적으로 640, 480 해상도로 읽어온다!!

추가적으로 리눅스에서 카메라의 해상도를 확인할 수 있는데, 이때, 사용할 수 있는 FPS도 같이 알려준다.

$ v4l2-ctl --device=/dev/video0 --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
        Type: Video Capture

        [0]: 'MJPG' (Motion-JPEG, compressed)
                Size: Discrete 1920x1080
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 320x240
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 640x360
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 1280x720
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 1280x800
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 640x480
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
        [1]: 'YUYV' (YUYV 4:2:2)
                Size: Discrete 1920x1080
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 320x240
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                Size: Discrete 640x360
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                Size: Discrete 1280x720
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 1280x800
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 640x480
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)

그리고 파이썬 기준 아래의 코드로 어떤 포맷으로 읽어오는지 확인할 수 있는데 나의 경우 16이 나왔는데, 16은 YUYV 포맷이다.

    camera_format = cap.get(cv2.CAP_PROP_FORMAT)
    print(f"format: {camera_format}")