在本节中将介绍之前提出的关于 opencv.js 的问题:
- 如何将 opencv 引入到 node 项目。
- 如何识别页面元素。
- 如何获取目标元素的坐标。
- 如何提高识别准确率。
- 选用那种识别算法。
如何将 opencv 引入到 node 项目
您可以查阅官网教程:Using OpenCV.js、Using OpenCV.js In Node.js,这里我将做出我的复述。
您需要获取 opencv.js
源文件, opencv.js
您可以从 https://docs.opencv.org/{VERSION_NUMBER}/opencv.js 的在线文档下载预构建脚本(例如:https://docs .opencv.org/4.5.0/opencv.js。如果您想要最新版本,请使用 4.x)
4.5.0 版的 opencv.js
脚本文件大小有 8M 多,为了加载它到项目内,您需要写一个加载程序 loadOpenCV.js
:
function loadOpenCV() {
return new Promise((resolve) => {
global.Module = {
onRuntimeInitialized: resolve,
};
global.cv = require("./opencv.js");
});
}
exports.loadOpenCV = loadOpenCV;
确保 opencv.js
和 loadOpenCV.js
在同一目录下。下面是使用 opencv.js 的示例代码:
const puppeteer = require("puppeteer");
const PNG = require("pngjs").PNG;
const { loadOpenCV } = require("./utils/loadOpenCV");
(async () => {
await loadOpenCV();
const browser = await puppeteer.launch({
headless: false,
defaultViewport: { width: 1440, height: 780 },
args: [
`--disable-notifications=true`,
"--window-position=1921,0",
`--window-size=1920,1080`,
],
userDataDir: "~/desktop/puppeteer-data",
});
const page = await browser.newPage();
await page.goto("https://www.google.com/");
// 截图(默认 png 格式)
const pageImgBuffer = await page.screenshot();
const pageImgPngData = PNG.sync.read(pageImgBuffer);
let pageSrc = cv.matFromImageData(pageImgPngData);
// 置灰处理
cv.cvtColor(pageSrc, pageSrc, cv.COLOR_RGBA2GRAY);
// ...
pageSrc.delete();
await page.close();
await browser.close();
})();
如何获取目标元素的坐标
在示例代码中,我对匹配到目标元素的坐标打上了红色标记,您可在示例代码中查看详细步骤。
如何识别页面元素
这是 opencv 的模版匹配线上演示 demo,如果将 puppeteer 的 page 截图作为母图,还需要目标元素的模版图片。通常,为了适配各种主题,您必须提前采集不同主题下的目标元素模版图片,例如下图所示的不同主题下的点赞按钮:
请注意,示例代码没有展示页面有滚动的情况,您需要将 opencv
匹配到的目标元素坐标转换成真实页面上的坐标。另外,使用一整个 page 的截图作为母图肯定会浪费算力,您可以减小母图尺寸,但请确保您的目标元素滚动到了母图的范围内再截取母图,并且做好坐标转换。
如何提高识别准确率
确保模版图片具有特征
为了节省算力,我们通常认为模版图片越小越好,但是太小的模版图片可能会失去它的特征。我想匹配点赞按钮,如果模版图片刚好是 👍 这么大,他没有任何背景颜色,那么它的特征点更少,匹配出错的机率会上升!可以适当增大模版图片尺寸,展示更多的背景颜色。
这是我使用图片识别查找元素的成功率,可以看出它能够稳定在 90% 以上:
隐藏识别区域内不必要的文字、图片
您完全可以用 css 隐藏掉页面内除目标外的文字、图片等,因为它们会影响识别的准确性。如果您要识别的目标元素是图片,您可以用 puppeteer 向页面插入 css 样式隐藏掉页面内的文字,甚至可以修改一些元素的颜色来使目标元素在截图区域内是最凸显的。
选用那种识别算法
cv.matchTemplate(headerSrc, templateSrc, dst, cv.TM_CCOEFF_NORMED, mask);
推荐使用 TM_CCOEFF_NORM
算法,它的匹配准确率比较高。
关于其他算法的比较: