python视频拆帧并根据所选区域保存指定区域
import cv2
import os# ----------- 区域选择部分(修复版)------------
roi_coordinates = {'x1':0, 'y1':0, 'x2':0, 'y2':0}
drawing = Falsedef select_region(event, x, y, flags, param):global roi_coordinates, drawingif event == cv2.EVENT_LBUTTONDOWN:drawing = Trueroi_coordinates = {'x1': x, 'y1': y, 'x2': x, 'y2': y}elif event == cv2.EVENT_MOUSEMOVE:if drawing:roi_coordinates['x2'] = xroi_coordinates['y2'] = yelif event == cv2.EVENT_LBUTTONUP:drawing = Falseroi_coordinates['x2'] = xroi_coordinates['y2'] = ydef preview_and_select(video_path, frame_num=0):global roi_coordinates, drawingroi_coordinates = {'x1':0, 'y1':0, 'x2':0, 'y2':0} # 重置状态cap = cv2.VideoCapture(video_path)if not cap.isOpened():raise ValueError("视频打开失败,请检查路径或文件格式")cap.set(cv2.CAP_PROP_POS_FRAMES, frame_num)ret, frame = cap.read()if not ret:cap.release()raise ValueError("无法读取指定帧,可能超出视频范围")cv2.namedWindow('Select ROI (Q=确认 R=重置 A/D=跳帧)')cv2.setMouseCallback('Select ROI (Q=确认 R=重置 A/D=跳帧)', select_region)clone = frame.copy()while True:current_frame = clone.copy()if roi_coordinates['x1'] != roi_coordinates['x2'] or roi_coordinates['y1'] != roi_coordinates['y2']:x1 = min(roi_coordinates['x1'], roi_coordinates['x2'])y1 = min(roi_coordinates['y1'], roi_coordinates['y2'])x2 = max(roi_coordinates['x1'], roi_coordinates['x2'])y2 = max(roi_coordinates['y1'], roi_coordinates['y2'])cv2.rectangle(current_frame, (x1, y1), (x2, y2), (0, 255, 0), 2)cv2.putText(current_frame, f"X: {x1}-{x2}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,0), 2)cv2.putText(current_frame, f"Y: {y1}-{y2}", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,0), 2)cv2.imshow('Select ROI (Q=确认 R=重置 A/D=跳帧)', current_frame)key = cv2.waitKey(1) & 0xFFif key == ord('r'):clone = frame.copy()roi_coordinates = {'x1':0, 'y1':0, 'x2':0, 'y2':0}elif key == ord('q'):breakelif key == ord('d'):frame_num += 10cap.set(cv2.CAP_PROP_POS_FRAMES, frame_num)ret, frame = cap.read()if ret:clone = frame.copy()roi_coordinates = {'x1':0, 'y1':0, 'x2':0, 'y2':0}elif key == ord('a'):frame_num = max(0, frame_num-10)cap.set(cv2.CAP_PROP_POS_FRAMES, frame_num)ret, frame = cap.read()if ret:clone = frame.copy()roi_coordinates = {'x1':0, 'y1':0, 'x2':0, 'y2':0}cv2.destroyAllWindows()cap.release()# 计算有效区域x = min(roi_coordinates['x1'], roi_coordinates['x2'])y = min(roi_coordinates['y1'], roi_coordinates['y2'])w = abs(roi_coordinates['x1'] - roi_coordinates['x2'])h = abs(roi_coordinates['y1'] - roi_coordinates['y2'])return (x, y, w, h) if w > 10 and h > 10 else None # 最小尺寸校验# ----------- 视频拆帧部分(增强版)------------
def crop_and_save_frames(video_path, output_dir, region, interval=1):"""增强功能:- 自动路径创建- 进度显示- 区域有效性验证- 错误处理"""if not os.path.exists(video_path):raise FileNotFoundError(f"视频文件不存在:{video_path}")os.makedirs(output_dir, exist_ok=True)cap = cv2.VideoCapture(video_path)if not cap.isOpened():raise IOError("无法打开视频文件,可能是不支持的格式")# 验证区域有效性total_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))total_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))x, y, w, h = regionif (x + w > total_width) or (y + h > total_height):cap.release()raise ValueError(f"选择区域超出视频尺寸(视频分辨率:{total_width}x{total_height})")frame_count = 0saved_count = 0total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))try:while True:ret, frame = cap.read()if not ret:breakif frame_count % interval == 0:roi = frame[y:y+h, x:x+w]if roi.size == 0: # 空图像检查continueoutput_path = os.path.join(output_dir, f"frame_{saved_count:05d}.jpg")cv2.imwrite(output_path, roi, [cv2.IMWRITE_JPEG_QUALITY, 95])saved_count += 1frame_count += 1if frame_count % 10 == 0:print(f"\r处理进度: {frame_count}/{total_frames} ({frame_count/total_frames:.1%})", end='')print(f"\n完成!共保存 {saved_count} 张图片到:{os.path.abspath(output_dir)}")finally:cap.release()# ----------- 完整使用示例 ------------
if __name__ == "__main__":# 配置参数video_path = r"E:\photo\video\1.mp4" # 替换为实际视频路径output_folder = r"E:\photo\video\1" # 输出目录preview_frame = 50 # 预览起始帧号frame_interval = 30 # 抽帧间隔(每30帧抽1帧)# 步骤1:选择区域selected_region = preview_and_select(video_path, preview_frame)if not selected_region:print("未选择有效区域,程序终止")exit()print(f"已选择区域:{selected_region}")# 步骤2:执行拆帧try:crop_and_save_frames(video_path=video_path,output_dir=output_folder,region=selected_region,interval=frame_interval)except Exception as e:print(f"处理出错:{str(e)}")
使用流程:
-
修改
video_path
为实际视频路径 -
运行后会弹出预览窗口:
-
鼠标拖拽选择区域
-
按Q确认选择
-
按R重置选择
-
按A/D前后跳转10帧
-
-
选择完成后自动执行拆帧操作