Jeffrey Cross
Jeffrey Cross

用加工和Kinect制作激光切割Zoetrope

此代码框向您展示如何使用Processing,Kinect和激光切割器创建物理zoetrope。草图将加载到从Kinect深度相机录制的电影中,使用Processing OpenCV库将该电影转换为一系列轮廓,并将矢量形式的轮廓保存为可以发送到激光切割机的DXF文件。我还将解释观察器机制的设计,使zoetrope旋转。

关于Zoetropes

在跳入代码之前,有点文化历史和灵感。 zoetrope是一种流行的维多利亚式光学玩具,它从纸圈中产生了一种动画形式。在圆圈上,zoetrope制作者将从动画中打印出一系列帧。然后圆圈将被一个不透明的圆盘包围,其中有一系列切口。当纸圈旋转时,观察者将通过狭缝观察它并看到动画。狭缝像电影放映机一样,让观众一次只能看到一帧快速连续,导致运动错觉。

最近,人们已经开始弄清楚如何用三维物体实现相同的幻觉。艺术家格雷戈里巴萨米安(Gregory Barsamian)制造的雕塑在频闪灯前旋转,以产生运动的幻觉。雕塑由不同运动阶段的一系列不同物体组成,闪光灯就像zoetrope中的狭缝一样,创造出运动的幻觉(巴萨米亚人可能会熟悉一些粉丝来自我们早期的报道:Gregory Barsamian的视觉坚持) 。

Pixar最近选择了为他们的大厅创建一个物理zoetrope的技巧。那里的动画师确信物理zoetrope是动画原理的无与伦比的演示:将一系列静止图像转换为移动图像:

那么,物理zoetrope的配方是什么?我们需要一系列代表运动中连续阶段的图像。然后我们需要将它们转换为不同的物理对象。一旦我们得到这些,我们需要一种可以旋转它们的机制。最后但并非最不重要的是,我们需要一个闪光灯将每个对象“冻结”成一个动画帧。

我们怎么能自己做?好吧,为了获得这一系列对象,我们将从Kinect生成的一段视频中提取剪影。然后我们将这些轮廓转换为矢量文件,我们可以使用它来控制激光切割机,以切割出动画每帧形状的一系列丙烯酸物体。

让我们潜入。

录制深度电影

首先要下载Dan Shiffman的Kinect库进行处理,并将其放入Processing libraries文件夹中。如果你不熟悉如何做到这一点,Dan在Kinect库页面上得到了非常明确的指示。

我们将使用这个库来录制Kinect的深度电影。 (理论上,你可能也可以使用传统的相机和光线充足的房间,但那会有什么乐趣?)值得庆幸的是,录制自己的深度电影只需几行代码即可离开Kinect示例使用Processing库:

讨论

让我们来谈谈它是如何工作的。首先,我们包括Kinect库和Processing视频库;我们以后需要它才能录制电影。然后,我们声明Kinect和MovieMaker对象。 MovieMaker是将草图输出录制到电影文件中的对象。

在设置中,我们将帧速率设置为24,以便它与我们录制的电影相匹配。我们还将草图配置为640 x 480,以匹配将从Kinect进入的视频图像的大小。我们做了一些基本的Kinect设置:告诉我们的kinect对象开始从设备读取数据并启用深度图像。然后我们初始化MovieMaker类,为其提供质量设置,文件类型和文件名。您可以在Processing文档中阅读有关MovieMaker如何工作的更多信息。重要的是我们传递给MovieMaker的帧速率与素描的帧速率匹配,以便我们的电影以正确的速度播放。

我们的绘图功能非常简单。我们所做的只是调用kinect.getDepthImage()并使用Processing image()函数将其输出绘制到我们的草图中。这将向我们展示表示Kinect从场景中提取的深度图的灰度图像。这将是一个黑白图像,其中每个像素的灰色颜色不对应于对象的光的颜色,而是对应于距离Kinect有多远。更近的物体将具有更亮的像素,而更远的物体将更暗。之后,我们将能够处理这些像素,以便为我们的轮廓挑选特定深度的物体。

现在我们已经在屏幕上绘制了深度图像,我们所要做的就是将结果捕获到我们正在录制的电影的新帧中(mm.addFrame())。草图的最后一个重要细节是我们使用关键事件为自己提供了一种停止和完成电影的方法。当有人按空格键时,电影将停止录制并保存文件。此外,我们必须记得在退出时进行一些Kinect清理,否则每当我们停止草图时我们都会遇到一些时髦的错误。

以下是使用此草图录制的电影的示例:

现在,如果您没有Kinect,或者您在录制深度电影时遇到问题,请不要绝望!您仍然可以继续玩下一步。你可以直接从Vimeo下载我直接跳跃插孔的深度电影:激光Zoetrope的Kinect测试电影。我还上传了我用于上面显示的最终激光zoetrope的深度电影,如果你想完全按照:Kinect深度测试电影。后来的电影中有Zach Lieberman,他是纽约的艺术家和黑客,也是OpenFrameworks的联合创始人之一,OpenFrameworks是一个基于C ++的Processing of cousin。

创建激光切割机文件

现在我们已经有了深度电影,我们需要编写另一个处理草图来处理该电影,为我们的动画选择帧,找到我们图形的轮廓,并保存出我们可以发送到激光切割机的矢量文件。

为了完成这些,我们将使用Processing OpenCV库和Processing的内置beginRaw()函数。创建一个新的Processing sketch,保存它,在sketch文件夹中创建一个“data”文件夹,将深度影片移动到那里(名为“test_movie.mov”),然后将以下源代码粘贴到草图中(或从lasercut_zoetrope_generator.pde文件):

讨论

如果你使用上面链接的第二个测试影片运行此草图,它将产生以下输出:

...还将在sketch文件夹中保存名为“full_output.dxf”的文件。这是我们可以带入Illustrator或任何其他设计程序的矢量文件,用于最终处理以发送到激光切割机。

现在,我们来看看代码。

在设置中,我们将test_movie.mov文件加载到OpenCV中,这应该是OpenCV过去帖子中应该熟悉的。我们还调用beginRaw(),一个用于创建矢量文件的Processing函数。 beginRaw()将使我们的草图将其所有输出记录到一个新的矢量文件中,直到我们调用endRaw(),这样我们就可以在绘制循环的多次迭代中构建我们的文件。在这种情况下,我们创建的是DXF文件而不是PDF,因为这种格式更容易处理需要连续线的激光器以产生可靠的输出。由Processing生产的PDF往往有许多离散的线段,当用激光切割时会产生时髦的结果,包括较慢的作业和不均匀的厚度。

现在,在我们深入了解draw方法之前,先了解一下方法。我们想要从我们的电影中抽出12个不同的帧,这将为我们的动画制作好帧。然后我们想让OpenCV提取它们的轮廓(或OpenCV用语中的“轮廓”),最后我们想要在屏幕上绘制那些它们不会重叠的最终DXF文件将包含所有框架。动画。

该草图通过创建在绘制循环外定义的“currentFrame”变量来解决这些问题。然后,在绘制循环的每次运行中,该变量递增并且我们使用它来做我们需要的一切:在电影中向前跳,移动到草图的不同区域以绘制,等等。最后,一旦我们已经完成了所有12帧到屏幕的绘制,我们称之为“endRaw()”来完成DXF文件,就像我们在第一个草图中调用“mm.finish()”来关闭电影文件一样。

那么,考虑到整体结构,我们如何绘制每个框架的轮廓?我们来看看代码:

opencv.jump(0.3 + map(currentFrame * timeBetweenFrames,0,9,0,1)); opencv.read();

这告诉OpenCV在电影中向前跳过一段特定的时间。 0.3是我们要抓住的帧的起点,是我通过猜测和检查得出的。我尝试了一堆不同的值,每次都运行草图并查看我最终得到的帧并判断它们是否能制作出好的动画。 “0.3”表示以秒为单位的开始时间。

我们希望所有的帧都是均匀间隔的,这样我们的动画就可以干净利落地播放。为了达到这个目的,我们根据我们所在的帧增加了0.3的跳跃量。一旦我们计算出合适的时间,我们就会使用“opencv.read()”来阅读电影的帧。

接下来的几行使用modulo运算符(“%”)和currentFrame数字,以便以四乘三的网格绘制帧。然后,有一个看起来简单的OpenCV调用,实际上考虑到上下文非常酷:

opencv.threshold(150);

这告诉我们的opencv对象将框架展平为纯黑白图像,消除所有灰度。它根据我们传入的灰度值确定要保留哪些部分,150。但是由于深度图像中的灰度值对应于物体的实际物理距离,实际上这意味着我们已经消除了图像中的任何物体。超过几英尺,只留下我们的主题在图像中被隔离。

如果您正在使用自己的深度图像,则需要在此处尝试不同的值,直到您看到一个轮廓,该轮廓仅表示您想要在动画中捕获的图形。

在调用“pushMatrix()”和“popMatrix()”之间包含的下几行可能是草图中最令人困惑的。值得庆幸的是,我们可以将它们分成两部分来理解它们:移动和缩放我们的图像并绘制由OpenCV计算的轮廓。

