Python从视频提取大量帧内存占用过高,求高效优化方案
Hey there! Let's sort out that memory overload you're hitting while pulling frames from your videos. The root cause here is super clear: your current code loads every frame into a single list in memory all at once. Even 3000 frames can add up quickly—each frame is a multi-channel array (like 3 channels for RGB), and storing all of them together eats up RAM fast.
Here are a few efficient fixes to get this under control:
1. Process Frames On-the-Fly (Don't Store All in Memory)
Instead of hoarding all frames in a list, process or save each frame immediately as you read it. This keeps your memory footprint tiny because you only hold one frame in RAM at a time.
Example: Save Frames as Image Files
This is great if you want to work with individual image files later:
import cv2 import os video_name1 = "videosDataset/AMAZExNHORMS2019_Lo-res.mp4" video_name2 = "videosDataset/CAMILLATHULINS2019_Lo-res.mp4" def extract_and_save_frames(video_path, output_dir, start_ratio=0.5, end_ratio=2/3): # Create output folder if it doesn't exist os.makedirs(output_dir, exist_ok=True) cap = cv2.VideoCapture(video_path) total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) start_frame = int(total_frames * start_ratio) end_frame = int(total_frames * end_ratio) print(f"Total video frames: {total_frames}") print(f"Extracting frames {start_frame} to {end_frame}") current_frame = 0 saved_count = 0 while cap.isOpened(): ret, frame = cap.read() if not ret: break # Skip frames before our target range if current_frame < start_frame: current_frame += 1 continue # Stop once we pass the end of our range if current_frame > end_frame: break # Save frame as JPG (adjust format/quality as needed) frame_filename = os.path.join(output_dir, f"frame_{saved_count:04d}.jpg") # Optional: Compress JPG to save space (quality 0-100) cv2.imwrite(frame_filename, frame, [cv2.IMWRITE_JPEG_QUALITY, 85]) saved_count += 1 current_frame += 1 # Print progress every 100 frames to avoid clutter if saved_count % 100 == 0: progress = (saved_count / (end_frame - start_frame)) * 100 print(f"Progress: {round(progress, 2)}%") cap.release() print(f"Done! Extracted {saved_count} frames to {output_dir}") # Extract frames from both videos extract_and_save_frames(video_name1, "extracted_frames/video1") extract_and_save_frames(video_name2, "extracted_frames/video2")
Example: Append Frames to Pickle (Without Loading All)
If you still want to use pickle for your dataset, append each frame to the pickle file incrementally instead of storing all in a list first:
import cv2 import pickle video_name1 = "videosDataset/AMAZExNHORMS2019_Lo-res.mp4" def save_frames_to_pickle(video_path, pickle_path, start_ratio=0.5, end_ratio=2/3): cap = cv2.VideoCapture(video_path) total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) start_frame = int(total_frames * start_ratio) end_frame = int(total_frames * end_ratio) current_frame = 0 # Open pickle file in append mode to add frames one by one with open(pickle_path, 'ab') as pickle_file: while cap.isOpened(): ret, frame = cap.read() if not ret: break if current_frame < start_frame: current_frame += 1 continue if current_frame > end_frame: break # Dump single frame to pickle pickle.dump(frame, pickle_file) current_frame += 1 progress = (current_frame - start_frame) / (end_frame - start_frame) * 100 print(f"Progress: {round(progress, 2)}%") cap.release() print(f"Frames saved to {pickle_path}") # Usage save_frames_to_pickle(video_name1, "frames1.pkl")
2. Stop Using cap.set() Repeatedly
Your original code uses cap.set(1, i) to jump to each frame, which is surprisingly inefficient. OpenCV has to seek through the video for every frame, which slows things down and can cause unexpected behavior. The examples above read frames sequentially instead—just skip the ones you don't need, which is way faster and more reliable.
Extra Optimization Tips
- Resize frames: If your computer vision task doesn't need full-resolution frames, shrink them before saving with
frame = cv2.resize(frame, (640, 480))(adjust dimensions to your needs). This cuts down memory and storage drastically. - Use efficient formats: JPG is lighter than PNG for most cases. If you need lossless storage, consider TIFF or adjust PNG compression.
- Batch processing (if needed): If you must process multiple frames at once, use small batches instead of loading everything—this balances memory usage and processing speed.
内容的提问来源于stack exchange,提问作者Otor

