python视频拆帧并根据所选区域保存指定区域

import cv2
import os

# ----------- 区域选择部分(修复版)------------
roi_coordinates = {'x1':0, 'y1':0, 'x2':0, 'y2':0}
drawing = False

def select_region(event, x, y, flags, param):
    global roi_coordinates, drawing
    if event == cv2.EVENT_LBUTTONDOWN:
        drawing = True
        roi_coordinates = {'x1': x, 'y1': y, 'x2': x, 'y2': y}
    elif event == cv2.EVENT_MOUSEMOVE:
        if drawing:
            roi_coordinates['x2'] = x
            roi_coordinates['y2'] = y
    elif event == cv2.EVENT_LBUTTONUP:
        drawing = False
        roi_coordinates['x2'] = x
        roi_coordinates['y2'] = y

def preview_and_select(video_path, frame_num=0):
    global roi_coordinates, drawing
    roi_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) & 0xFF
        
        if key == ord('r'):
            clone = frame.copy()
            roi_coordinates = {'x1':0, 'y1':0, 'x2':0, 'y2':0}
        elif key == ord('q'):
            break
        elif key == ord('d'):
            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}
        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 = region
    
    if (x + w > total_width) or (y + h > total_height):
        cap.release()
        raise ValueError(f"选择区域超出视频尺寸(视频分辨率:{total_width}x{total_height})")
    
    frame_count = 0
    saved_count = 0
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    
    try:
        while True:
            ret, frame = cap.read()
            if not ret:
                break
            
            if frame_count % interval == 0:
                roi = frame[y:y+h, x:x+w]
                if roi.size == 0:  # 空图像检查
                    continue
                
                output_path = os.path.join(output_dir, f"frame_{saved_count:05d}.jpg")
                cv2.imwrite(output_path, roi, [cv2.IMWRITE_JPEG_QUALITY, 95])
                saved_count += 1
            
            frame_count += 1
            if 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)}")

使用流程:

  1. 修改video_path为实际视频路径

  2. 运行后会弹出预览窗口:

    • 鼠标拖拽选择区域

    • 按Q确认选择

    • 按R重置选择

    • 按A/D前后跳转10帧

  3. 选择完成后自动执行拆帧操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值