我正在使用MoviePy从服务器上的视频剪辑自动创建GIF。图书馆帮助进行各种各样的视频转换和剪辑,以创建gif。在
在我当前的项目中,我有一个视频剪辑,里面有很多移动的物体,很难对感兴趣的区域进行自动跟踪。 (下面一个快速的gif显示了场景,虽然那里的背景可以很容易地消除和做目标的跟踪。但假设跟踪对象超出了项目的范围)。
如下面的gif所示,红色矩形是随时间从左向右移动的感兴趣区域。 我想裁剪这个区域并创建一个GIF。在
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gRPHLtCX-1640780015513)(https://i.stack.imgur.com/rGfeK.gif)]
我用的是MoviePy,我从一个视频剪辑了一个矩形来创建一个gif。但矩形被固定在其原始坐标位置。我正在寻找一种方法,移动矩形随着时间和裁剪它创建一个GIF。在
clip = (VideoFileClip("my_video.mp4")
.subclip((1,10.1),(1,14.9))
.resize(0.5)
.crop(x1=145,y1=110,x2=400,y2=810))
clip.write_gif("my_gif.gif")
如何添加时间因子,使坐标随时间变化。在
欢迎提出任何建议。在
您正在moviepy中查找scroll
函数。它缺少文档,但是它们是here,以及源代码{a2}。在
moviepy.video.fx.all.scroll(clip, h=None, w=None, x_speed=0, y_speed=0, x_start=0, y_start=0, apply_to='mask')
参数:
-
clip
;要作用的剪辑 -
h
和{},它们决定最终剪辑的高度和宽度 -
x_speed
和{},它们决定滚动的速度。不确定这些是用什么度量的,所以您可能需要调查源代码,或者只是尝试并出错。 -
x_start
和{}这是它开始滚动的距离(0,0)。 -
apply_to
;如果要使用掩码,则不需要它!
最终代码:
clip = (VideoFileClip("my_video.mp4")
.subclip((1,10.1),(1,14.9))
.resize(0.5)
.crop(x1=145,y1=110,x2=400,y2=810))
# You won't need to set `w` and `h` if you are separately cropping it
new_clip = vfx.scroll(clip, w=clip.w, h=var, x_speed=speed, y_start=height_to_top_of_wanted_bit)
new_clip.set_duration(1.0 / speed)
clip.write_gif("my_gif.gif")
注意,此代码未经测试。在
# -*- coding: utf-8 -*-
import cv2
import numpy as np
import time
import os
import math
from common.libs.utils.file_utils import *
from common.libs.utils.image_utils import *
from PIL import Image
from moviepy.editor import *
import moviepy.video.fx.all as vfx
# 最后影片的分辨率片,根据视频来设置,默认是1920*1080
img_size = (int(1920), int(1080))
def cut_film(soure_filename, output_filename, start_time, peroid):
"""
裁剪视频
:param start_time: 开始时间【s】
:param peroid: 持续时间【s】
:param soure_filename 源视频
:param output_filename 输出视频
:return:
"""
global img_size
# 数据源
videoCapture = cv2.VideoCapture(soure_filename)
if videoCapture.isOpened():
print('open success')
else:
print('open fail')
fps = videoCapture.get(cv2.CAP_PROP_FPS)
# 获取大小(1920*1080)
img_size = (int(videoCapture.get(cv2.CAP_PROP_FRAME_WIDTH)),
int(videoCapture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
video_writer = cv2.VideoWriter(output_filename, cv2.VideoWriter_fourcc('X', 'V', 'I', 'D'), fps, img_size)
i = 0
# 开始帧和结束帧
start_frame = fps * start_time
end_frame = start_frame + peroid * fps
print(type(start_frame))
print('即将裁剪的开始帧为:%f,结束帧为:%f' % (start_frame, end_frame))
while True:
success, frame = videoCapture.read() # 循环读取下一帧
if success:
i += 1
if start_frame <= i <= end_frame:
# 将截取到的画面写入“新视频”
video_writer.write(frame)
else:
print('end')
break
videoCapture.release()
return fps
def compound_film(film_names, video_output_path):
"""
合并视频
:param film_names:
:return:
"""
print('一共要合成%d段视频' % len(film_names))
print(film_names)
# 通过第一个视频,获取帧率、宽、高
videoCaptureNorm = cv2.VideoCapture(film_names[0])
# 帧率、宽、高
fps = videoCaptureNorm.get(cv2.CAP_PROP_FPS)
width = (int(videoCaptureNorm.get(cv2.CAP_PROP_FRAME_WIDTH)))
height = (int(videoCaptureNorm.get(cv2.CAP_PROP_FRAME_HEIGHT)))
# =====================================================================
# 待写入对象,写入到result.mp4文件中
videoWriter = cv2.VideoWriter(video_output_path, cv2.VideoWriter_fourcc('m', 'p', '4', 'v'), fps, (width, height))
for index, film_name in enumerate(film_names):
print('视频名称:%s' % film_name)
videoCapture = cv2.VideoCapture(film_name)
# 循环写入数据
while True:
success, frame = videoCapture.read()
# 视频必须保证分辨率一致,才能合并
# frame_suitable = cv2.resizeWindow(frame, (width, height), interpolation=cv2.INTER_CUBIC)
videoWriter.write(frame)
if success:
continue
else:
break
videoCapture.release()
print('第%d个视频合成完成~' % (index + 1))
videoWriter.release()
videoCaptureNorm.release()
def compound_pic_special(images_path, output_video_path, fps, total_second):
"""
图片合成视频【卡点视频】
:param path: 图片文件路径
:param output_video_path:合成视频的路径
:param size:
:return:
"""
# 获取该目录下的所有文件名
file_list = os.listdir(images_path)
file_list = sort_strings_with_emb_numbers(file_list)
print('一共有%d张图片' % len(file_list))
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
video = cv2.VideoWriter(output_video_path, fourcc, fps, img_size)
percentSecond = total_second/(len(file_list))
total_count = math.ceil(fps*percentSecond)
print('每张图片都需要重复写入%d帧' % total_count)
for i,item in enumerate(file_list):
if item.endswith('.jpg'): # 判断图片后缀是否是.jpg
image_path = images_path + item
# 缩放图片到合适的分辨率,并覆盖源文件
resize_image(image_path, img_size, image_path)
count = 0
while count < total_count:
temp_tuple_size = (int(1920 + 0.1 * count), int(1080 + 0.1 * count))
frame = cv2.imread(image_path)
# 直接缩放到指定大小
frame_suitable = cv2.resize(frame, (temp_tuple_size[0], temp_tuple_size[1]), interpolation=cv2.INTER_CUBIC)
video.write(frame_suitable)
count += 1
else:
print('名称为:%s,文件格式不对,过滤掉~' % item)
# 释放资源
video.release()
def compound_bgm(video_path, bgm_path, video_output_path):
"""
通过视频、BGM 合成一段视频
:param video_path: 视频路径
:param bgm_path: BGM路径
:return:
"""
# $ ffmpeg -i 2_003_014.mp4 -vn -y -acodec copy 3.aac
# 1.提前temp.mp4这个视频的BGM,文件结果为:temp.aac
# os.system('ffmpeg -i temp.mp4 -vn -y -acodec copy temp.aac')
# 2.获取视频的长度
cap = cv2.VideoCapture(video_path)
# 帧率
fps = cap.get(cv2.CAP_PROP_FPS)
# 总帧数
frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
# 总时长-秒,这里做取整操作 【浮点类型】
time_count = math.floor(frame_count / fps)
print('帧率:%f,总帧数:%d' % (fps, frame_count))
print(time_count)
# 3.截取音频
# 为了简单,这里一般不会超过一分钟
#bgm_temp_path = get_temp_path(bgm_path, 'temp_new')
#os.system('ffmpeg -i %s -ss 00:00:00 -t 00:00:%d -acodec copy %s' % (bgm_path, time_count, bgm_temp_path))
# 3.1 删除源音频并重命令当前文件
#os.remove(bgm_path)
#os.rename(bgm_temp_path, bgm_path)
# 4.视频、音频合二为一
#video_output_path = get_temp_path(video_path, 'output')
if os.path.exists(video_output_path):
os.remove(video_output_path)
os.system('ffmpeg -i %s -i %s -vcodec copy -acodec copy %s' % (video_path, bgm_path, video_output_path))
print('音视频合成完成~')
def make_sticke_point_video(source_film_path, source_image_path, source_bgm_path,oupputMp4Path, video_output_path):
"""
制作卡点视频
:return:
"""
# 1.从一段视频中剪辑前2s的视频
# 也可以指定开始帧和持续时间
# 生成的第一段视频2s
template_video_time = 2
video_part1 = oupputMp4Path + 'part1.mp4'
fps = cut_film(source_film_path, video_part1, 0, template_video_time)
print('第一段视频剪辑完成~')
print('第一段视频的帧率是:%f' % fps)
# 2.把所有图片合成一个视频
# #获取语音时长
audioclip = AudioFileClip(source_bgm_path)
audio_time = audioclip.duration
video_part2 = oupputMp4Path + 'part2.mp4'
compound_pic_special(source_image_path, video_part2, fps, audio_time)
print('第二段视频合成完成~')
# 3.加入背景音乐
compound_bgm(video_part2, source_bgm_path, video_output_path)
#transitions_animation('C:\\tmp\\NbaNew\\20211229\\result\\2626507b8820ddea8b308ba091335e51\\outputMp4\\part1.mp4','C:\\tmp\\NbaNew\\20211229\\result\\2626507b8820ddea8b308ba091335e51\\outputMp4\\part2.mp4','C:\\tmp\\NbaNew\\20211229\\result\\2626507b8820ddea8b308ba091335e51\\outputMp4\\part3.mp4')
def transitions_animation(path_video1, path_video2, output_path):
"""
两段视频中转场动画(以淡入淡出为例)
注意:保证视频拍摄帧率一致
:param video1:
:param video2:
:return:
"""
# 获取视频时长
clip_video1 = VideoFileClip(path_video1)
duration_video1 = clip_video1.duration
clip_video2 = VideoFileClip(path_video2)
duration_video2 = clip_video2.duration
print(f'两段视频的时长分别为:{duration_video1},{duration_video2}')
# 统一视频分辨率
w, h, fps = clip_video1.w, clip_video1.h, clip_video1.fps
clip_video2_new = clip_video2.resize((w, h))
# 转场时长,默认2s
transitions_time = 2
# 第一段视频执行淡出效果
subVideo1_part1 = clip_video1.subclip(0, duration_video1 - 1)
subVideo1_part2 = clip_video1.subclip(duration_video1 - 1).fadeout(1, (1, 1, 1))
# 第二段视频执行淡入效果
subVideo2_part1 = clip_video2_new.subclip(0, 1).fadein(1, (1, 1, 1))
subVideo2_part2 = clip_video2_new.subclip(1)
# 合并4段视频
result_video = concatenate_videoclips([subVideo1_part1, subVideo1_part2, subVideo2_part1, subVideo2_part2])
# pass 写入视频文件
result_video.write_videofile(output_path)
def resize_animation(path_video1, output_path):
"""
两段视频中转场动画(以淡入淡出为例)
注意:保证视频拍摄帧率一致
:param video1:
:param video2:
:return:
"""
# 获取视频时长
clip1 = VideoFileClip(path_video1).crop(0, 0, 540, 660)
duration_video1 = clip1.duration
# 第一段视频执行淡出效果
#subVideo1_part1 = clip_video1.subclip(0, duration_video1).resize(lambda t : 1+0.0001*t)
# 合并4段视频
#result_video = concatenate_videoclips([subVideo1_part1, subVideo1_part2, subVideo2_part1, subVideo2_part2])
#newclip = clip1.fx(vfx.mask_and, clip1)
# preview(newclip)
clip1.write_videofile(output_path)