热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

python自动制作gif并添加文字

python,自动,制作,g
引言

    最近租的房子快到期了,哎,因为去年是第一次找房子租,结果遇到了一个东北黑中介,押一付三,房子有啥问题,灯坏了,下水道堵了,原来签合同的时候说的客气,说是马上就会上门解决,结果实际上我每次找他,都是各种推脱,最后不了了之。全部是我自己想办法解决的。现在又打电话催着我要搬走,押金也不打算退给我了。真的是rlgl。我反对地域黑,但是以后东北的中介一生黑。(再次声明,不是地域黑,我也有认识的人很好的东北的朋友。)今天我又看到了有钱可以为所欲为的经典gif,所以我心血来潮想做一个自己的东北黑中介版的为所欲为gif。本来我想找一个没有文字的gif,然后找一个软件添加文字,然后合成gif的。结果在网上搜索了一下,没有找到合适的原版gif,只找到了一个有文字的gif。到是有软件可以手动添加文字合成gif的,但是如果要首先把原来的文字去除,然后再一点点添加,手动要做大量的工作,很是麻烦。我再尝试了半个小时还没弄好就放弃了(但是我想应该有比较简便的方法,只是我懒得再自己去找了)。最后我想既然作为一个程序员,能不能通过代码去实现这个功能呢?当然可以啦。我最熟悉的是python,自然优先考虑上python了。之前接触过PIL这个库,我记得它有给图片添加文字的功能,而且最近不是才刚刚看了moviepy这个库,它可以很容易合成gif,把这两个结合到一起不就很容易实现我想要的目标了吗?一不做二不休,开搞!

1. 处理gif以及基本思路

    我找到的原始的gif如下图1所示:

o_1cem1dj0ii4s16a22q12hn287a.gif-w.jpg
图1 原始"为所欲为"gif(来自微博)

如果你的电脑已经安装了ffmpeg的话,那么将gif分割成单帧的序列图片是非常容易的,运行下面的命令:

ffmpeg -i 为所欲为.gif %d.jpg 

就可以将为所欲为.gif分割成1.jpg,2.jpg,...。当然你也可以直接使用python的PIL库来处理gif,使用如下的代码即可:

from PIL import Image,ImageSequence gif = Image.open("为所欲为.gif") for i,frame in enumerate(ImageSequence.Iterator(gif),1): frame.save("%d.jpg" % i)

或者你也可以使用之前介绍过的moviepy库(具体可以参考我之前的一篇博客),如果你对opencv比较熟悉的话,自然也可以使用opencv来处理。这里就不详细说了。
    得到gif的帧序列图像之后,简单来说一下后面处理的思路。因为原来的gif有些帧是有文字的,我们要想添加自己的文字,就必须要把原来的文字去除掉,然后换成自己的。所以后面基本的思路就是:

  • 找出那些有文字出现的帧,把文字去除;
  • 原来的文字去除之后,替换成自己的合适的文字,形成新的帧图像;
  • 将新的帧图像合成为新的gif(这里使用moviepy合成)。

2. 实现代码以及简要解析

具体实现代码如下:

