我们如何使用计算机视觉算法:使用OpenCV.js在移动浏览器中进行视频处理

在线识别一个人已经具有了所有可能性,但是到目前为止,很少使用它们。也许我们是率先为用户实现最佳方案的人之一-通过智能手机登录网站,为您的驾驶执照或护照拍照,然后将数据发送到系统。

让我们考虑一下计算机视觉算法如何帮助直接在移动浏览器中识别视频流中的文档。在本文中,我们分享了我们在SimbirSoft上如何使用OpenCV.js的经验,可能遇到的困难,如何确保速度并在不减慢速度的情况下获得“流畅的” UX。




任务是什么


正在开发的算法的业务场景如下。通过手机访问该站点的用户应该能够拍摄其文档并将其发送到系统进行进一步处理。申请使用任何服务时,这可能是身份验证过程的一部分。

在这种情况下,Web应用程序比移动应用程序更可取,因为它的可用性和减少的完成操作的时间。该网页不需要安装,可以在加载后立即工作。用户可以在收到链接后立即执行必要的操作-提交应用程序,而不会因其他操作而分心。从业务角度看,这些因素提高了流程的转化率和商业效率。

从体系结构的角度来看,需要该算法直接检测文档的边界并裁剪图像中的多余背景。身份验证,身份验证和欺诈检查将由其他组件实施。但是,建议至少进行最少的检查,以排除发送名片,空纸矩形和其他明显不相关的图像来处理图像。

要求


作为我们项目的一部分,该算法还有以下其他要求:

  • 实时工作能力:在算法运行期间,来自摄像机的视频流不应“变慢”;
  • 具有在广泛的对比度和背景纹理下工作的能力:低对比度和对比度,同质和异质背景;
  • 支持多种智能手机型号,包括几年前发布的预算型号。

最后,该项目中没有用于训练机器学习算法的数据集,也无法收集和标记它。我们从Google的搜索结果中仅获得了一些测试样本。

考虑到问题的这种表述,我们决定基于opencv库中的经典计算机视觉算法进行开发。另一种可能性是使用机器学习算法和神经网络,但是由于性能要求,它已在工作的早期阶段被丢弃:应用后,不可能在所有目标设备上提供实时帧处理。

通用方法和算法结构


该算法的主要思想是参考框架,必须沿着该参考框架对齐文档。它的使用可同时追求多个目标。首先,它将提供合适的图像尺寸,足以用于文档的进一步处理。其次,正如我们稍后将看到的那样,它可以用作搜索文档边框时的候选过滤器之一。第三,如果找不到文档的边框,则可用于捕获和裁剪图像。



图。1.算法的一般结构

该算法的一般结构如图2所示。1.视频流中的帧以一个周期进行处理,两次迭代之间的超时设置为符合所需的FPS-我们以每秒30帧的速度停止。这样,您可以避免“速度变慢”,并减少处理器的负载和设备的功耗。

每个处理的帧都经过预处理,在此期间执行两个主要操作。首先,创建一个固定尺寸为640x480的帧的副本,该算法的其他步骤将通过该副本工作。原始图像也将保留,检测到的文档将被剪切掉。这将保存最终图像的质量。其次,将创建的副本翻译成灰色阴影。该算法将忽略正在处理的文档的颜色,因为该颜色可能因国家/地区而异,甚至在该国家/地区内的不同地区也不同-例如美国的驾驶执照。

检测文档的第一步是在图像中搜索面部。使用这种启发式方法可以消除名片和其他明显不相关的图像的捕获。使用标准opencv'shash CascadeClassifier.detectMultiScale()和预先训练的级联haarcascade_frontalface_default进行搜索。检测到的面部的最小和最大尺寸受到限制,这可以减少计算成本,并且还进一步限制了图像中文档的比例。当脸部位于参考框内区域的左部分(或护照的左下部分)时,则认为图像中检测到脸部(图2)。这是确保图像中文档正确对齐的附加措施。

本文中的示例不包含个人数据。



图。 2.图像中人脸的预期位置区域。支撑框显示为红色,预期面部位置区域的边框显示为绿色。

检测到面部后,我们继续检测边界。通常在这里使用findContours()。但是,这种方法仅在对比情况下才有效,例如,一张纸放在一张黑暗的桌子上。如果对比度较低,或者光线较差,或者某人手里拿着一张纸,用手指覆盖了边框的一部分,则检测到的轮廓会分解为单独的部分,“丢失”重要部分,或者根本无法检测到。

