社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

10万+的短视频被批量生产了,Python表示不服

Python爱好者社区 • 4 年前 • 470 次点击  

阅读文本大概需要 10 分钟。



1

目 标 场 景


做过自媒体的朋友应该都知道,「GIF动画视频」有段时间在各大自媒体平台很受欢迎。


前期有些自媒体大 V 靠搬运一些搞笑、好玩的 GIF,然后利用剪辑软件合成一段视频,再添加一个节奏感强的 BGM 后,上传各大自媒体平台后,能带来不错的阅读量和收益。



本篇文章的目的是带大家利用 Python 实现制作 GIF 动画视频,批量制作短视频这一骚操作。


2

准 备 工 作


首先,对视频和背景音乐的剪辑,这里用到了「moviepy」库,通过 pip3 安装到虚拟环境中。


# moviepy 用于视频剪辑和背景音乐的合成、剪辑
pip3 install moviepy


另外,项目中需要利用「PIL」库来分析、获取 GIF 动画中的所有帧图片。


# 将GIF图片转为帧,需要对GIF进行分析
pip3 install ffmpeg


编写脚本之前,我们需要提前准备一些 GIF 动画素材。当然,你也可以去爬取一些搞笑、好玩的 GIF 动画。


另外再准备一段 BGM 作为视频的背景音乐。



3

编  写  脚  本


第一步,我们需要把每一个 GIF 动画转为一段视频。


由于 GIF 动画已经是一段包含很多帧的视频了,没法直接通过 moviepy 库转为一段普通视频。


所以,这里需要对 GIF 动画进行分析,将动画转为「静态帧图片」。


def get_gif_frames(gif_path, temp_path):
    """
    获取一段GIf图片下的所有静态帧
    get_gif_frames('./../gifs/3.gif', './../gif_temp/')
    :return:
    """


    # 分析gif图片
    mode = analyseImage(gif_path)['mode']

    im = Image.open(gif_path)

    i = 1
    p = im.getpalette()
    last_frame = im.convert('RGBA')

    try:
        while True:
            # print("saving %s (%s) frame %d, %s %s" % (gif_path, mode, i, im.size, im.tile))

            '''
            If the GIF uses local colour tables, each frame will have its own palette.
            If not, we need to apply the global palette to the new frame.
            '''

            if not im.getpalette():
                im.putpalette(p)

            new_frame = Image.new('RGBA', im.size)

            '''
            Is this file a "partial"-mode GIF where frames update a region of a different size to the entire image?
            If so, we need to construct the new frame by pasting it on top of the preceding frames.
            '''

            if mode == 'partial':
                new_frame.paste(last_frame)

            new_frame.paste(im, (0, 0), im.convert('RGBA'))
            new_frame.save(temp_path + '/%s-%d.png' % (''.join(os.path.basename(gif_path).split('.')[:-1]), i), 'PNG')

            i += 1
            last_frame = new_frame
            im.seek(im.tell() + 1)
    except EOFError:
        # print('产生EOFError!!!')
        pass


另外,我们下载的 GIF 动画的静态帧图片分辨率大概率是不一致的,所以对图片批量修改分辨率「修改分辨率」变的很有必要。


这里将所有图片的分辨率统一修改为 720*1080,在转换的过程中,如果存在空白部分,就使用黑色进行填充。


