目标
通过摄像头采集视频
我们经常需要通过摄像头采集直播。在 OpenCV.js 中,我们使用 WebRTC 和 HTML canvas 元素实现此操作。现在,让我们通过摄像头(内置或 USB)采集一段视频,将其转换为灰度视频并进行显示。
要采集视频,您需要将部分 HTML 元素添加到网页中
- 一个用于直接显示摄像头视频的 <video>
- 一个用于逐帧将视频传输到 canvas ImageData 的 <canvas>
- 另一个用于显示 OpenCV.js 采集的视频的 <canvas>
首先,我们使用 WebRTC 浏览器 navigator.mediaDevices.getUserMedia 获取媒体流。
let video = document.getElementById("videoInput"); // video 是视频标签的 ID
navigator.mediaDevices.getUserMedia({ video: true, audio: false })
.then(function(stream) {
video.srcObject = stream;
video.play();
})
.catch(function(err) {
console.log("An error occurred! " + err);
});
- 注意
- 当您通过视频文件采集视频时,无需此函数。但请注意,HTML 视频元素仅支持 Ogg(Theora)、WebM(VP8/VP9) 或 MP4(H.264) 视频格式。
播放视频
现在,浏览器获取了摄像头流。然后,我们使用 Canvas 2D API 的 CanvasRenderingContext2D.drawImage() 方法将视频绘制到 canvas 中。最后,我们可以使用 图像操作入门 中的方法在 canvas 中读取和显示图像。对于播放视频,应每隔几毫秒执行一次 cv.imshow()。我们建议使用 setTimeout() 方法。如果视频为 30fps,则延迟时间应为(1000/30 - processing_time)。
let canvasFrame = document.getElementById("canvasFrame"); // canvasFrame 是 <canvas> 的 ID
let context = canvasFrame.getContext("2d");
let src = new cv.Mat(height, width, cv.CV_8UC4);
let dst = new cv.Mat(height, width, cv.CV_8UC1);
const FPS = 30;
function processVideo() {
let begin = Date.now();
context.drawImage(video, 0, 0, width, height);
src.data.set(context.getImageData(0, 0, width, height).data);
cv.cvtColor(src, dst, cv.COLOR_RGBA2GRAY);
cv.imshow("canvasOutput", dst); // canvasOutput 是另一个 <canvas> 的 ID
// 安排下一帧。
let delay = 1000/FPS - (Date.now() - begin);
setTimeout(processVideo, delay);
}
// 安排第一帧。
setTimeout(processVideo, 0);
OpenCV.js 使用上述方法实现 cv.VideoCapture(videoSource)。您无需手动添加隐藏画布元素。
- 参数
-
- 返回值
- cv.VideoCapture 实例
我们使用 read(图像)获取视频的一个帧。出于性能考虑,图像使用 cv.CV_8UC4 类型构建,且大小与视频一致。
- 参数
-
图像 | 图像类型为 cv.CV_8UC4,且大小与视频一致。 |
可以将上述播放视频代码简化为以下所示。
let src = new cv.Mat(height, width, cv.CV_8UC4);
let dst = new cv.Mat(height, width, cv.CV_8UC1);
let cap = new cv.VideoCapture(videoSource);
const FPS = 30;
function processVideo() {
let begin = Date.now();
cap.read(src);
cv.cvtColor(src, dst, cv.COLOR_RGBA2GRAY);
cv.imshow("canvasOutput", dst);
// 安排下一帧。
let delay = 1000/FPS - (Date.now() - begin);
setTimeout(processVideo, delay);
}
// 安排第一帧。
setTimeout(processVideo, 0);
- 注意
- 停止后,请记住删除 src 和 dst。
试试看