# -*- coding:utf-8 -*- import matplotlib.pyplot as plt from PIL import Image,ImageDraw,ImageFont from scipy.misc import imread,imsave,imresize import numpy as np import os from glob import glob from moviepy.editor import ImageSequenceClip # 消除文字的图片序号 modify_img_index = list(range(11,14))+list(range(27,37)) + list(range(44,61)) + list(range(62,81)) \ + list(range(82,94)) + list(range(97,106)) + list(range(112,132)) + list(range(146,168)) text_info = [('好啊',(120,140))]*(14-11) + \ [('就算你今天找人来抗议',(40,140))]*(37-27) + \ [('就算你的理由再完美',(50,140))] *(61-44) + \ [('我想不退钱就不退钱',(50,140))] *(81-62) + \ [('毕竟我是东北黑中介',(50,140))] *(94-82) + \ [('东北黑中介了不起啊',(50,140))] *(106-97) + \ [('sorry,东北黑中介真的可以为所欲为',(5,140))]*(132-112) + \ [('以后让他天天闹',(60,140))]*(158-146) + \ [('天天闹',(110,140))]*(169-158) index_to_text = dict(zip(modify_img_index,text_info)) # 保存图片文件夹 save_dir = "output" if not os.path.exists(save_dir): os.mkdir(save_dir) new_color = np.array([50,50,50]) # 消除文字之后的颜色 fOntsize= 18 fOntcolor= (0,255,255) fps = 10 resize_scale = 1.5 # 新图的宽和高是原来的多少倍 # 消除文字的x,y边界坐标 y_begin = 10 y_end = 280 x_begin = 140 x_end = 160 # 全部文件列表(无序) img_names = glob("*.jpg") #print(img_names) def modify(): # 遍历每一个图像 for img_name in img_names: img = imread(img_name) h,w = img.shape[0],img.shape[1] #plt.imshow(img) img_new = np.copy(img) index = int(img_name.split(".")[0]) # 如果图片有文字,需要修改 if index in modify_img_index: # 消除文字 img_new[x_begin:x_end,y_begin:y_end] = new_color text,pos = index_to_text[index] img_new = drawtext(Image.fromarray(img_new),text,pos,fontsize,fontcolor) #保存新的图片 # imsave(os.path.join(save_dir,img_name),img_new) if resize_scale != 1: # resize img_new = img_new.resize((int(w*resize_scale),int(h*resize_scale))) img_new.save(os.path.join(save_dir,img_name)) print("Modify and save %s!" % img_name) else: # 没有文字,不需要修改,直接保存原来的图片 if resize_scale != 1: img = imresize(img,resize_scale) imsave(os.path.join(save_dir,img_name),img) print("Save %s!" % img_name) def drawtext(img,text,pos,fontsize,fontcolor): ''' draw text for img @param img:image to draw text @param text:text to draw @param pos:where to draw the text((x,y)) @param fontsize: font size @param fontcolor: font color ''' fOnt= ImageFont.truetype("simsun.ttc",fontsize) draw = ImageDraw.Draw(img) draw.text(pos,text,fontcolor,fOnt= font) # plt.imshow(img) # plt.show() return img def test_drawtext(): img_name = "4.jpg" img = Image.open(img_name) #text = "好啊" text = "东北黑中介了不起啊" pos = (40,140) fOntsize= 20 fOntcolor= (0,255,255) drawtext(img,text,pos,fontsize,fontcolor) # 利用图像序列生成gif def generate_gif(img_names,save_path,fps = 10): # img_names:list of image names clip = ImageSequenceClip(img_names,fps = fps) clip.write_gif(save_path) print("Write %s successfully!" % save_path) # 主函数 def main(): modify() save_gif_path = os.path.join(save_dir,"东北黑中介.gif") img_names = sorted(glob(os.path.join(save_dir,"*.jpg")),key = lambda x: int(x.split(".")[0].split("/")[1])) generate_gif(img_names,save_gif_path,fps) #test_drawtext() if __name__ == '__main__': main()

上面的代码思路其实还是比较清楚的。主要麻烦的地方就在于,我们没法自动确认哪些帧出现了文字,所以在这里我采用手动观察图像(有167张图)的方式,确定了出现文字的帧序列区间;然后对于这些帧序列区间,其文字替换成我自己的文字。还有一个问题就是,我要消除文字,就要知道文字的位置,其实这个可以归结为一个目标检测的问题,或者说是一个OCR问题(OCR的第一步,定位文字),但是OCR做起来还是很麻烦的,这里暂时不说,后面有空我会专门讨论OCR问题。这里比较好的地方在于,所有的文字出现的高度位置几乎是一样的,只是宽度方向的位置不一样(有的字多,有的字少),这里我还是采用手动观察标注的办法,可以借助于matplotlib显示图像,用鼠标点击图像的某一点,它会显示该点的(x,y)坐标和像素值,这样我们就可以手动确定要消除的文字的位置,以及消除文字要填充的颜色了。按照这个思路,我们可以得到一些消除原来文字,添加新文字之后的图像如下图2所示:

