python按照指定时间范围裁剪视频

按照指定时间裁剪视频通常有如下三种方法:

但对于从监控等视频流保存的视频文件来说,可能存在文件的起始时间戳不准确的情况。即第一帧视频数据对应的时间戳不是 00:00:00 。这种情况下,上述方法的前两种将无法正确处理(第三种方法未尝试)。因此,我采用了一种更笨(速度更慢,但更直观)的做法。即逐帧读取视频,通过判断各帧与第一帧的相对时间来确定是否保留当前帧,由此规避了首帧时间戳错误的问题。程序片段记录如下:

  1. import cv2
  2.  
  3. def clip_video_with_opencv(src_path:str, dst_path:str, start_time:str, stop_time:str):
  4. """ Clip the video with OpenCV for the specified time period (Force the start timestamp of the video to `00:00:00`)
  5.  
  6. This function is time consuming.
  7. If the start timestamp of the video is correct, you can use the `cap.set()` function to jump to the
  8. start time of the target clip directly, or use the `moviepy` module instead.
  9.  
  10. Args:
  11. `src_path` (str): Source video path. The path of the video to be clipped
  12. `dst_path` (str): Destination video path. Storage path of the clipped video
  13. `start_time` (str): Time to start clipping. The format is `hh:mm:ss`
  14. `stop_time` (str): Time to stop clipping. `None` if the stop time is the end of the video. The format is `hh:mm:ss`
  15. """
  16.  
  17. th, tm, ts = map(int, start_time.split(":"))
  18. start_time = th*3600 + tm*60 + ts # Seconds
  19. if stop_time:
  20. th, tm, ts = map(int, stop_time.split(":"))
  21. stop_time = th*3600 + tm*60 + ts # Seconds
  22.  
  23. ### Prepare to load video data
  24. in_cap = cv2.VideoCapture(src_path)
  25. frame_total = int(in_cap.get(cv2.CAP_PROP_FRAME_COUNT))
  26. fps = int(in_cap.get(cv2.CAP_PROP_FPS))
  27. # fourcc_int = int(in_cap.get(cv2.CAP_PROP_FOURCC))
  28. frame_width, frame_height = int(in_cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(in_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
  29.  
  30. ### Prepare to save video clip
  31. # fourcc_str = "{}{}{}{}".format(chr(fourcc_int&255), chr((fourcc_int>>8)&255), chr((fourcc_int>>16)&255), chr((fourcc_int>>24)&255))
  32. # fourcc = cv2.VideoWriter_fourcc(*fourcc_str)
  33. fourcc = cv2.VideoWriter_fourcc(*'mp4v')
  34. out_cap = cv2.VideoWriter(dst_path, fourcc, fps, (frame_width, frame_height))
  35.  
  36. ### Frame-by-frame processing
  37. frame_idx = 0
  38. start_ts = None
  39. while in_cap.isOpened():
  40. success = in_cap.grab() # !!! Load a frame of data but do not decode it (decoding is time consuming)
  41. if not success:
  42. break
  43. video_ts = int(round(int(in_cap.get(cv2.CAP_PROP_POS_MSEC))/1000)) # Seconds
  44. if start_ts is None:
  45. start_ts = video_ts
  46. video_ts -= start_ts # Force the start timestamp of the video to 00:00:00
  47. th, tm, ts = video_ts//3600, (video_ts%3600)//60, video_ts%3600%60
  48. if video_ts < start_time: # Skip the video frames before the target clip
  49. print("{}/{}: {:0>2d}:{:0>2d}:{:0>2d} [skip]".format(frame_idx+1, frame_total, th, tm, ts), end="\r")
  50. elif video_ts >= start_time and (stop_time is None or video_ts <= stop_time): # Save video frames of the target clip
  51. print("{}/{}: {:0>2d}:{:0>2d}:{:0>2d} [write]".format(frame_idx+1, frame_total, th, tm, ts), end="\r")
  52. success, frame = in_cap.retrieve() # !!! Decode the frame data. Only the video frames of the target clip are decoded
  53. if not success:
  54. break
  55. out_cap.write(frame)
  56. else:
  57. break
  58. frame_idx += 1
  59. in_cap.release()
  60. out_cap.release()
  61. print("{}/{}: {:0>2d}:{:0>2d}:{:0>2d} [done]".format(frame_idx, frame_total, th, tm, ts))

留下评论

您的邮箱地址不会被公开。 必填项已用 * 标注