공부/A.I
[OpenCV] Frame latency 문제
래울
2024. 8. 17. 17:09
OpenCV로 카메라의 영상을 녹화할 때 녹화된 영상의 재생 속도가 맞지 않는 경우가 종종 있다.
cv2.VideoWriter로 영상 생성 시, 프레임 지연으로 인해 적절한 속도로 프레임을 write하지 못하기 때문일 수 있다.
책상 옆에 굴러다니던 라이언...
아래 예시 코드를 보면
카메라의 fps는 30이고 30fps로 생성된 VideoWriter에 frame을 write한다.
하지만 추론모델을 거치기 때문에 아래와 같이 각 프레임에 지연이 발생한다.
즉, 아래 그림과 같이 VideoWriter에 쌓이게 된다.
즉 실제로는 4~5fps만 write하고 있지만, VideoWriter를 30fps로 생성하게 되면 7초 이상이 지나야 1초의 영상이 생성된다.
따라서 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()