o_1cem488ad1tkk3pe16pb7jn10lva.jpg-w.jpg

图2 原始帧和消除文字后添加新文字的帧(上是原始帧,下是处理之后的新帧)
    最终合成的新的gif如下图3所示:
o_1cem4hohk1nkc1r7f1rkt37h1pu3a.gif-w.jp

图3 最终合成的gif

3. 小结

    本文主要利用python的PIL库(用于处理图像,添加文字等)和moviepy(用于生成gif)自动生成了gif并添加了文字。但是还有一些问题,比如文字自动定位并识别没有解决。这些问题留到后面介绍OCR技术的时候再和大家介绍。如果只是简单地尝试合成gif,大家不妨自己动手试一试。

全文完,感谢阅读! 热爱编程,热爱机器学习! github:http://www.github.com/Lyrichu github blog:http://Lyrichu.github.io 个人博客站点:http://www.movieb2b.com(不再维护)
推荐阅读
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • 第四章高阶函数(参数传递、高阶函数、lambda表达式)(python进阶)的讲解和应用
    本文主要讲解了第四章高阶函数(参数传递、高阶函数、lambda表达式)的相关知识,包括函数参数传递机制和赋值机制、引用传递的概念和应用、默认参数的定义和使用等内容。同时介绍了高阶函数和lambda表达式的概念,并给出了一些实例代码进行演示。对于想要进一步提升python编程能力的读者来说,本文将是一个不错的学习资料。 ... [详细]
  • 基于dlib的人脸68特征点提取(眨眼张嘴检测)python版本
    文章目录引言开发环境和库流程设计张嘴和闭眼的检测引言(1)利用Dlib官方训练好的模型“shape_predictor_68_face_landmarks.dat”进行68个点标定 ... [详细]
  • Python爬虫中使用正则表达式的方法和注意事项
    本文介绍了在Python爬虫中使用正则表达式的方法和注意事项。首先解释了爬虫的四个主要步骤,并强调了正则表达式在数据处理中的重要性。然后详细介绍了正则表达式的概念和用法,包括检索、替换和过滤文本的功能。同时提到了re模块是Python内置的用于处理正则表达式的模块,并给出了使用正则表达式时需要注意的特殊字符转义和原始字符串的用法。通过本文的学习,读者可以掌握在Python爬虫中使用正则表达式的技巧和方法。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程
    本文介绍了关于人工智能、神经网络和深度学习的知识点,并提供了YOLOv7基于自己的数据集从零构建模型完整训练、推理计算的详细教程。文章还提到了郑州最低生活保障的话题。对于从事目标检测任务的人来说,YOLO是一个熟悉的模型。文章还提到了yolov4和yolov6的相关内容,以及选择模型的优化思路。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Python如何调用类里面的方法
    本文介绍了在Python中调用同一个类中的方法需要加上self参数,并且规范写法要求每个函数的第一个参数都为self。同时还介绍了如何调用另一个类中的方法。详细内容请阅读剩余部分。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 本文介绍了如何使用python从列表中删除所有的零,并将结果以列表形式输出,同时提供了示例格式。 ... [详细]
  • 本文介绍了在使用Python中的aiohttp模块模拟服务器时出现的连接失败问题,并提供了相应的解决方法。文章中详细说明了出错的代码以及相关的软件版本和环境信息,同时也提到了相关的警告信息和函数的替代方案。通过阅读本文,读者可以了解到如何解决Python连接服务器失败的问题,并对aiohttp模块有更深入的了解。 ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
author-avatar
Healthcen健康
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有