如何截取屏幕并生成GIF动画

来源:互联网 发布:什么软件可以剪辑视频 编辑:程序博客网 时间:2024/06/14 06:41

    前面在写关于VPython的文章的时候,我考虑了一下如何把VPython的动作记录下来,存为GIF动画再放到博客上来.
    首先,在维基百科找到关于GIF文件格式的描述,由于该部分占用篇幅太多,我就不贴出来了,大家可以查看这里:

    http://en.wikipedia.org/wiki/Graphics_Interchange_Format

    有了上面的信息,就可以编写Python程序了,PIL里面有个类ImageGrab可以轻易地截取屏幕.另外,python的写文件时,可以直接写入字符串和十六进制数,非常方便.

    以下是整个程序源代码:

[python] viewplaincopy

  1. # -*- coding: cp936 -*-  
  2. import ImageGrab # from PIL  
  3. import time  
  4. import string  
  5. from PIL import Image, ImageChops  
  6. from PIL.GifImagePlugin import getheader, getdata  
  7. import os  
  8. import numpy as np  
  9.   
  10.   
  11. def intToBin(i):  
  12.     """ 把整型数转换为双字节 """  
  13.     # 先分成两部分,高8位和低8位  
  14.     i1 = i % 256  
  15.     i2 = int( i/256)  
  16.     # 合成小端对齐的字符串  
  17.     return chr(i1) + chr(i2)  
  18. def getheaderAnim(im):  
  19.     """ 生成动画文件头 """  
  20.     bb = "GIF89a"  
  21.     bb += intToBin(im.size[0])  
  22.     bb += intToBin(im.size[1])  
  23.     bb += "\x87\x00\x00"  #使用全局颜色表  
  24.     return bb  
  25. def getAppExt(loops=0):  
  26.     """ 应用扩展,默认为0,为0是表示动画是永不停止 
  27.     """  
  28.     bb = "\x21\xFF\x0B"  # application extension  
  29.     bb += "NETSCAPE2.0"  
  30.     bb += "\x03\x01"  
  31.     if loops == 0:  
  32.         loops = 2**16-1  
  33.     bb += intToBin(loops)  
  34.     bb += '\x00'  # end  
  35.     return bb  
  36.   
  37.   
  38. def getGraphicsControlExt(duration=0.1):  
  39.     """ 设置动画时间间隔 """  
  40.     bb = '\x21\xF9\x04'  
  41.     bb += '\x08'  # no transparancy  
  42.     bb += intToBin( int(duration*100) ) # in 100th of seconds  
  43.     bb += '\x00'  # no transparant color  
  44.     bb += '\x00'  # end  
  45.     return bb  
  46. def _writeGifToFile(fp, images, durations, loops):  
  47.     """ 把一系列图像转换为字节并存入文件流中 
  48.     """  
  49.     # 初始化  
  50.     frames = 0  
  51.     previous = None  
  52.     for im in images:  
  53.         if not previous:  
  54.             # 第一个图像  
  55.             # 获取相关数据  
  56.             palette = getheader(im)[1]  #取第一个图像的调色板  
  57.             data = getdata(im)  
  58.             imdes, data = data[0], data[1:]              
  59.             header = getheaderAnim(im)  
  60.             appext = getAppExt(loops)  
  61.             graphext = getGraphicsControlExt(durations[0])  
  62.               
  63.             # 写入全局头  
  64.             fp.write(header)  
  65.             fp.write(palette)  
  66.             fp.write(appext)  
  67.               
  68.             # 写入图像  
  69.             fp.write(graphext)  
  70.             fp.write(imdes)  
  71.             for d in data:  
  72.                 fp.write(d)  
  73.               
  74.         else:  
  75.             # 获取相关数据            
  76.             data = getdata(im)   
  77.             imdes, data = data[0], data[1:]         
  78.             graphext = getGraphicsControlExt(durations[frames])  
  79.               
  80.             # 写入图像  
  81.             fp.write(graphext)  
  82.             fp.write(imdes)  
  83.             for d in data:  
  84.                 fp.write(d)     
  85.         # 准备下一个回合  
  86.         previous = im.copy()          
  87.         frames = frames + 1  
  88.   
  89.     fp.write(";")  # 写入完成  
  90.     return frames  
  91. def writeGif(filename, images, duration=0.1, loops=0, dither=1):  
  92.     """ writeGif(filename, images, duration=0.1, loops=0, dither=1) 
  93.     从输入的图像序列中创建GIF动画 
  94.     images 是一个PIL Image [] 或者 Numpy Array 
  95.     """  
  96.     images2 = []  
  97.     # 先把图像转换为PIL格式  
  98.     for im in images:  
  99.           
  100.         if isinstance(im,Image.Image): #如果是PIL Image  
  101.             images2.append( im.convert('P',dither=dither) )  
  102.               
  103.         elif np and isinstance(im, np.ndarray): #如果是Numpy格式  
  104.             if im.dtype == np.uint8:  
  105.                 pass  
  106.             elif im.dtype in [np.float32, np.float64]:  
  107.                 im = (im*255).astype(np.uint8)  
  108.             else:  
  109.                 im = im.astype(np.uint8)  
  110.             # 转换  
  111.             if len(im.shape)==3 and im.shape[2]==3:  
  112.                 im = Image.fromarray(im,'RGB').convert('P',dither=dither)  
  113.             elif len(im.shape)==2:  
  114.                 im = Image.fromarray(im,'L').convert('P',dither=dither)  
  115.             else:  
  116.                 raise ValueError("图像格式不正确")  
  117.             images2.append(im)  
  118.               
  119.         else:  
  120.             raise ValueError("未知图像格式")  
  121.       
  122.     # 检查动画播放时间  
  123.     durations = [duration for im in images2]  
  124.     # 打开文件  
  125.     fp = open(filename, 'wb')  
  126.     # 写入GIF  
  127.     try:  
  128.         n = _writeGifToFile(fp, images2, durations, loops)  
  129.         print n, '帧图像已经写入'  
  130.     finally:  
  131.         fp.close()  
  132. seq = []  
  133. steps = float(raw_input("输入录制间隔(单位:秒):"))  
  134. x = raw_input("按下任意键开始录制...")  
  135. os.system("mkdir images")  #创建一个文件夹,用于存放临时文件  
  136. for i in range(10):  
  137.     time.sleep(steps)  
  138.     im = ImageGrab.grab((0,0,320,245)) #捕捉屏幕的左上角(0,0,320,245)部分  
  139.     #这里由于ImageGrab.grab返回的图像格式未知,只能使用笨办法,每次存盘再打开,格式就正确了  
  140.     im.save("images\\test" +str(i) + ".gif") #图像存入  
  141.     seq.append(Image.open("images\\test" +str(i) + ".gif")) #重新打开  
  142.     print "采集了第" + str(i) + "帧图像"  
  143. print "屏幕捕捉完成"  
  144. writeGif('output.gif',seq, duration=0.5, dither=0)  
  145. x = raw_input("按下任意键退出")  

    使用方法: 
    1,运行程序,弹出"输入录制间隔(单位:秒):", 输入你希望的间隔,比如0.5秒.
    2,按下任意键开始录制
    3,捕捉完成后,按下任意键结束.
    4,最终的GIF文件将保存为当前目录下的output.gif

    程序输出:

    

输入录制间隔(单位:秒):0.1
按下任意键开始录制...
采集了第0帧图像
采集了第1帧图像
采集了第2帧图像
采集了第3帧图像
采集了第4帧图像
采集了第5帧图像
采集了第6帧图像
采集了第7帧图像
采集了第8帧图像
采集了第9帧图像
屏幕捕捉完成
10 帧图像已经写入
按下任意键退出


引自:http://blog.csdn.net/hackjames/article/details/6950837