将HTML画布导出为图像序列

2 投票
1 回答
2461 浏览
提问于 2025-04-17 10:28

我创建了一个非常简单的页面,它可以处理一个Three.js场景(其实也可以是任何非WebGL的<canvas>动画),并将单独的图像(或者图像序列)导出,之后再转换成视频。

我这样做部分是为了学习Python,但更重要的是,能够快速在Three.js中制作原型,然后导出高分辨率、流畅的视频,这个想法对我来说非常吸引人。以前我用过屏幕录制软件来抓取视频,但总感觉有点笨重,而且实时录制时帧率下降会在最终视频中显现出来。

我现在的流程如下:

JavaScript:

  • 创建WebGL场景并设置渲染循环
  • 设置画布的宽度和高度为所需的尺寸
  • 使用requestAnimationFrame来渲染场景
  • 暂停场景的渲染/更新循环
  • 调用toDataURL()方法获取画布的base64字符串
  • 向Python脚本发送POST请求,传递base64字符串(还有其他信息,比如保存的目标目录和是单张图像还是图像序列)

Python:

  • 去掉MIME头的内容类型并解码base64字符串
  • 将字符串写入图像文件
  • 如果文件写入成功,打印/返回一个表示成功的字符串,否则打印出错误信息

import base64, cgi, cgitb, datetime, glob, re, os

cgitb.enable() #cgitb.enable(display=0, logdir='/tmp') print "Content-type: text/html" print def main(): form = cgi.FieldStorage() saveLocation = "../httpdocs/export/" # POST变量 dataURL = form['dataURL'].value captureSequence = form['captureSequence'].value folderName = saveLocation + form['folderName'].value saveImage(dataURL, captureSequence, saveLocation, folderName) def saveImage(dataURL, captureSequence, saveLocation, folderName): # 去掉MIME内容类型(例如 "data:image/png;base64,") dataURL = dataURL[dataURL.index(','):] decodedString = base64.decodestring(dataURL) if captureSequence == 'true': # 基于 http://www.akeric.com/blog/?p=632 currentImages = glob.glob(folderName + "/*.jpg") # TODO: 文件名是否应该从 %08d+1 开始,而不是 %08d+0? numList = [0] if not os.path.exists(folderName): os.makedirs(folderName) for img in currentImages: i = os.path.splitext(img)[0] try: num = re.findall('[0-9]+$', i)[0] numList.append(int(num)) except IndexError: pass numList = sorted(numList) newNum = numList[-1] + 1 saveName = folderName + '/%08d.jpg' % newNum else: if not os.path.exists(saveLocation): os.makedirs(saveLocation) saveName = saveLocation + datetime.datetime.now().isoformat().replace(':', '.') + '.jpg' # TODO: 而不是返回一个简单的字符串,考虑返回一个对象? try: output = open(saveName, 'w') output.write(decodedString) output.close() print 'true' except Exception, e: print e if __name__ == '__main__': main()

JavaScript:

  • 接收来自Python脚本的响应并显示返回的信息
  • 恢复/更新渲染循环
  • (根据需要重复这个过程,直到达到想要的帧数)

这个过程我会在本地运行,所以不会有写入冲突的风险。

我做了一些快速测试,整体上看是有效的,只是有点慢。


  • 我在这个过程中有没有遗漏什么明显的东西?有什么可以改进的地方吗?(特别是在Python方面)
  • 每张图像单独发一个ajax请求效率低吗?一个好处是我可以随时停止或关闭标签页,这样之前的所有图像都会被保存。把所有的base64字符串存起来,最后一起发送会有好处吗?
  • 由于requestAnimationFrame会将更新循环限制在60fps,是否可以轻松设置一个更低的帧率?比如说出于某种风格原因,我想以15fps更新,唯一的选择是使用setTimeout(callback, 1000 / targetFPS),但这样会随着时间的推移而漂移吗?
  • 接着上面的问题,这个动画有一个变量frame,每次更新循环时递增1。这个变量用于控制动画的不同部分(例如,旋转立方体,并传递给顶点/片段着色器以操控颜色和纹理坐标)。

    如果我想模拟15fps,我是否应该将frame递增(60 / 15)呢?有没有更优雅的方法来轻松切换帧率?

  • 最后,有没有什么技术可以用来提高渲染图像的质量?(我在想,是否可以先保存为双倍大小再缩小?)

我希望这些内容能让你明白,任何见解或建议都将非常感谢。

源文件: http://cl.ly/3V46120C2d3B0A1o3o25(在Mac / Chrome稳定版上测试,需要WebGL)

1 个回答

-3

在EaselJS中,你可以设置每秒帧数(FPS)。

EaselJS Ticker

撰写回答