因此,我们采取了不同的方法。二值化后,我们首先使用Canny()将图像通过边框过滤器,然后使用Huff变换HoughLines()在所得图片中查找线条。阈值参数立即设置为足够大,等于30-以过滤检测到的短片段和其他不相关的片段。

额外地过滤了所得的一组线,仅保留了靠近参考帧的线。为此,我们首先将框架线的方程式转换为极坐标系中的点(rho,theta)-theta始终为0或pi / 2,rho对于每条线都是唯一的。然后,根据欧氏度量,我们从霍夫变换获得的直线中仅选择那些位于控制点附近的直线,同时考虑到值的大小差异。

我们将过滤后获得的一组线分配到与参考框架的四条线相对应的四组中,找到各组之间成对的线的交点,取平均值并获得四个点的坐标-检测到的文档的角(图3)。



图。 3.过滤线并定义文档边角。绿线-过滤的结果,黄点-检测到的文档角

接下来,您需要确保框架的质量。为此,我们验证框架最后一次保持静止。为此,请使用absdiff()从当前帧中减去周期开始时的帧,并将其与阈值进行比较。在减法之前,我们还使用高斯滤波器GaussianBlur()平滑图像,以减少噪声和其他随机因素的影响。我们还通过计算框架的Laplacian Laplacian(),估计其方差并将获得的值与阈值进行比较来评估框架的焦点

如果所有检查都成功,则可以进行最后一部分。我们将检测到的角度坐标重新计算为原始曝光不足图像的坐标系,并使用roi()方法切出结果区域成功检测到文档。

实施功能


在算法开发过程中,其主要组件均以python脚本的形式进行组装。之后,将该算法移植到opencv.js和javascript,然后移植到wasm。该方法是在所有阶段都考虑方便性。在python上,我们的团队可以更方便地试验算法的各种变体并进行粗略的参数设置。移植到javascript使得在目标平台(包括各种设备和浏览器)上测试算法的操作成为可能。根据这些检查的结果,对算法参数进行了微调。最后,在wasm上重写代码的关键部分使我们获得了额外的性能提升。

迁移期间,在OpenCV API中发现了许多差异,从而导致实现方面的微小变化。例如,python中拉普拉斯算子的方差被简单地视为Laplacian()。Var()。使用OpenCV.js,无法使用NumPy,但是没有提供var()方法的替代实现。解决方案:将meanStdDev()函数计算为标准偏差(清单1)。

private isImageBlurry(image: cv.Mat): boolean {
		const laplacian = new cv.Mat();
		cv.Laplacian(image, laplacian, cv.CV_64F);
		const s_mat = new cv.Mat();
		cv.meanStdDev(laplacian, new cv.Mat(), s_mat);
		const s = s_mat.data64F[0];
		const v = Math.pow(s, 2);
		return (v < this.laplacianVarianceThreshold);
	}

清单1.通过opencv.js(TypeScript)中拉普拉斯算子的变化来评估对图像的关注度

另一个功能是需要减小库的大小。 OpenCV.js原始格式的容量为7.9 MB。通过Internet进行下载会减慢算法的初始化速度。解决此问题的方法是在库组装过程中“修剪”未使用的模块,这可以显着减小输出文件的大小:我们设法实现1.8 MB的大小。可以在配置文件platform / js / opencv_js.config.py(清单2)中配置程序集中包含的组件列表。

white_list = makeWhiteList([core, imgproc, objdetect, video, dnn, features2d, photo, aruco, calib3d])

清单2.包含在javascript程序集中的opencv模块的原始白名单

最后,将算法移至Web Worker做出了重要贡献,以确保算法的所需性能。此步骤加上FPS的限制,使我们摆脱了算法运行过程中视频流的“减速”,这对UX产生了积极影响。

结果


捕获和裁剪图像的示例如图2所示。4.可以看出,在深色均匀背景下可获得最高品质的裁剪,而在浅色不均匀背景下则可获得最低的品质。这是与在不同背景上获得并用于检测文档边界的渐变相关的预期效果。在深色背景上,渐变结果要比在浅色背景上大,均匀的背景导致渐变值的变化较小。这样可以可靠地检测边界,从而更好地进行裁剪。




图。4.使用算法裁剪文档的示例

结论


本文介绍了一种算法,该算法可从适用于移动浏览器的视频流中检测帧上的文档,并考虑使用opencv.js库实现的功能。该算法可让您获得足够质量的文档输出图像,以供算法进一步用于身份验证,身份验证等。最终实现的速度使您可以获得“流畅的” UX,而不会出现“速度降低”和帧丢失的情况。

感谢您的关注!希望本文对您有所帮助。

All Articles