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

Mat*_*son 2 javascript canvas linear-algebra three.js typescript

首先,我试图实现的视觉示例:

例子

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

简短的 (tl;dr) 问题

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

关于我正在努力实现的目标的背景

我正在尝试构建一个网页,其工作方式如下:

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

我正在努力解决的部分是第 4 步。为了确保我只处理视频每一帧图像的相关部分,重要的是我“扭曲”图像,以便它只显示“电视屏幕”区域,而不是整个网络摄像头图片。

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

  • 这需要某种透视变换,并且由于网络摄像头可以处于任何角度并且我们不处理平行线,因此需要 3 维变换,而 2D 是不够的。这是因为 2D 变换(平移/旋转/缩放/倾斜)将无法处理收敛的边。
  • HTML5 画布是二维上下文,因此只能支持 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/

加起来

我真的很感激任何帮助解决为什么这不像我预期的那样工作,或者是否有更好/更容易的方法来实现我需要的任何指示。

Mat*_*son 5

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

我能够通过改变它来使它工作:

transformMatrix.set(a1, a2, a3, 0, 
                    b1, b2, b3, 0, 
                    c1, c2, c3, 0, 
                    0,  0,  0,  1);
Run Code Online (Sandbox Code Playgroud)

对此:

transformMatrix.set(a1, a2, 0, a3, 
                    b1, b2, 0, b3, 
                    0,  0,  0, 1, 
                    c1, c2, 0, c3);
Run Code Online (Sandbox Code Playgroud)

Math StackExchange 上的这个答案有助于解决这个问题。

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