如何使用HTML5 canvas和three.js实现四点透视变换?

时间:2019-01-01 18:39:11

标签: javascript typescript canvas three.js linear-algebra

首先,是我要实现的目标的直观示例:

example

(照片来源:https://unsplash.com/photos/pGcqw1ARGyg

简短(tl; dr)问题

使用HTML5视频和画布,如何执行4点透视变换,以便仅在画布中渲染帧的“电视屏幕”部分?为什么my implementation没有显示正确的区域?

关于我要实现的目标的背景

我正在尝试构建一个如下所示的网页:

  1. 用户将网络摄像头对准电视,使其处于框架中的某个位置(但可能成任何角度)
  2. 使用HTML5视频和画布,可在网页上捕获并预览网络摄像头
  3. 用户能够(通过单击预览)定义电视屏幕的四个角的位置(4对x / y坐标)
  4. **视频发生了变形(使用某种透视变换),因此画布仅显示其实际电视屏幕的图像部分(而不是整个网络摄像头视图)** < / li>
  5. 然后对图像执行某些处理(例如,识别最突出的颜色)。除了指出我希望能够最后访问HTML5画布的内容/像素之外,这部分内容不在此问题的范围内。

我苦苦挣扎的部分是第4步。为了确保我只处理视频每一帧的图像的相关部分,重要的是我将图像“扭曲”为仅显示“电视屏幕”区域,而不显示整个网络摄像头图片。

做了一些阅读,我的理解是:

  • 这需要某种透视变换,并且由于网络摄像头可以处于任何角度,并且我们不处理平行线,因此需要进行3维变换,而2D不够。这是因为2D变换(平移/旋转/缩放/倾斜)将无法处理会聚的边。
  • HTML 5 canvas是一个二维上下文,因此只能支持2D转换,而不能支持3D转换。因为我需要一种适用于canvas的解决方案,所以我不能简单地使用3D CSS转换(例如https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/matrix3d)。这表明也许WebGL更适合我处理3D方面的事情。

到目前为止我尝试过的事情

考虑到这一点,我尝试了以下方法:

a)使用video标签捕获网络摄像头

b)使用three.js,创建一个渲染到canvas元素中的3D场景(以便我可以对生成的画布内容执行图像处理)

c)three.js场景包括:   -包含一个扁平网格,其中使用VideoTexture在一侧显示视频。   -透视摄像头,最初放置时可以显示整个摄像头图像

d)允许用户单击四个角点来定义他们的电视在哪里,确定x / y坐标是什么并保存它们

e)计算透视变换,该变换将“拉伸”出图像,以便正确的区域“填满帧”。换句话说,将四个单击的“电视角”点拉伸到视口的四个角。我一直在使用这个库:https://github.com/jlouthan/perspective-transform来计算这个。

f)我的想法是,如果将适当的变换应用于包含视频的网格,并且相机保持在固定位置,那么当以2D方式查看时,输出画布将包含所需的图像。

链接到我当前(中断)的实现

这是我目前在上面所做的尝试的链接。它显示了视频,并允许您单击四个角。如果单击原点(在中心)周围的点,这似乎可行,但是问题是,如果您选择图像中其他位置的区域,则会显示错误的区域。

https://bitbucket.org/mattwilson1024/perspective-transform/src/master/

总结

我非常感谢您提供任何帮助,弄清为什么它无法按我的预期工作,或者对于是否有更好/更轻松的方法来实现我所需要的任何指示。

1 个答案:

答案 0 :(得分:1)

原始实现的问题在于创建transformMatrix的方式。

我可以通过更改以下方法使其起作用:

transformMatrix.set(a1, a2, a3, 0, 
                    b1, b2, b3, 0, 
                    c1, c2, c3, 0, 
                    0,  0,  0,  1);

对此:

transformMatrix.set(a1, a2, 0, a3, 
                    b1, b2, 0, b3, 
                    0,  0,  0, 1, 
                    c1, c2, 0, c3);

This answer on the Math StackExchange对于解决此问题很有帮助。

为了将来任何人发现该问题的利益,我更新了原始问题,使其指向包含损坏代码的存档分支。可以找到有效的版本here