如何把图片变得炫酷多彩,Python教你这样实现!

举报
技术火炬手 发表于 2019/09/23 11:18:53 2019/09/23
6.6k+ 0 2
【摘要】 程序猿的哭泣最近项目忙的有点疯狂到996ICU的节奏,差一点就一周无休了…还好周六晚上总算是硬气的决定休一天,不然真的如下图般:来自30岁程序猿的哭泣了。今天西安的天气很不错,早起带着俩小兔崽子出门压马路,一直疯的天黑。中午俩人闹着要吃饺子,吃饭期间随手拍一张发给躺在床上追剧的家庭妇女,还被吐槽照片水平太差,把孩子拍的真瓜。这我一下就不淡定了,晚上想想怎么用Python将单调的图片,变得更有...

程序猿的哭泣

最近项目忙的有点疯狂到996ICU的节奏,差一点就一周无休了…还好周六晚上总算是硬气的决定休一天,不然真的如下图般:来自30岁程序猿的哭泣了。

640.gif

今天西安的天气很不错,早起带着俩小兔崽子出门压马路,一直疯的天黑。中午俩人闹着要吃饺子,吃饭期间随手拍一张发给躺在床上追剧的家庭妇女,还被吐槽照片水平太差,把孩子拍的真瓜。

image.png

这我一下就不淡定了,晚上想想怎么用Python将单调的图片,变得更有趣!

有趣的图片

如何能让图片变得好玩?首先需要让它动起来!可如果是多张图片,我们还可以将其拼接起来组成gif动图,可一张图怎么玩?记得之前写过一个小练习,把一张图片拆分成九宫格的分片图。那么,能否由此下手整出点花样呢?先来看看最终实现的两种方案吧:

轮播闪现

640 (1).gif

分块加载

640 (2).gif

实现分析

命令行交互

首先,看过上面的两张动图,细心的朋友会发现,动图将原有的图片拆分为了25块,然后进行特定的拼接。那么只能拆分成25么,模式又该怎么选择呢?此时我们需要引入一个模块argparse,它是专门用作命令行参数配置的库。之前专门写过一篇针对该模块的总结文章,大家可以去看看:

对于python命令行,你应该这么做才专业

好了,回到当下内容,我们需要针对三项进行配置,图片路径、gif展示方式、拆分图片数量

# -*- coding: utf-8 -*-
# @Author   : 王翔
# @WeChat   : King_Uranus
# @公众号    : 清风Python
# @Date     : 2019/9/22 22:11
# @Software : PyCharm
# @version  :Python 3.7.3
# @File     : FunnyPicture.py

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("-p", "--picture", required=True,
                    help="请填写所需制作的图片全路径")
parser.add_argument('-t', '--type', default='join',
                    choices=['join', 'alone'],
                    help="join为分块加载,alone为轮播闪现")
parser.add_argument("-n", "--split_number", type=int, default=9,
                    choices=[9, 16, 25, 36, 49, 64, 81, 100],
                    help="选择拆分的图片数量")
args = parser.parse_args()

有了这些参数,我们就可以开始编写代码了…

图片裁剪

图片的剪裁与拆分使用什么模块呢?from PIL import Image简单通过Pillow的Image就可以实现相关操作了!

看到gif图我们会发现上下存在部分的留白,这是为什么?因为不是每张图都是等宽高的,所以我们要事先准备一块白色的幕布,然后将图片居中贴在白色背景图上。幕布大小如何决定,取图片宽高的最大值,生成一张正方形的白色幕布。

...

from PIL import Image
img = Image.open(args.picture.replace('\\', '/'))
_width, _height = img.size
img_size = _width if _width > _height else _height
blank_image = Image.new(self.img_mode, (self.img_size, self.img_size), color='white')
blank_image.save(....)
...

之后,就方便我们进行拆分了。

朋友圈不能发动图

我们的gif做好了,可以朋友圈不能发动图,这该如何是好?其实只需3行代码就能把一个gif的图片转化为视频文件。

模块安装:pip install moviepy

# -*- coding: utf-8 -*-
# @Author   : 王翔
# @WeChat   : King_Uranus
# @公众号    : 清风Python
# @Date     : 2019/9/22 22:11
# @Software : PyCharm
# @version  :Python 3.7.3
# @File     : FunnyPicture.py

import moviepy.editor as mp
clip = mp.VideoFileClip(filename)
clip.write_videofile('result.mp4')

640 (3).gif

