وبلاگ فربد | Farbod Blog

توی این وبلاگ راجع به پروژه هام صحبت می کنم

استخراج کردن frame های ویدیو با پایتون

  • ۱۰۷۳

سلام دوستان :)

توی پست امروز قراره با هم نحوه ساخت یک اسکریپت پایتونی که فریم های ویدیو رو استخراج می کنه ببینیم.

برای این کار ما قراره از دو روش استفاده کنیم:

  1. استخراج کردن فریم ها با استفاده از opencv.
  2. استخراج کردن فریم ها با استفاده از moviepy.

پس قبل از اینکه بخوایم هر کاری انجام بدیم بریم کتابخونه هایی که نیاز داریم رو نصب کنیم.

برای نصب کتابخونه ها در ویندوز وارد cmd بشین و دستور زیر رو وارد کنین:

pip install opencv-python moviepy numpy

برای نصب کتابخونه ها در لینوکس یا مک هم وارد terminal بشین و دستور زیر رو وارد کنین:

sudo pip3 install opencv-python moviepy numpy

خب حالا که همه چی آمادست بریم کارمون رو شروع کنیم :)

 

1-روش اول: استخراج کردن frame های ویدیو با استفاده از opencv:

خب همونطور که میدونین  opencv یک کتابخونه بسیار گسترده هست که کار های خیلی زیادی میشه باهاش انجام داد. اما ما توی این پست از بخش کوچیکی از این کتابخونه استفاده می کنیم.

برای شروع کار یک سورس پایتون درست کنین و کد های زیر رو داخلش قرار بدین:

from datetime import timedelta
import cv2
import numpy as np
import os

# i.e if video of duration 30 seconds, saves 10 frame per second = 300 frames saved in total
SAVING_FRAMES_PER_SECOND = 10

def format_timedelta(td):
    """Utility function to format timedelta objects in a cool way (e.g 00:00:20.05) 
    omitting microseconds and retaining milliseconds"""
    result = str(td)
    try:
        result, ms = result.split(".")
    except ValueError:
        return result + ".00".replace(":", "-")
    ms = int(ms)
    ms = round(ms / 1e4)
    return f"{result}.{ms:02}".replace(":", "-")


def get_saving_frames_durations(cap, saving_fps):
    """A function that returns the list of durations where to save the frames"""
    s = []
    # get the clip duration by dividing number of frames by the number of frames per second
    clip_duration = cap.get(cv2.CAP_PROP_FRAME_COUNT) / cap.get(cv2.CAP_PROP_FPS)
    # use np.arange() to make floating-point steps
    for i in np.arange(0, clip_duration, 1 / saving_fps):
        s.append(i)
    return s


def main(video_file):
    filename, _ = os.path.splitext(video_file)
    filename += "-opencv"
    # make a folder by the name of the video file
    if not os.path.isdir(filename):
        os.mkdir(filename)
    # read the video file    
    cap = cv2.VideoCapture(video_file)
    # get the FPS of the video
    fps = cap.get(cv2.CAP_PROP_FPS)
    # if the SAVING_FRAMES_PER_SECOND is above video FPS, then set it to FPS (as maximum)
    saving_frames_per_second = min(fps, SAVING_FRAMES_PER_SECOND)
    # get the list of duration spots to save
    saving_frames_durations = get_saving_frames_durations(cap, saving_frames_per_second)
    # start the loop
    count = 0
    while True:
        is_read, frame = cap.read()
        if not is_read:
            # break out of the loop if there are no frames to read
            break
        # get the duration by dividing the frame count by the FPS
        frame_duration = count / fps
        try:
            # get the earliest duration to save
            closest_duration = saving_frames_durations[0]
        except IndexError:
            # the list is empty, all duration frames were saved
            break
        if frame_duration >= closest_duration:
            # if closest duration is less than or equals the frame duration, 
            # then save the frame
            frame_duration_formatted = format_timedelta(timedelta(seconds=frame_duration))
            cv2.imwrite(os.path.join(filename, f"frame{frame_duration_formatted}.jpg"), frame) 
            # drop the duration spot from the list, since this duration spot is already saved
            try:
                saving_frames_durations.pop(0)
            except IndexError:
                pass
        # increment the frame count
        count += 1



if __name__ == "__main__":
    import sys
    video_file = sys.argv[1]
    main(video_file)

توضیح: خب اول کتابخونه های مورد نیازمون رو import کردیم،

بعد یک متغیر به نام SAVING_FRAMES_PER_SECOND تعریف کردیم که مشخص میکنه که در هر ثانیه چند تا فریم ذخیره کنه،شما میتونین این مقدار رو به مقدار دلخواه خودتون تغییر بدین.

