使用 OpenCV.js 在 JavaScript 中介绍计算机视觉
简介 OpenCV或开源计算机视觉库,是用于图像处理和图像识别的强大库。该图书馆拥有庞大的社区,并已在许多领域广泛使用,从人脸检测到互动艺术。它最初是用 C++ 构建的,但是已经为不同的语言创建了绑定,例如 Python 和 Java。它甚至可以在 JavaScript 中作为 OpenCV.js 使用,这就是我们将在本教程中使用的。 在这个项目中,我们将创建一个网页,用户可以在其中上传图像,以检
简介
OpenCV或开源计算机视觉库,是用于图像处理和图像识别的强大库。该图书馆拥有庞大的社区,并已在许多领域广泛使用,从人脸检测到互动艺术。它最初是用 C++ 构建的,但是已经为不同的语言创建了绑定,例如 Python 和 Java。它甚至可以在 JavaScript 中作为 OpenCV.js 使用,这就是我们将在本教程中使用的。
在这个项目中,我们将创建一个网页,用户可以在其中上传图像,以检测其中包含的所有圆圈。我们将用黑色轮廓突出显示圆圈,用户将能够下载修改后的图像。
这个项目的代码在这个 GitHub repo中可用。
先决条件
要完成本教程,您需要引入 OpenCV.js 库。 3.3.1 版本可在此处获得:
https://docs.opencv.org/3.3.1/opencv.js
将此文件在本地保存为opencv.js
,放在您可以轻松找到的位置。
第 1 步 — 设置项目
要开始,您首先需要为您的项目创建一个空间。创建一个名为opencvjs-project
的目录:
mkdir opencvjs-project
将opencv.js
的本地副本移动到此目录。
接下来,使用以下模板添加index.html
文件:
索引.html
<!DOCTYPE html>
<html>
<head>
<title>OpenCV.js</title>
</head>
<body>
<!-- Our HTML will go here-->
<script type="text/javascript">
// Our JavaScript code will go here
</script>
</body>
</html>
除了此文件中现有的空<script>
标记外,添加一个新的<script>
标记,该标记引用本地opencv.js
文件。脚本很大,加载需要一些时间,所以最好异步加载。这可以通过将async
添加到<script>
标签来完成:
索引.html
<script type="text/javascript">
// Our JavaScript code will go here
</script>
<script async src="opencv.js" type="text/javascript"></script>
由于文件大小,OpenCV.js 可能无法立即准备好,我们可以通过显示内容正在加载来提供更好的用户体验。我们可以向页面添加一个加载微调器(归功于 StackOverflow 上的Sampson)。
首先,添加一个<div>
元素<body>
:
索引.html
<body>
<!-- Our HTML will go here-->
<div class="modal"></div>
<script type="text/javascript">
// Our JavaScript code will go here
</script>
<script async src="opencv.js" type="text/javascript"></script>
</body>
接下来,将以下 CSS 添加到index.html
的<head>
中的单独的<style>
标记中。微调器默认是不可见的(感谢display: none;
):
索引.html
/* display loading gif and hide webpage */
.modal {
display: none;
position: fixed;
z-index: 1000;
top: 0;
left: 0;
height: 100%;
width: 100%;
background: rgba( 255, 255, 255, .8)
url('http://i.stack.imgur.com/FhHRx.gif')
50% 50%
no-repeat;
}
/* prevent scrollbar from display during load */
body.loading {
overflow: hidden;
}
/* display the modal when loading class is added to body */
body.loading .modal {
display: block;
}
为了显示加载 gif,我们可以将"loading"
类添加到正文中。将以下内容添加到空的<script>
。
索引.html
document.body.classList.add('loading');
当 OpenCV.js 加载时,我们想要隐藏加载 gif。修改引用本地opencv.js
文件的<script>
标签,添加onload
事件监听器:
索引.html
<script async src="opencv.js" onload="onOpenCvReady();" type="text/javascript"></script>
然后将onOpenCvReady
添加到另一个<script>
标记以处理删除"loading"
类:
索引.html
// previous code is here
function onOpenCvReady() {
document.body.classList.remove('loading');
}
在浏览器中打开 HTML 页面并检查 OpenCV.js 是否按预期加载。
注意: 使用浏览器的开发人员工具,您应该确认 Console 选项卡中没有错误消息,并且 Network 选项卡显示正确引用的opencv.js
文件。您将定期在浏览器中刷新此页面以查看您的最新更改。
既然您已经设置了项目,您就可以构建图像上传功能了。
步骤 2 — 上传图片
要创建上传功能,首先将<input>
元素添加到index.html
:
索引.html
<input type="file" id="fileInput" name="file" />
如果我们只想显示源图像,我们还需要添加一个<img>
元素和一个事件监听器,它会响应<input>
元素的变化。复制以下标签并将其放在<input>
标签下:
索引.html
<img id="imageSrc" alt="No Image" />
使用它们的id
值获取<img>
元素和<input>
元素:
索引.html
// previous code is here
let imgElement = document.getElementById('imageSrc');
let inputElement = document.getElementById('fileInput');
现在,添加事件监听器,它会在<input>
更改时触发(即上传文件时)。从更改事件中,可以访问上传的文件(event.target.files 100048 0]
),并使用 URL.createObjectURL
)将其转换为 URL。图片的src
属性可以更新为这个 URL:
索引.html
// previous code is here
inputElement.onchange = function() {
imgElement.src = URL.createObjectURL(event.target.files[0]);
};
在原始图像旁边,我们可以显示第二张图像,指示检测到的圆圈。图像将显示<canvas>
元素,用于使用 JavaScript 绘制图形:
索引.html
<canvas id="imageCanvas"></canvas>
我们可以添加另一个事件监听器,它使用上传的图像更新<canvas>
:
索引.html
// previous code is here
imgElement.onload = function() {
let image = cv.imread(imgElement);
cv.imshow('imageCanvas', image);
image.delete();
};
在此步骤中,您已设置图像上传和显示功能。在下一步中,您将探索如何使用 OpenCV 检测圆圈。
第 3 步 - 检测圆圈
这就是 OpenCV 的强大之处,因为检测圆圈是一项内置任务。我们希望在用户单击按钮时找到圆圈,因此我们需要添加按钮和事件侦听器:
索引.html
<button type="button" id="circlesButton" class="btn btn-primary">Circle Detection</button>
索引.html
// previous code is here
document.getElementById('circlesButton').onclick = function() {
// circle detection code
};
根据图像,圆形检测可能需要一段时间,因此最好禁用按钮以防止用户多次点击它。在按钮上显示加载微调器也很有用。我们可以重用初始脚本加载中的加载 gif:
索引.html
// previous code is here
document.getElementById('circlesButton').onclick = function() {
this.disabled = true;
document.body.classList.add('loading');
// circle detection code
this.disabled = false;
document.body.classList.remove('loading');
};
检测圆圈的第一步是从<canvas>
读取图像。
在 OpenCV 中,图像作为Mat
个对象进行存储和操作。这些本质上是保存图像中每个像素值的矩阵。
对于我们的圆形检测,我们将需要三个 Mat 对象:
-
srcMat
- 保存源图像(从中检测到圆圈) -
circlesMat
- 存储我们检测到的圆圈 -
displayMatOne
- 向用户显示(我们将在其上绘制突出显示的圆圈)
对于最终的Mat
,我们可以使用clone
函数复制第一个:
索引.html
// circle detection code
let srcMat = cv.imread('imageCanvas');
let displayMat = srcMat.clone();
let circlesMat = new cv.Mat();
srcMat
需要转换为灰度。这通过简化图像使圆检测更快。我们可以使用cvtColor
函数来做到这一点。
该功能需要:
-
来源
Mat
(srcMat
) -
目标
Mat
(在这种情况下,源和目标 Mat 将是相同的srcMat
) -
表示颜色转换的值。
cv.COLOR_RGBA2GRAY
是灰度的常数。
索引.html
cv.cvtColor(srcMat, srcMat, cv.COLOR_RGBA2GRAY);
cvtColor
函数与其他 OpenCV.js 函数一样,接受更多参数。这些不是必需的,因此它们将设置为默认值。您可以参考文档以获得更好的自定义。
一旦图像转换为灰度,就可以使用HoughCircles
函数来检测圆圈。
该功能需要:
-
源
Mat
从中可以找到圆圈 (srcMat
) -
一个目的地
Mat
它将存储圆圈 (circlesMat
) -
检测圆的方法(
cv.HOUGH_GRADIENT
) -
累加器分辨率的反比(
1
) -
圆心之间的最小距离 (
45
)
还有更多参数和算法的阈值(75
和40
),可以用来提高图像的准确性。
也可以通过设置最小 (0
) 和最大半径 (0
) 来限制要检测的圆的范围。
索引.html
cv.HoughCircles(srcMat, circlesMat, cv.HOUGH_GRADIENT, 1, 45, 75, 40, 0, 0);
现在,我们应该有一个Mat
对象,其中检测到圆圈。
接下来,我们将在<canvas>
中绘制圆圈。
第四步——画圆
现在可以突出显示所有检测到的圆圈。我们想在每个圆圈周围画一个轮廓以显示给用户。要使用 OpenCV.js 绘制圆,我们需要中心点和半径。这些值存储在circlesMat
中,因此我们可以通过遍历矩阵的列来检索它:
索引.html
for (let i = 0; i < circlesMat.cols; ++i) {
// draw circles
}
circlesMat
依次存储中心点和半径的x
和y
值。
例如,对于第一个圆圈,可以按如下方式检索值:
let x = circlesMat.data32F[0];
let y = circlesMat.data32F[1];
let radius = circlesMat.data32F[2];
要获取每个圆圈的所有值,我们可以执行以下操作:
索引.html
for (let i = 0; i < circlesMat.cols; ++i) {
let x = circlesMat.data32F[i * 3];
let y = circlesMat.data32F[i * 3 + 1];
let radius = circlesMat.data32F[i * 3 + 2];
// draw circles
}
最后,使用所有这些值,我们能够在圆圈周围绘制轮廓。
要在 OpenCV.js 中绘制圆圈,我们需要:
-
目的地
Mat
(我们要向用户显示的图像 -displayMat
) -
中心
Point
(使用 x 和 y 值) -
半径值
-
一个标量(RGB 值的数组)
还有其他参数可以传入circles
,例如线条粗细,在本例中为3
:
索引.html
let center = new cv.Point(x, y);
cv.circle(displayMat, center, radius, [0, 0, 0, 255], 3);
画圆的全部代码如下:
索引.html
for (let i = 0; i < circlesMat.cols; ++i) {
let x = circlesMat.data32F[i * 3];
let y = circlesMat.data32F[i * 3 + 1];
let radius = circlesMat.data32F[i * 3 + 2];
let center = new cv.Point(x, y);
// draw circles
cv.circle(displayMat, center, radius, [0, 0, 0, 255], 3);
}
绘制完displayMat
上的所有圆圈后,我们可以将其显示给用户:
索引.html
cv.imshow('imageCanvas', displayMat);
最后,清理我们不再需要的Mat
个对象是一个好习惯。这样做是为了防止内存问题:
索引.html
srcMat.delete();
displayMat.delete();
circlesMat.delete();
当我们把它们放在一起时,圆形检测和绘制代码将如下所示:
索引.html
// previous code is here
document.getElementById('circlesButton').onclick = function() {
this.disabled = true;
document.body.classList.add('loading');
let srcMat = cv.imread('imageCanvas');
let displayMat = srcMat.clone();
let circlesMat = new cv.Mat();
cv.cvtColor(srcMat, srcMat, cv.COLOR_RGBA2GRAY);
cv.HoughCircles(srcMat, circlesMat, cv.HOUGH_GRADIENT, 1, 45, 75, 40, 0, 0);
for (let i = 0; i < circlesMat.cols; ++i) {
let x = circlesMat.data32F[i * 3];
let y = circlesMat.data32F[i * 3 + 1];
let radius = circlesMat.data32F[i * 3 + 2];
let center = new cv.Point(x, y);
// draw circles
cv.circle(displayMat, center, radius, [0, 0, 0, 255], 3);
}
cv.imshow('imageCanvas', displayMat);
srcMat.delete();
displayMat.delete();
circlesMat.delete();
this.disabled = false;
document.body.classList.remove('loading');
};
这样,您就添加了检测和围绕图像中的圆圈绘制圆圈的逻辑。
第 5 步 — 下载图像
图像被修改后,用户可能想要下载它。为此,请在index.html
文件中添加一个超链接:
索引.html
<a href="#" id="downloadButton">Download Image</a>
我们将href
设置为图像 URL,将download
属性设置为图像文件名。设置download
属性向浏览器指示应该下载资源而不是导航到它。我们可以使用函数toDataURL()
从<canvas>
创建图像 URL。
将以下 JavaScript 添加到<script>
的底部:
索引.html
// previous code is here
document.getElementById('downloadButton').onclick = function() {
this.href = document.getElementById('imageCanvas').toDataURL();
this.download = 'image.png';
};
现在用户可以轻松下载修改后的图像。
结论
使用 OpenCV 可以检测圆圈。一旦您习惯了将图像作为Mat
个对象进行操作,您就可以做更多的事情。HoughCircles
算法是 OpenCV 提供的众多算法之一,可让图像处理和图像识别变得更加容易。
您可以在OpenCV 网站上找到更多教程,包括人脸识别和模板匹配。您还可以通过访问机器学习主题页面来阅读有关计算机视觉的更多信息。
更多推荐
所有评论(0)