OpenCV の正式名は Open Source Computer Vision Library で、クロスプラットフォームのコンピュータ ビジョン ライブラリです。OpenCV は Intel Corporation によって開始され、開発に参加しており、BSD ライセンスの下でリリースされており、商用および研究分野で無料で使用できます。OpenCV は、リアルタイム画像処理、コンピュータ ビジョン、パターン認識プログラムの開発に使用できます。このライブラリは、インテルの IPP を使用して処理を高速化することもできます。
OpenCV.js - OpenCV と JS 開発者を接続する
HTML5 の台頭により、Web 側で画像処理関連テクノロジを使用することが特に重要になっており、OpenCV.js はJavascript 開発者と OpenCV の間の橋渡しをします。これはもともとインテル コーポレーションによって開始された研究でしたが、2017 年に OpenCV プロジェクトに統合されました。
Emscriptenは、LLVM から JavaScript へのコンパイラーであり、C++ の基礎となる関数を、ブラウザー側で直接実行できる asm.js または WebAssembly にコンパイルします。OpenCV.js は、OpenCV 関数を Emscripten を通じて asm.js または WebAssembly にコンパイルし、Web アプリケーション用の JS API を提供します。
OpenCV.js の目標:
1. Web上でOpenCVの開発と利用を実現
2. オンライン コミュニティを支援するために、開発者やコンピュータ ビジョン研究者は、さまざまな Web ベースの OpenCV サンプルに対話的にアクセスして、特定のビジョン アルゴリズムを理解できるようにします。
基本的な概念と操作
マトリックス
OpenCV.js では、新しい変数型である行列型である Mat 型を導入しています。let mat = new cv.Mat();
新しい行列型の作成に使用され、OpenCV.js によって読み取られる画像はすべてこの型に格納されます。
画像の読み書き
OpenCV.js は、<img>
またはの内容<canvas>
を行列型として読み込んで変換したり、加工した画像を<img>
または の<canvas>
内容として利用したりすることができます。具体的な方法は次のとおりです。
// 读取
let mat = cv.imread("inputCanvasId");
// 其中 inputCanvasId 为目标 DOM 元素的 id 属性
// 写入
cv.imshow(mat, "outputCanvasId");
// mat 为处理后图像矩阵变量
官方已经提供比较完整的说明文档,这里不详细介绍具体的 API 了,我们简单说一下用法。注意:所有的代码应该写在初始化函数onRuntimeInitialized中。cv 提供了imread(imageid | HTMLImageElement | HTMLCanvasElement) 的函数签名,这可以从指定的源读取图片内容,并返回一个cv.Mat类型的对象,这是一个基本的存储二维空间的数据结构——矩阵,这里用来存储图片的像素结构,后面很多操作都是基于Mat类型来操作的。输出图片也非常方便,直接调用cv.imshow(canvasid | HTMLCanvasElement, cv.Mat对象) 就可以把图像绘制在屏幕上。需要记住的是 WebAssembly 不会帮助你回收内存,所以记得 cv.Mat 对象用完后及时调用delete()方法释放内存。
图像数据类型
Mat 是 OpenCV 基础的图像数据结构,其数据类型对照表如下:
| Data Properties
| C++Type | JavaScript Typed Array | Mat Type | | --- | --- | --- | --- | | data | uchar | Uint8Array | CV_8U | | data8S | char | Int8Array | CV_8S | | data16U | ushort | Uint16Array | CV_16U | | data16S | short | Int16Array | CV_16S | | data32S | int | Int32Array | CV_32S | | data32F | float | Float32Array | CV_32F | | data64F | double | Float64Array | CV_64F |
灰度实现
<!DOCTYPE html>
<html>
<head>
<title>OpenCV.js</title>
<style type="text/css">
.wrap-image {
display: flex;
flex-direction: row;
margin-top: 10px;
}
.wrap-image img,
.wrap-image canvas {
width: 300px;
margin-right: 10px;
}
</style>
</head>
<body>
<h3 id="status">Loading the Opencv ...</h3>
<input type="file" id="fileInput-gray"/>
<div class="wrap-image">
<img id="imageUpload-gray" alt="No Image" />
<canvas id="canvasOutput-gray"></canvas>
</div>
<script type="text/javascript">
//图片灰度处理效果:
let imgElement = document.getElementById('imageUpload-gray');
let inputElement = document.getElementById('fileInput-gray');
inputElement.onchange = function() {
imgElement.src = URL.createObjectURL(event.target.files[0]);
}
imgElement.onload =function() {
let src = cv.imread('imageUpload-gray');
let dst = new cv.Mat();
cv.cvtColor(src, dst, cv.COLOR_RGBA2GRAY, 0);
cv.imshow('canvasOutput-gray', dst);
src.delete(); dst.delete();
};
function onOpenCvReady() {
document.getElementById('status').remove();
}
</script>
<script async src="https://docs.opencv.org/4.5.3/opencv.js" onload="onOpenCvReady();" type="text/javascript"></script>
</body>
</html>
image.png
图像阈值处理
//图片灰度处理效果:
let imgElement = document.getElementById('imageUpload');
let inputElement = document.getElementById('fileInput');
inputElement.onchange = function() {
imgElement.src = URL.createObjectURL(event.target.files[0]);
}
imgElement.onload =function() {
let src = cv.imread('imageUpload');
let dst = new cv.Mat();
// cv.cvtColor(src, dst, cv.COLOR_RGBA2GRAY, 0);
cv.threshold(src, dst, 177, 200, cv.THRESH_BINARY);
cv.imshow('canvasOutput', dst);
src.delete(); dst.delete();
};
function onOpenCvReady() {
document.getElementById('status').remove();
}
image.png
自适应阈值
let imgElement = document.getElementById('imageUpload');
let inputElement = document.getElementById('fileInput');
inputElement.onchange = function() {
imgElement.src = URL.createObjectURL(event.target.files[0]);
}
imgElement.onload =function() {
let src = cv.imread('imageUpload');
let dst = new cv.Mat();
cv.cvtColor(src, src, cv.COLOR_RGBA2GRAY, 0);
// You can try more different parameters
cv.adaptiveThreshold(src, dst, 200, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 3, 2);
cv.imshow('canvasOutput', dst);
src.delete(); dst.delete();
};
function onOpenCvReady() {
document.getElementById('status').remove();
}
image.png
滤镜处理
let imgElement = document.getElementById('imageUpload');
let inputElement = document.getElementById('fileInput');
inputElement.onchange = function() {
imgElement.src = URL.createObjectURL(event.target.files[0]);
}
imgElement.onload =function() {
let src = cv.imread('imageUpload');
let dst = new cv.Mat();
let M = cv.Mat.eye(3, 3, cv.CV_32FC1);
let anchor = new cv.Point(-1, -1);
// You can try more different parameters
cv.filter2D(src, dst, cv.CV_8U, M, anchor, 0, cv.BORDER_DEFAULT);
cv.imshow('canvasOutput', dst);
src.delete(); dst.delete();
};
function onOpenCvReady() {
document.getElementById('status').remove();
}
image.png
图像模糊
let imgElement = document.getElementById('imageUpload');
let inputElement = document.getElementById('fileInput');
inputElement.onchange = function() {
imgElement.src = URL.createObjectURL(event.target.files[0]);
}
imgElement.onload =function() {
let src = cv.imread('imageUpload');
let dst = new cv.Mat();
let ksize = new cv.Size(3, 3);
// You can try more different parameters
cv.GaussianBlur(src, dst, ksize, 0, 0, cv.BORDER_DEFAULT);
cv.imshow('canvasOutput', dst);
src.delete(); dst.delete();
};
function onOpenCvReady() {
document.getElementById('status').remove();
}
image.png
图像直方图
let imgElement = document.getElementById('imageUpload');
let inputElement = document.getElementById('fileInput');
inputElement.onchange = function() {
imgElement.src = URL.createObjectURL(event.target.files[0]);
}
imgElement.onload =function() {
let src = cv.imread('imageUpload');
// let dst = new cv.Mat();
// let src = cv.imread('canvasInput');
cv.cvtColor(src, src, cv.COLOR_RGBA2GRAY, 0);
let srcVec = new cv.MatVector();
srcVec.push_back(src);
let accumulate = false;
let channels = [0];
let histSize = [256];
let ranges = [0, 255];
let hist = new cv.Mat();
let mask = new cv.Mat();
let color = new cv.Scalar(255, 255, 255);
let scale = 2;
// You can try more different parameters
cv.calcHist(srcVec, channels, mask, hist, histSize, ranges, accumulate);
let result = cv.minMaxLoc(hist, mask);
let max = result.maxVal;
let dst = new cv.Mat.zeros(src.rows, histSize[0] * scale,
cv.CV_8UC3);
// draw histogram
for (let i = 0; i < histSize[0]; i++) {
let binVal = hist.data32F[i] * src.rows / max;
let point1 = new cv.Point(i * scale, src.rows - 1);
let point2 = new cv.Point((i + 1) * scale - 1, src.rows - binVal);
cv.rectangle(dst, point1, point2, color, cv.FILLED);
}
cv.imshow('canvasOutput', dst);
src.delete(); dst.delete(); srcVec.delete(); mask.delete(); hist.delete();
}
function onOpenCvReady() {
document.getElementById('status').remove();
}
image.png