از این تکنیک برای اعمال ریاضیات هوشمندانه در ویدیوهای خود و کاهش لرزش استفاده کنید.
تثبیت ویدئو تکنیکی است که حرکت و لرزش ناخواسته را در فیلم های ویدئویی کاهش می دهد. عکسبرداری با دست، لرزش و حرکت همگی می توانند باعث حرکات ناپایدار دوربین شوند. تثبیتکننده ویدیو ویدیویی روانتر تولید میکند.
هدف اصلی تثبیت کننده ویدیو تخمین حرکت دوربین بین فریم های متوالی است. سپس این فرآیند می تواند تبدیل های مناسب را برای تراز کردن فریم ها اعمال کند. این حرکت درک شده را به حداقل می رساند.
تنظیم محیط
با ایجاد یک محیط مجازی شروع کنید تا مطمئن شوید بسته هایی که برای اجرای برنامه نصب می کنید با بسته های موجود تضاد ندارند. سپس این دستور ترمینال را برای نصب کتابخانه های مورد نیاز اجرا کنید:
pip install opencv-python numpy
این دستور کتابخانه های NumPy و OpenCV را نصب می کند. NumPy ابزارهایی را برای کارهای عددی فراهم می کند در حالی که OpenCV با وظایف بینایی رایانه سروکار دارد.
کد منبع کامل در یک مخزن GitHub موجود است.
واردات کتابخانه های مورد نیاز و تعریف سه کارکرد حیاتی
یک فایل پایتون جدید ایجاد کنید و نام دلخواه خود را به آن بدهید. کتابخانه های NumPy و OpenCV را در ابتدای اسکریپت وارد کنید.
import numpy as np
import cv2
وارد کردن این کتابخانه ها به شما امکان می دهد از توابع آنها در کد خود استفاده کنید.
در مرحله بعد، سه عملکرد را تعریف کنید که برای فرآیند تثبیت بسیار مهم هستند.
تابع محاسبه_حرکت_میانگین
یک تابع ایجاد کنید و نام آن را account_moving_average بگذارید. این تابع میانگین متحرک یک منحنی معین را با استفاده از شعاع مشخص شده شما محاسبه می کند. از یک عملیات کانولوشن با اندازه پنجره مشخص و یک هسته یکنواخت استفاده می کند. این میانگین متحرک به هموار کردن نوسانات در مسیر کمک می کند.
def calculate_moving_average(curve, radius):
# Calculate the moving average of a curve using a given radius
window_size = 2 * radius + 1
kernel = np.ones(window_size) / window_size
curve_padded = np.lib.pad(curve, (radius, radius), 'edge')
smoothed_curve = np.convolve(curve_padded, kernel, mode='same')
smoothed_curve = smoothed_curve[radius:-radius]
return smoothed_curve
تابع یک منحنی صاف برمی گرداند. به کاهش نویز و نوسانات در منحنی کمک می کند. این کار را با میانگین گیری مقادیر درون پنجره کشویی انجام می دهد.
تابع smooth_trajectory
یک تابع دیگر ایجاد کنید و نام آن را smooth_trajectory بگذارید. این تابع میانگین متحرک را در هر بعد از مسیر اعمال می کند. با ایجاد یک کپی هموار از مسیر اصلی به این امر دست خواهد یافت. این باعث بهبود بیشتر پایداری ویدیو می شود.
def smooth_trajectory(trajectory):
# Smooth the trajectory using moving average on each dimension
smoothed_trajectory = np.copy(trajectory)
for i in range(3):
smoothed_trajectory[:, i] = calculate_moving_average(
trajectory[:, i],
radius=SMOOTHING_RADIUS
)
return smoothed_trajectory
تابع smooth_trajectory یک مسیر صاف شده را برمی گرداند.
تابع fix_border
یک تابع نهایی ایجاد کنید و نام آن را fix_border بگذارید. این تابع با اعمال یک چرخش و تغییر مقیاس، مرز قاب را ثابت می کند. قاب ورودی را می گیرد، شکل آن را محاسبه می کند، یک ماتریس تبدیل ایجاد می کند و تبدیل را روی قاب اعمال می کند. در نهایت فریم ثابت را برمی گرداند.
def fix_border(frame):
# Fix the frame border by applying rotation and scaling transformation
frame_shape = frame.shape
matrix = cv2.getRotationMatrix2D(
(frame_shape[1] / 2, frame_shape[0] / 2),
0,
1.04
)
frame = cv2.warpAffine(frame, matrix, (frame_shape[1], frame_shape[0]))
return frame
تابع fix_border تضمین میکند که فریمهای تثبیتشده هیچ گونه آرتیفکت حاشیهای ناشی از فرآیند تثبیت ندارند.
راه اندازی تثبیت ویدئو و گرفتن ورودی
با تنظیم شعاعی که تابع هموارسازی مسیر استفاده می کند، شروع کنید.
SMOOTHING_RADIUS = 50
سپس، از مسیر ویدیویی ویدیویی لرزان که میخواهید تثبیت کنید عبور کنید.
# Open the input video file
# Replace the path with 0 to use your webcam
cap = cv2.VideoCapture('inputvid.mp4')
ویژگی های ویدیوی لرزان را دریافت کنید:
num_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS)
فرمت خروجی را تنظیم کنید. این فرمتی است که برنامه ویدیوی تثبیت شده را با آن ذخیره می کند. می توانید از هر فرمت ویدیویی رایجی که دوست دارید استفاده کنید.
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
در نهایت، نویسنده ویدیو را مقداردهی اولیه کنید:
out = cv2.VideoWriter('video_out.mp4', fourcc, fps, (2 * width, height))
پسوند نام فایلی که به برنامه نویس ویدیو ارسال می کنید باید با نامی باشد که در فرمت خروجی تنظیم کرده اید.
خواندن و پردازش فریم ها
اولین مرحله پردازش فیلم لرزان از اینجا شروع می شود. این شامل خواندن فریمها از ویدیوی ورودی، محاسبه تبدیلها و پر کردن آرایه تبدیلها است.
با خواندن فریم اول شروع کنید.
_, prev_frame = cap.read()
prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)
سپس آرایه تبدیل را مقداردهی اولیه کنید. اطلاعات مربوط به هر فریم را ذخیره می کند.
transforms = np.zeros((num_frames - 1, 3), np.float32)
در نهایت، شما باید جریان نوری بین فریم های متوالی را محاسبه کنید. سپس، تبدیل affine بین نقاط را تخمین بزنید.
for i in range(num_frames - 2):
# Calculate optical flow between consecutive frames
prev_points = cv2.goodFeaturesToTrack(
prev_gray,
maxCorners=200,
qualityLevel=0.01,
minDistance=30,
blockSize=3
)
success, curr_frame = cap.read()
if not success:
break
curr_gray = cv2.cvtColor(curr_frame, cv2.COLOR_BGR2GRAY)
curr_points, status, err = cv2.calcOpticalFlowPyrLK(
prev_gray,
curr_gray,
prev_points,
None
)
assert prev_points.shape == curr_points.shape
idx = np.where(status == 1)[0]
prev_points = prev_points[idx]
curr_points = curr_points[idx]
# Estimate affine transformation between the points
matrix, _ = cv2.estimateAffine2D(prev_points, curr_points)
translation_x = matrix[0, 2]
translation_y = matrix[1, 2]
rotation_angle = np.arctan2(matrix[1, 0], matrix[0, 0])
transforms[i] = [translation_x, translation_y, rotation_angle]
prev_gray = curr_gray
حلقه روی هر فریم (به جز آخرین فریم) برای محاسبه تبدیل ها تکرار می شود. جریان نوری بین فریم های متوالی را با استفاده از روش لوکاس-کاناد محاسبه می کند. cv2.goodFeaturesToTrack نقاط ویژگی را در قاب قبلی prev_gray تشخیص می دهد. سپس، cv2.calcOpticalFlowPyrLK این نقاط را در فریم فعلی curr_gray ردیابی می کند.
فقط نقاط با وضعیت 1 (نشان دهنده ردیابی موفق) به تخمین ماتریس تبدیل افین کمک می کنند. کد، متغیر prev_gray را با فریم مقیاس خاکستری فعلی برای تکرار بعدی به روز می کند.
هموارسازی مسیر
برای دستیابی به یک نتیجه پایدار، باید مسیر به دست آمده از تحولات را هموار کنید.
# Calculate the trajectory by cumulatively summing the transformations
trajectory = np.cumsum(transforms, axis=0)
# Smooth the trajectory using moving average
smoothed_trajectory = smooth_trajectory(trajectory)
# Calculate the difference between the smoothed and original trajectory
difference = smoothed_trajectory - trajectory
# Add the difference back to the original transformations to obtain smooth
# transformations
transforms_smooth = transforms + difference
کد بالا مسیر حرکت دوربین را محاسبه کرده و آن را صاف می کند.
تثبیت و نوشتن قاب ها
مرحله آخر تثبیت فریم ها و نوشتن فیلم تثبیت شده در یک فایل خروجی است.
با تنظیم مجدد ضبط ویدیو شروع کنید. این اطمینان حاصل می کند که عملیات آینده از ابتدای ویدیو خوانده می شود.
cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
سپس با پردازش هر فریم ویدیو را تثبیت کنید.
# Process each frame and stabilize the video
for i in range(num_frames - 2):
success, frame = cap.read()
if not success:
break
translation_x = transforms_smooth[i, 0]
translation_y = transforms_smooth[i, 1]
rotation_angle = transforms_smooth[i, 2]
# Create the transformation matrix for stabilization
transformation_matrix = np.zeros((2, 3), np.float32)
transformation_matrix[0, 0] = np.cos(rotation_angle)
transformation_matrix[0, 1] = -np.sin(rotation_angle)
transformation_matrix[1, 0] = np.sin(rotation_angle)
transformation_matrix[1, 1] = np.cos(rotation_angle)
transformation_matrix[0, 2] = translation_x
transformation_matrix[1, 2] = translation_y
# Apply the transformation to stabilize the frame
frame_stabilized = cv2.warpAffine(
frame,
transformation_matrix,
(width, height)
)
# Fix the border of the stabilized frame
frame_stabilized = fix_border(frame_stabilized)
# Concatenate the original and stabilized frames side by side
frame_out = cv2.hconcat([frame, frame_stabilized])
# Resize the frame if its width exceeds 1920 pixels
if frame_out.shape[1] > 1920:
frame_out = cv2.resize(
frame_out,
(frame_out.shape[1] // 2, frame_out.shape[0] // 2)
)
# Display the before and after frames
cv2.imshow("Before and After", frame_out)
cv2.waitKey(10)
# Write the frame to the output video file
out.write(frame_out)
کد بالا با استفاده از تبدیل های محاسبه شده، از جمله تنظیمات ترجمه و چرخش، هر فریم را تثبیت می کند. سپس فریم های تثبیت شده را با فریم های اصلی ترکیب می کند تا مقایسه ای ارائه دهد.
انتشار فیلم ضبط و نویسنده
با آزاد کردن اشیاء ضبط ویدیو و نویسنده، برنامه را نهایی کنید.
# Release the video capture and writer, and close any open windows
cap.release()
out.release()
cv2.destroyAllWindows()
این کد همچنین تمامی پنجره های باز را می بندد.
خروجی نهایی برنامه
خروجی برنامه چیزی شبیه به این خواهد بود:
و در اینجا نمونه ای از ویدئوی تثبیت شده است:
خروجی مقایسه بین فیلم لرزان و تثبیت شده را نشان می دهد.
قابلیت های OpenCV را کاوش کنید
شما می توانید OpenCV را در بسیاری از زمینه هایی که شامل بینایی کامپیوتری هستند، اعمال کنید. این به این دلیل است که طیف گسترده ای از عملکردها را ارائه می دهد. شما باید توانایی های آن را با کار بر روی پروژه های بیشتری که شامل بینایی کامپیوتری می شوند، کشف کنید. این شما را با مفاهیم جدید آشنا می کند و زمینه های جدیدی را برای تحقیق در مورد آنها به شما می دهد.