(本篇文章来自小编博客
本篇文章将说一下如何使用python将普通图片处理成抖音效果,甚至制作gif动图。
1. 基本原理
Sketch练习稿之绘制抖音音符 中讲述了抖音效果的原理,领域内可能有人称这种效果为红蓝溢出效果。按照前面文章中的描述,这里以一张图片为例简单再讲一下原理。
一张正常的位图,一般 jpg 图片颜色方案为 RGB,而 png图片除了 RGB色彩通道还有 A通道控制透明,不过色彩配置还是一致的,以手上的图片 glasses.jpg 为例。
去掉 GB 两通道的图片如下:
去掉 R 通道的图片如下:
如果之间将两张图以相加的方式进行混合,就会得到原图,即第一张正常的图片。如果去掉 R 通道的图片在去掉 GB 通道的图片上方,并且向上和向左错开10px,就会得到抖音效果的图片:
2. 实现图片抖音效果
使用python3为图片添加抖音效果
2.1 库安装
需要用到两个库,一个是 pillow 用来读写图片数据,另一个是 numpy 用来处理图片数据转换成的的矩阵数据,使用 pip3 安装即可:
pip3 install pillow
pip3 install numpy
安装完成后,执行 pip3 list 会列出安装的包,检查是否完成安装。
2.2 库的操作
2.2.1 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)
2.2.2 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)
2.3 具体实现导入两个库
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()
最终得到图片如下:
3. 创建gif动图
上面我们实现了普通图片添加抖音效果,那么想不想看一下错位不同大小的抖音效果的变化呢?可以创建不同错位下的静态抖音效果图,然后用静态图创建gif动图,废话不多说,行动!
3.1 库安装
这里创建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}!')
3.2 封装抖音效果添加函数
为了更方便得到不同错位尺寸下的静态抖音效果图,这里封装上面的实现,得到以下函数接口:
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。
3.3 整体实现
导入必要库:
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图片效果如下:
完整源码参考:
4. 参考