بعدش یک تابع تعریف کردیم که خیلی نقش مهمی توی برناممون نداره و تنها کاری که انجام میده خوشگل کردن فرمت تایم هست :))

تابع بعدی هم یک لیست به ما بر می گردونه که هر فریم در زمان مشخص شده داخلش ذخیره شده.

خب حالا رسیدیم به تابع main که کار اصلی رو انجام میده و تمام فریم ها رو بیرون میکشه و تو یک فولدر با اسم خود ویدیو ذخیره میکنه.

خب برای اجرا وارد cmd یا terminal بشین و اول وارد مسیر ویدیوتون بشین و بعد دستور زیر رو وارد کنین:

python {name}.py {videoname}

بجای  name باید اسم اسکریپت پایتونتون رو وارد کنین و بجای videoname باید اسم ویدیویی که میخواین فریم هاشو استخراج کنین وارد کنین.

 

خب این روش opencv بود.

بریم سراغ روش دوم یعنی استخراج از طریق moviepy.

2-روش دوم: استخراج کردن frame های ویدیو با استفاده از moviepy:

خب moviepy هم یک کتابخونه مثل opencv هست اما با امکانات خیلی کمتر،ولی فعلا کار ما رو راه میندازه :))) پس بریم شروع کنیم :)

خب برای شروع یک سورس پایتون درست کنین و کد زیر رو داخلش قرار بدین:

from moviepy.editor import VideoFileClip
import numpy as np
import os
from datetime import timedelta

# i.e if video of duration 30 seconds, saves 10 frame per second = 300 frames saved in total
SAVING_FRAMES_PER_SECOND = 10

def format_timedelta(td):
    """Utility function to format timedelta objects in a cool way (e.g 00:00:20.05) 
    omitting microseconds and retaining milliseconds"""
    result = str(td)
    try:
        result, ms = result.split(".")
    except ValueError:
        return result + ".00".replace(":", "-")
    ms = int(ms)
    ms = round(ms / 1e4)
    return f"{result}.{ms:02}".replace(":", "-")


def main(video_file):
    # load the video clip
    video_clip = VideoFileClip(video_file)
    # make a folder by the name of the video file
    filename, _ = os.path.splitext(video_file)
    filename += "-moviepy"
    if not os.path.isdir(filename):
        os.mkdir(filename)

    # if the SAVING_FRAMES_PER_SECOND is above video FPS, then set it to FPS (as maximum)
    saving_frames_per_second = min(video_clip.fps, SAVING_FRAMES_PER_SECOND)
    # if SAVING_FRAMES_PER_SECOND is set to 0, step is 1/fps, else 1/SAVING_FRAMES_PER_SECOND
    step = 1 / video_clip.fps if saving_frames_per_second == 0 else 1 / saving_frames_per_second
    # iterate over each possible frame
    for current_duration in np.arange(0, video_clip.duration, step):
        # format the file name and save it
        frame_duration_formatted = format_timedelta(timedelta(seconds=current_duration)).replace(":", "-")
        frame_filename = os.path.join(filename, f"frame{frame_duration_formatted}.jpg")
        # save the frame with the current duration
        video_clip.save_frame(frame_filename, current_duration)


if __name__ == "__main__":
    import sys
    video_file = sys.argv[1]
    main(video_file)

توضیح: خب اول کتابخونه های مورد نیازمون رو import کردیم.

بعدش متغیر و تابع بعدی تکراریه و بالا هم تعریفشون کردیم.

توی بخش main هم گفتیم که یک ورودی video از ما بگیره و فریم هاشو استخراج کنه.

توی چند خط آخر هم گفتیم که مقدار ورودی video رو بگیره و تابع main رو روش اجرا کردیم.

 

روش اجرا کردن این کد هم به صورت همون کد بالایی هست.

خب همونطور که احتمالا متوجه شدین کد های روش دوم کمتره. ولی بجاش سرعت opencv از moviepy بیشتره. اگر بخواین برای video های طولانی و با حجم زیاد این اسکریپت رو استفاده کنین بهتون توصیه میکنم که از کتابخونه opencv استفاده کنین، ولی اگر میخواین برای ویدیو های کوتاه اینکار رو انجام بدین بهتره که از روش دوم یعنی moviepy استفاده کنین چون کد هایی که توی اسکریپت استفاده میشه کمتره و برای استفاده روی ویدیو های کم حجم و سبک بهتره.

 

امیدوارم براتون مفید بوده باشه D:


  • سلام
    بسیار عالی
    مطالب همه فوق العاده ان
    خیلی ممممنون
    پاسخ:
    سلام دوست عزیز
    ممنون از لطف و توجه شما
    موفق باشید
ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی