python实现图片的抖音效果
前两篇文章 Affinity Photo绘制抖音音符 和 Sketch练习稿之绘制抖音音符 研究了如何用设计软件绘制抖音音符,本篇文章将说一下如何使用python将普通图片处理成抖音效果,甚至制作gif动图。
基本原理
Sketch练习稿之绘制抖音音符 中讲述了抖音效果的原理,领域内可能有人称这种效果为红蓝溢出效果。按照前面文章中的描述,这里以一张图片为例简单再讲一下原理。
一张正常的位图,一般 jpg
图片颜色方案为 RGB
,而 png
图片除了 RGB
色彩通道还有 A
通道控制透明,不过色彩配置还是一致的,以手上的图片 glasses.jpg
为例。
去掉 GB
两通道的图片如下:
去掉 R
通道的图片如下:
如果之间将两张图以相加的方式进行混合,就会得到原图,即第一张正常的图片。如果去掉 R
通道的图片在去掉 GB
通道的图片上方,并且向上和向左错开10px,就会得到抖音效果的图片:
实现图片抖音效果
使用python3为图片添加抖音效果
库安装
需要用到两个库,一个是 pillow 用来读写图片数据,另一个是 numpy 用来处理图片数据转换成的的矩阵数据,使用 pip3 安装即可:
pip3 install pillow
pip3 install numpy
安装完成后,执行 pip3 list
会列出安装的包,检查是否完成安装。
库的操作
pillow
本文主要用到库中的 Image 模块。
from PIL import Image
使用 Image 模块的 open
方法可以打开图片得到图片原生数据,类型为 PIL.JpegImagePlugin.JpegImageFile
:
image_data = Image.open('picname.jpg')
上述对象有两个常用的方法:
- show() : 显示图片,比如
image_data.show()
- save(path): 按照指定路径保存图片,比如
image_data.save(path)
numpy
这里主要使用numpy 库的矩阵操作,可以很方便的对矩阵进行切片操作,与 matlab 中的操作极其类似,首先导入库:
import numpy as np
np.array()
方法可以将上面 PIL.JpegImagePlugin.JpegImageFile
对象转换为原始矩阵数据,生成的矩阵为三维矩阵,前两维度为高宽像素点坐标,第三维度为 RGB
三通道,需要注意的是转换得到的矩阵数据类型为 numpy.uint8
:
img_array = np.array(image_data)
上面的操作可以逆向进行,也就是将 img_array 转换为 PIL.JpegImagePlugin.JpegImageFile
对象,需要依赖于 Image 的另一个方法 fromarray(array)
image = Image.fromarray(img_array)
可以很方便地使用切片操作处理图片矩阵数据中三个通道的数据,比如将 R
通道的数据设置为0:
img_array[:, :, 0] = 0
另外还需要用到矩阵复制的方法 numpy.copy(array)
,会得到 array 的复制对象:
img_other = np.copy(img_array)
具体实现
- 导入两个库
import numpy as np from PIL import Image
- 打开指定图片,这里以
glasses.jpg
为例,转换得到图片矩阵数据:img_data = Image.open('glasses.jpg') img_array = np.array(img_data)
- 复制得到两个新的矩阵:
img_r = np.copy(img_array) img_bg = np.copy(img_array)
- 这里需要注意的是,为了保证得到数据保存后的图片还是原图片的尺寸,这里只对有错位相加部分的像素点进行去通道操作,这里计划将 img_gb 向上向左错位10px,那么只需处理 img_gb 宽和高纬度上第 11px 到最后的像素点的
R
通道数据为0,同时 img_r 宽和高纬度上从第0到倒数第11个像素点的GB
通道数据为0:# 去GB两通道数据 img_r[:-10, :-10, 1:3] = 0 # 去除R通道数据 img_gb[10:, 10:, 0] = 0
- 将两个矩阵数据错位相加保存到 img_array 中,即 img_r 的
[:-10,:-10,:]
数据与 img_gb 的[10:, 10:, :]
数据相加,赋值给 img_array 的[:-10, :10, :]
:img_array[:-10, :-10, :] = img_r[:-10, :-10, :] + img_gb[10:, 10:, :]
- 从矩阵数据创建
PIL.JpegImagePlugin.JpegImageFile
对象,并保存和显示图片:image = Image.fromarray(img_array) image.save('glasses_douyin.jpg') image.show()
最终得到图片如下:
创建gif动图
上面我们实现了普通图片添加抖音效果,那么想不想看一下错位不同大小的抖音效果的变化呢?可以创建不同错位下的静态抖音效果图,然后用静态图创建gif动图,废话不多说,行动!
库安装
这里创建gif动图,使用到 imageio 库,同样可以使用 pip3 安装:
pip3 install imageio
使用 imageio 模块的 mimsave()
方法生成 gif 动图,比较简单,利用方法封装得到 create_gif
函数:
def create_gif(image_list, gif_name):
'''创建gif图片
:param image_list: 图片名称列表
:type image_list: list
:param gif_name: gif图片路径
:type gif_name: unicode字符串
'''
print(f'\nCreating 「{gif_name}」from {image_list} ...')
frames = []
for image_name in image_list:
frames.append(imageio.imread(image_name))
imageio.mimsave(gif_name, frames, 'GIF', duration = 0.05)
print(f'Saved {gif_name}!')
封装抖音效果添加函数
为了更方便得到不同错位尺寸下的静态抖音效果图,这里封装上面的实现,得到以下函数接口:
def convert_douyin_image(pic_path, offset=10):
'''为普通图片添加红蓝溢出位移效果,抖音app效果
:param pic_path: 图片路径
:type pic_path: unicode字符串
:param offset: 红蓝位移大小,单位像素,默认值是10
:return r_pic_path: 返回转换图片的路径
:rtype: unicode字符串
'''
if os.path.exists(pic_path):
pic_name, extension = os.path.splitext(pic_path)
pic_path_new = pic_name + '_' + str(offset) + extension
img_data = Image.open(pic_path)
img_array = np.array(img_data)
if offset > 0:
img_r = np.copy(img_array)
img_gb = np.copy(img_array)
img_r[:-offset, :-offset, 1:3] = 0
img_gb[offset:, offset:, 0] = 0
img_array[:-offset, :-offset, :] = img_r[:-offset, :-offset, :] + img_gb[offset:, offset:, :]
image = Image.fromarray(img_array)
image.save(pic_path_new)
print(f'Saved {pic_path_new}!')
return pic_path_new
接口中,对 offset 为 0 时做了处理,直接保存原图,最终函数返回保存的图片路径,方便生成图片路径列表用于生成gif。
整体实现
导入必要库:
import os
import imageio
import numpy as np
from PIL import Image
这里定义一个图片路径,方便指定待处理图片:
PIC_PATH = 'glasses.jpg'
最后,先得到错位[0, 2, 4, 6, 8, 10]px 的静态抖音效果图和图片路径列表,为了是动图连贯,处理列表中元素按照错位尺寸为 [0, 2, 4, 6, 8, 10, 8, 6, 4, 2, 0]px,然后根据 PIC_PATH 得到 gif图片保存路径,调用 create_gif 函数生成gif:
if __name__ == '__main__':
pic_list = []
for i in range(0, 11, 2):
pic_path = convert_douyin_image(PIC_PATH, i)
if pic_path:
pic_list.append(pic_path)
temp_l = list.copy(pic_list)
temp_l.reverse()
pic_list = pic_list + temp_l[1:]
p_name, p_ext = os.path.splitext(PIC_PATH)
gif_name = PIC_PATH.replace(p_ext, '.gif')
create_gif(pic_list, gif_name)
最终得到的gif图片效果如下:
完整源码参考:
https://github.com/smslit/tools-with-script/blob/master/douyin/douyin.py