没错,就是这么简单…但该模块封装了很多子模块,总体下载还是比较大的。我们在代码中自动引入该功能,同时生成gif与MP4文件。

总体代码

总体代码如下:

# -*- coding: utf-8 -*-
# @Author   : 王翔
# @WeChat   : King_Uranus
# @公众号    : 清风Python
# @Date     : 2019/9/22 22:11
# @Software : PyCharm
# @version  :Python 3.7.3
# @File     : FunnyPicture.py

import argparse
from PIL import Image
import os
import copy
import moviepy.editor as mp

BasePath = os.path.dirname(os.path.realpath(__file__))
class FunnyPicture:
    def __init__(self):

        self.img_mode = None
        self.img_size = None
        self.blank_image = None
        self.git_list = list()
        # 获取图片名称(去除后缀名)
        self.picture_name = os.path.splitext(os.path.split(args.picture)[1])[0]
        self.save_path = os.path.join(BasePath, self.picture_name)
        if not os.path.exists(self.save_path):
            os.mkdir(self.save_path)
        # 格式化图片路径
        self.picture = self.resize_picture()

    def resize_picture(self):
        img = Image.open(args.picture.replace('\\', '/'))
        self.img_mode = img.mode
        _width, _height = img.size
        self.img_size = _width if _width > _height else _height
        self.blank_image = Image.new(self.img_mode, (self.img_size, self.img_size), color='white')
        self.blank_image.save(os.path.join(self.save_path, '{}_blank.jpg'.format(self.picture_name)))
        _image = copy.copy(self.blank_image)
        if _width > _height:
            _image.paste(img, (0, int((self.img_size - _height) / 2)))
        else:
            _image.paste(img, (int((self.img_size - _width) / 2), 0))
        return _image

    def split_picture(self):
        size = int(args.split_number ** 0.5)
        side_len = int(self.img_size / size)
        _index = 1
        blank_image = copy.copy(self.blank_image)
        for i in range(0, size):
            for j in range(0, size):
                if args.type != "join":
                    blank_image = copy.copy(self.blank_image)
                per_size = (j * side_len, i * side_len, (j + 1) * side_len, (i + 1) * side_len)
                per_img = self.picture.crop(per_size)
                blank_image.paste(per_img, (j * side_len, i * side_len))
                self.git_list.append(copy.copy(blank_image))
                # 希望保留部分图片内容的可以取消注释
                # 中途的每一块局部图
                # per_img.save(os.path.join(self.save_path, '{}_per{}.jpg'.format(self.picture_name, _index)))
                # 动图的每一帧图片
                # blank_image.save(os.path.join(self.save_path, '{}_per_gif{}.jpg'.format(self.picture_name, _index)))
                _index += 1

    def composite_gif(self):
        images = []
        im = Image.open(os.path.join(self.save_path, '{}_blank.jpg'.format(self.picture_name)))
        for per_gif in self.git_list:
            images.append(per_gif)
        for i in range(10):
            images.append(self.picture)
        gif_name = "{}_result.gif".format(os.path.join(self.save_path, self.picture_name))
        im.save(gif_name, save_all=True, loop=True, append_images=images, duration=200)
        self.composite_mp4(gif_name)

    @staticmethod
    def composite_mp4(filename):
        clip = mp.VideoFileClip(filename)
        clip.write_videofile(os.path.splitext(filename)[0] + '.mp4')
if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("-p", "--picture", required=True,
                        help="请填写所需制作的图片全路径")
    parser.add_argument('-t', '--type', default='join',
                        choices=['join', 'alone'],
                        help="join为分块加载,alone为轮播闪现")
    parser.add_argument("-n", "--split_number", type=int, default=9,
                        choices=[9, 16, 25, 36, 49, 64, 81, 100],
                        help="选择拆分的图片数量")
    args = parser.parse_args()
    main = FunnyPicture()
    main.split_picture()
    main.composite_gif()

关于打包

既然为了好玩,当然要打包成exe的可执行文件喽。但关于打包,需要说明两点:

1.通过pyinstaller打包的软件,会被杀软误报。解压、使用时需添加白名单。

2.刚才说到moviepy依赖的模块太多,打包会导致异常,所以未将次功能进行打包,如果喜欢创建视频的,可以使用3行命令单独执行下。

来看看打包效果:

640 (4).gif

公众号回复趣图,获取代码及打包好的exe文件,拿去开心的装13吧!

The End

本文来源:清风Python公众号,欢迎关注。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。