ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [OpenCV] Frame latency 문제
    공부/A.I 2024. 8. 17. 17:09

    OpenCV로 카메라의 영상을 녹화할 때 녹화된 영상의 재생 속도가 맞지 않는 경우가 종종 있다.

    cv2.VideoWriter로 영상 생성 시, 프레임 지연으로 인해 적절한 속도로 프레임을 write하지 못하기 때문일 수 있다.

     

    책상 옆에 굴러다니던 라이언...

    before(fps30)

     

     

    아래 예시 코드를 보면

    카메라의 fps는 30이고 30fps로 생성된 VideoWriter에 frame을 write한다.

    하지만 추론모델을 거치기 때문에 아래와 같이 각 프레임에 지연이 발생한다.

    약 0.2 ~ 정도의 지연

     

    즉, 아래 그림과 같이  VideoWriter에 쌓이게 된다.

    30fps -> 4fps

     

     

    즉 실제로는 4~5fps만 write하고 있지만, VideoWriter를 30fps로 생성하게 되면 7초 이상이 지나야 1초의 영상이 생성된다.

    6s -> 1s

     

    따라서 frame latency를 고려한 VideoWriter의 fps를 주게되면 정상적인 속도의 영상을 얻을 수 있다.

     


    코드1

    더보기

    테스트 코드(before)

    from ultralytics import YOLO
    import cv2
    import datetime
    
    # opencv draw 변수들 정의
    red = (0, 0, 255)
    green = (0, 255, 0)
    width = 640
    height = 480
    
    def create_video_writer(video_cap, output_filename, record_fps):
        frame_width = int(video_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        frame_height = int(video_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    	fps = int(video_cap.get(cv2.CAP_PROP_FPS))
        fourcc = cv2.VideoWriter_fourcc(*'m', 'p', '4', 'v')
        return cv2.VideoWriter(output_filename, fourcc, fps, (frame_width, frame_height))
    
    ########### main ##########
    # Load the YOLOv8 model
    model = YOLO(".venv/yolov8m-pose.pt")
    
    #width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    #height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    #cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) # 가로
    #cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height
    
    cap = cv2.VideoCapture(0)
    name = datetime.datetime.now().strftime("%y%m%d_%H%M%S") + "_output"
    writer = create_video_writer(cap, "./" + name + ".mp4", 4)
    
    program_start_sec = datetime.datetime.now()
    sec_sum = 0
    sec_cnt = 0
    is_first_frame = True
    
    while cap.isOpened():
        # Read a frame from the video
        predict_start_sec = datetime.datetime.now()
        success, frame = cap.read()
    
        if success:
            # Run YOLOv8 inference on the frame
            results = model(frame, verbose=False, conf=0.6)
    
            annotated_frame = results[0].plot()
            # Display the annotated frame
    
            cv2.imshow("show", annotated_frame)
            writer.write(annotated_frame)
            if is_first_frame:
                program_start_sec = datetime.datetime.now()
                is_first_frame = False
                continue
    
            # latency 계산, 추론 지연 시간 계산
            predict_end_sec = datetime.datetime.now()
            predict_sec_total = (predict_end_sec - predict_start_sec).total_seconds()
            sec_cnt += 1
            sec_sum += predict_sec_total
            print(f'time to predict 1 frame: {predict_sec_total * 1000: .0f} milliseconds')
    
            if cv2.waitKey(1) & 0xFF == ord("q"):
                break
            program_end_sec = datetime.datetime.now()
        else:
            break
    
    program_sec_total = (program_end_sec - program_start_sec).total_seconds()
    writer.release()
    print(f'mp4 play time: {format(program_sec_total, "10.2f")} seconds')
    print(f'result: {sec_sum / sec_cnt * 1000: .0f} milliseconds')
    # Release the video capture object and close the display window
    cap.release()
    cv2.destroyAllWindows()

     

    코드2

    더보기

    코드(after)

    from ultralytics import YOLO
    import cv2
    import datetime
    
    # opencv draw 변수들 정의
    red = (0, 0, 255)
    green = (0, 255, 0)
    width = 640
    height = 480
    
    def create_video_writer(video_cap, output_filename, record_fps):
        frame_width = int(video_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        frame_height = int(video_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        fourcc = cv2.VideoWriter_fourcc(*'m', 'p', '4', 'v')
        return cv2.VideoWriter(output_filename, fourcc, record_fps, (frame_width, frame_height))
    
    ########### main ##########
    # Load the YOLOv8 model
    model = YOLO(".venv/yolov8m-pose.pt")
    
    #width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    #height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    #cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) # 가로
    #cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height
    
    cap = cv2.VideoCapture(0)
    name = datetime.datetime.now().strftime("%y%m%d_%H%M%S") + "_output"
    writer = create_video_writer(cap, "./" + name + ".mp4", 4)
    
    program_start_sec = datetime.datetime.now()
    sec_sum = 0
    sec_cnt = 0
    is_first_frame = True
    
    while cap.isOpened():
        # Read a frame from the video
        predict_start_sec = datetime.datetime.now()
        success, frame = cap.read()
    
        if success:
            # Run YOLOv8 inference on the frame
            results = model(frame, verbose=False, conf=0.6)
    
            annotated_frame = results[0].plot()
            # Display the annotated frame
    
            cv2.imshow("show", annotated_frame)
            writer.write(annotated_frame)
            if is_first_frame:
                program_start_sec = datetime.datetime.now()
                is_first_frame = False
                continue
    
            # latency 계산, 추론 지연 시간 계산
            predict_end_sec = datetime.datetime.now()
            predict_sec_total = (predict_end_sec - predict_start_sec).total_seconds()
            sec_cnt += 1
            sec_sum += predict_sec_total
            print(f'time to predict 1 frame: {predict_sec_total * 1000: .0f} milliseconds')
    
            if cv2.waitKey(1) & 0xFF == ord("q"):
                break
            program_end_sec = datetime.datetime.now()
        else:
            break
    
    program_sec_total = (program_end_sec - program_start_sec).total_seconds()
    writer.release()
    print(f'mp4 play time: {format(program_sec_total, "10.2f")} seconds')
    print(f'result: {sec_sum / sec_cnt * 1000: .0f} milliseconds')
    # Release the video capture object and close the display window
    cap.release()
    cv2.destroyAllWindows()

     

Designed by Tistory.