def resize_image(target_image_path, target_size):
    """
    调整图片大小,缺失的部分用黑色填充
    :param target_image_path: 图片路径
    :param target_size: 分辨率大小
    :return:
    """

    image = Image.open(target_image_path)

    iw, ih = image.size  # 原始图像的尺寸
    w, h = target_size  # 目标图像的尺寸
    scale = min(w / iw, h / ih)  # 转换的最小比例

    # 保证长或宽,至少一个符合目标图像的尺寸
    nw = int(iw * scale)
    nh = int(ih * scale)

    image = image.resize((nw, nh), Image.BICUBIC)  # 缩小图像
    # image.show()

    new_image = Image.new('RGB', target_size, (0, 0, 0, 0))  # 生成黑色图像
    # // 为整数除法,计算图像的位置
    new_image.paste(image, ((w - nw) // 2, (h - nh) // 2))  # 将图像填充为中间图像,两侧为灰色的样式
    # new_image.show()

    # 覆盖原图片
    new_image.save(target_image_path)


然后,将统一分辨率后的静态帧图片转换为一段普通视频。


在转换为视频之前,我们需要提供一个「合理的转换帧率」来保证视频播放的流畅性。由于最后需要将多段视频合成为一段视频,这里默认指定帧率为 10帧/s。


GIF 动画原始的帧率、播放时长等动画文件属性值可以利用「imgpy」获取到。

 

def get_gif_info(gif_path):
    """
    获取gif文件的详细信息
    每一个gif的帧率不一样,有的<10fps;有的>10fps
    :param gif_path:
    :return:
    """

    with Img(fp=gif_path) as im:
        # 1.有多少帧
        frame_count = im.frame_count

        # 2.图片信息
        # {'version': b'GIF89a', 'background': 31, 'duration': 70, 'extension': (b'NETSCAPE2.0', 795), 'loop': 0}
        duration_pre = im.info.get('duration')

        # 根据规律,除以7位实际的播放时长
        duration = duration_pre / 7

        # 6.color palette
        # print(im.mode_desc)

        # print((frame_count, duration))

        # 返回帧率和时长
        return (frame_count / duration), duration


最后,我们利用 moviepy 库中的「ImageSequenceClip」类将这些图片写入到一个视频文件中。


def pics_to_video(pics_path, output_path, fps, duration):
    """
    图片转为视频
    pics_to_video('./../gif_temp/', './../video_temp/temp1.mp4', 20)
    :param pics_path:
    :param output_path:
    :return:
    """

    image_paths = list(map(lambda x: pics_path + x, os.listdir(pics_path)))

    # 注意:这里必须进行一次排序,保证所有帧的顺序是一致
    image_paths = sort_strings_with_emb_numbers(image_paths)

    # 过滤掉非图片
    image_paths = list(filter(lambda image_path: image_path.endswith('.png'), image_paths))

    # 图片剪辑类
    clip = ImageSequenceClip(image_paths,
                             fps=fps)

    # 写成视频之前,需要把gif都转成同一个分辨率
    clip.write_videofile(output_path)


循环上面的操作,就可以将所有的 GIF 动画转换为一个普通视频文件。


第二步是将所有的视频文件进行剪辑,写入一个单独的文件中。利用 moviepy 库下面的 「 VideoFileClip 」可以非常快捷方便地完成这一操作。


def compound_a_video(self, videos_path):
        """
        合成一个视频
        :param videos_output:视频集合的完整目录
        :return:
        """

        # 定义一个数组
        L = []

        for video_path in videos_path:
            # 载入视频
            video = VideoFileClip(video_path)
            # 添加到数组
            L.append(video)

        # 拼接视频
        final_clip = concatenate_videoclips(L)

        # 生成目标视频文件
        final_clip.to_videofile(self.video_output_temp, fps=self.fps, remove_temp=False)


最后一步是往视频中添加背景音乐。


首先是通过 AudioFileClip 和 VideoFlieClip 获取到视频文件和音频文件的播放时长,对播放时长较长的文件进行截取处理。


# 1.音频文件
audioclip = AudioFileClip(self.bgm_path)

# 2.视频文件
 videoclip = VideoFileClip(self.video_output_temp)

# 3.获取视频和音频的时长
video_time = videoclip.duration
audio_time = audioclip.duration

print('视频时长:%f,音频时长:%f' % (video_time, audio_time))

# 4.对视频或者音频进行裁剪
if video_time > audio_time:
      # 视频时长>音频时长,对视频进行截取
      ideoclip_new = videoclip.subclip(0, audio_time)
      audioclip_new = audioclip
else:
      # 音频时长>视频时长,对音频进行截取
      videoclip_new = videoclip
      audioclip_new = audioclip.subclip(0, video_time)


然后把音频文件通过 set_audio() 添加到视频操作类中,最后重新写入到一个新的视频文件当中。


# 5.视频中加入音频
video_with_new_audio = videoclip_new.set_audio(audioclip_new)

# 6.写入到新的视频文件中
video_with_new_audio.write_videofile("mp4_with_audio.mp4",
                                             codec='libx264',
                                             audio_codec='aac',
                                             temp_audiofile='temp-audio.m4a',
                                             remove_temp=True
                                             )


4

结 果 结 论


以上的脚本会对指定文件夹的的 GIF 动画文件分别生成一段普通视频,然后把所有的视频合成一段视频,然后再添加一段 BGM 背景音乐,最后写入到一个新的视频文件中,如此,就完成了制作一个 GIF 视频的操作。


当然,本文只是提供一个思路,让 Python 爬取一些有趣好玩的 GIF 动画进而批量做成视频,上传各大自媒体平台,应该也能获取到不错的阅读量。

如果你觉得文章还不错,请大家点赞分享下。你的肯定是我最大的鼓励和支持。

😁 顺便提一嘴:


本周自媒体副业实战公开课(共9节)预告:



 我将于🕘12月24日晚9点 | 周五视频号免费直播

🌻直播主题:自媒体定位准,一做就圈粉!

我敢跟你们拍🤲胸脯保证,这极大概率是全网最干货的直播,全程没有回放。
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/124899