本节的前三行不会做任何事情,只会改变我们的参考框架。 pushMatrix()和popMatrix()是一个奇怪命名的约定,使复杂的绘图代码变得更加容易。它让我们做的是暂时改变Processing sketch的大小和形状,以便我们可以反复使用相同的绘图代码在不同的比例和屏幕的不同部分进行绘制。

pushMatrix(); translate(x + 20,y);规模(0.2);

这是它的工作原理。首先我们调用pushMatrix(),这意味着:“保存我们的位置”,这样我们可以在调用popMatrix()时跳回到它。然后我们调用“translate()”,它根据我们当前的帧使用我们在上面设置的x和y变量将我们移动到草图的不同部分。然后我们称之为“scale()”,这样我们绘制的任何其他东西,直到下一个popMatrix()将是通常的20%。

这三行的结果是我们可以完成接下来的OpenCV部分 - 计算和绘制轮廓 - 而不必考虑屏幕在哪里发生。如果没有pushMatrix,我们必须将x和y值添加到我们的所有坐标中,并将所有大小乘以0.2。这使事情变得更加简单。

现在,OpenCV代码:

Blob [] blobs = opencv.blobs(1,width * height / 2,100,true,OpenCV.MAX_VERTICES * 4); for(int i = 0; i

这段代码看起来确实很复杂,但并不是那么糟糕。最有趣的一行是第一行,它叫做“opencv.blobs()”。该函数分析我们存储的图像并查找连续的区域,其中所有相邻像素是相同的颜色。在我们的示例电影的情况下,将会有一个blob,它将围绕Zach的轮廓。我们对阈值的使用消除了场景中的所有其他内容。如果你正在使用我的其他示例电影或你自己的深度电影,你可能有多个blob,没关系,你最终会得到一个更复杂的矢量文件。

一旦我们了解它,绘制这些blob也不是太糟糕。我们遍历它们的数组,每个blob都有一个我们访问的点数组,以便创建向量。基本上,我们正在玩连接点:从每个点到它们之间的下一个绘制线,直到我们完成整个形状。

这就是生成DXF文件的全部内容。

准备激光

生成此DXF文件后,您需要将其带入Illustrator或您喜欢的矢量编辑程序以执行一些基本清理:将每个帧组合成一个对象,剪切出与矩形重叠的轮廓部分,以便数字实际上将附加到它的基础等。我还选择了这12个帧中的9个,然后复制它们以便我有一个循环动画而不是一个重置回起始姿势的动画。我已经在这里上传了最终的Illustrator文件供您查看:contour_animation_for_laser.ai

一旦我们得到轮廓切口,最后一步是设计并切割它们将旋转的轮子。我获得了一个推力轴承(一种工程师的懒惰苏珊),可以让我的光盘自由旋转。我的轴承在顶部有孔,用于将物品固定在其上。我测量了它们之间的距离,然后将光盘设计放在一起,可以安装到轴承上并保持动画的每个帧:

为插槽获得合适的尺寸,使剪影在没有任何胶水的情况下紧密贴合,需要进行一些实验,并在激光上进行一些错误的启动。您可以在此处下载此设计的Illustrator文件:contour_disc_for_laser.ai

一旦你有了这两个Illustrator文件,在激光上切割它们就像打印一样容易。字面意思:你实际上是通过在Illustrator中打印来开始这个过程的。你必须填写一些关于激光器功率和速度设置的更多细节,但是你要参加比赛了。激光看起来像这样(在这种情况下不切割zoetrope部分,但它是相同的激光):

希望本教程为您提供足够的内容,以便您开始记录Kinect深度数据并使用它来生成可激光切割的矢量文件。玩得开心!

获得自己的激光切割Zoetropes!

为了回应这个项目的所有重大反应,我开始建立一个实际生产激光切割zoetropes的网站:PhysicalGIF.com。我们提供的套件可以将来自设计师制作的动画GIF的zoetrope放在一起。这些套件将提供组装像这里所示的zoetrope所需的一切:激光切割部件,底座,甚至频闪灯。最终,您甚至可以上传自己的GIF,将它们转换为物理形式。现在去那里注册,以便在工具包可用时收到通知。

更多:查看所有Codebox专栏请访问我们的Make:Arduino页面,了解更多有关此流行爱好微控制器的信息

在Maker Shed中:

处理入门使用Processing学习计算机编程的简单方法,这是一种简单的语言,可让您使用代码创建绘图,动画和交互式图形。编程课程通常从理论开始,但本书可以让您直接进入创造性和有趣的项目。它是任何想要学习基本编程的人的理想选择,并且可以为具有一些编程技能的人提供简单的图形介绍。

分享

发表评论