如何使用HTML5 canvas & three.js实现4点透视变换?
How to implement 4-point perspective transform using HTML5 canvas & three.js?
首先,我要实现的目标的直观示例:
(图片来源:https://unsplash.com/photos/pGcqw1ARGyg)
简短的 (tl;dr) 问题
使用 HTML5 视频和 canvas,如何执行 4 点透视变换以便我可以只渲染 [=107] 中帧的 "TV screen" 部分=]?为什么 my implementation 显示的区域不正确?
关于我正在努力实现的目标的背景
我正在尝试构建一个网页,其工作方式如下:
- 用户将他们的网络摄像头指向电视,使其位于画面中的某个位置(但可能处于任何角度)
- 使用 HTML5 视频 & canvas,网络摄像头被捕获并在网页上预览
- 用户可以定义(通过点击预览)电视屏幕的 4 个角(4 对 x/y 坐标)
- ** 视频被扭曲(使用某种透视变换),因此 canvas 仅 显示其实际电视屏幕的图像部分(不是整个网络摄像头视图)**
- 然后对图像执行一些处理(例如,识别最突出的颜色)。这部分超出了这个问题的范围,除了指出我希望能够在最后访问 HTML5 canvas 的 content/pixels。
我遇到困难的部分是第 4 步。为了确保我只处理视频每一帧图像的相关部分,重要的是我 "warp" 图像所以它只显示 "TV screen" 区域而不是整个网络摄像头图片。
经过一些阅读,我的理解是:
- 这需要某种透视变换,并且由于网络摄像头可以处于任何角度并且我们不处理平行线,因此需要 3 维变换,而 2D 是不够的。这是因为二维变换 (translate/rotate/scale/skew) 无法处理会聚边。
- HTML5 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 场景(这样我就可以对生成的 canvas 内容执行我的图像处理)
c) three.js 场景包括:
- 包含使用 VideoTexture
在一侧显示视频的平面网格。
- 一个透视相机,最初定位为显示整个网络摄像头图像
d) 允许用户单击四个角点来定义他们的电视位置,计算出 x/y 坐标并保存它们
e) 计算一个透视变换,它将 "stretch" 图像输出,以便正确的区域 "fills the frame"。换句话说,将单击的四个 "TV corner" 点拉伸到视口的四个角。我一直在使用这个库:https://github.com/jlouthan/perspective-transform 来计算这个。
f) 我的想法是,如果对包含视频的网格应用适当的变换,并且摄像机保持在固定位置,那么输出 canvas 将包含观看时所需的图像它是二维的。
Link 到我当前的(损坏的)实现
这是我目前对上述内容的尝试 link。它显示视频并允许您单击四个角。如果你点击原点周围的点(在中心)似乎它有效,但问题是如果你选择图像中其他地方的区域,它会显示错误的区域。
https://bitbucket.org/mattwilson1024/perspective-transform/src/master/
总结
我非常感谢任何帮助解决为什么这不像我预期的那样工作,或者任何关于是否有 better/easier 方法来实现我的目标的指示需要。
原始实现的问题在于 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.
首先,我要实现的目标的直观示例:
(图片来源:https://unsplash.com/photos/pGcqw1ARGyg)
简短的 (tl;dr) 问题
使用 HTML5 视频和 canvas,如何执行 4 点透视变换以便我可以只渲染 [=107] 中帧的 "TV screen" 部分=]?为什么 my implementation 显示的区域不正确?
关于我正在努力实现的目标的背景
我正在尝试构建一个网页,其工作方式如下:
- 用户将他们的网络摄像头指向电视,使其位于画面中的某个位置(但可能处于任何角度)
- 使用 HTML5 视频 & canvas,网络摄像头被捕获并在网页上预览
- 用户可以定义(通过点击预览)电视屏幕的 4 个角(4 对 x/y 坐标)
- ** 视频被扭曲(使用某种透视变换),因此 canvas 仅 显示其实际电视屏幕的图像部分(不是整个网络摄像头视图)**
- 然后对图像执行一些处理(例如,识别最突出的颜色)。这部分超出了这个问题的范围,除了指出我希望能够在最后访问 HTML5 canvas 的 content/pixels。
我遇到困难的部分是第 4 步。为了确保我只处理视频每一帧图像的相关部分,重要的是我 "warp" 图像所以它只显示 "TV screen" 区域而不是整个网络摄像头图片。
经过一些阅读,我的理解是:
- 这需要某种透视变换,并且由于网络摄像头可以处于任何角度并且我们不处理平行线,因此需要 3 维变换,而 2D 是不够的。这是因为二维变换 (translate/rotate/scale/skew) 无法处理会聚边。
- HTML5 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 场景(这样我就可以对生成的 canvas 内容执行我的图像处理)
c) three.js 场景包括:
- 包含使用 VideoTexture
在一侧显示视频的平面网格。
- 一个透视相机,最初定位为显示整个网络摄像头图像
d) 允许用户单击四个角点来定义他们的电视位置,计算出 x/y 坐标并保存它们
e) 计算一个透视变换,它将 "stretch" 图像输出,以便正确的区域 "fills the frame"。换句话说,将单击的四个 "TV corner" 点拉伸到视口的四个角。我一直在使用这个库:https://github.com/jlouthan/perspective-transform 来计算这个。
f) 我的想法是,如果对包含视频的网格应用适当的变换,并且摄像机保持在固定位置,那么输出 canvas 将包含观看时所需的图像它是二维的。
Link 到我当前的(损坏的)实现
这是我目前对上述内容的尝试 link。它显示视频并允许您单击四个角。如果你点击原点周围的点(在中心)似乎它有效,但问题是如果你选择图像中其他地方的区域,它会显示错误的区域。
https://bitbucket.org/mattwilson1024/perspective-transform/src/master/
总结
我非常感谢任何帮助解决为什么这不像我预期的那样工作,或者任何关于是否有 better/easier 方法来实现我的目标的指示需要。
原始实现的问题在于 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.