OpenCV.jsで魚眼補正
途中まで作業中のメモから転記
OpenCVのReleasesページからopencv-4.8.0-docs.zipをダウンロード。4.8.1はこのファイルに当たるものがないようなので1つ前のバージョンを使用。
展開して中にあるファイルopencv.jsを持ってくる
チュートリアルを参考に補正行うコードを作成。パラメータは適当な値をハードコードしているだけ
cv.getOptimalNewCameraMatrix
のところでエラーが出てしまう。
どうやらOpenCV.jsのデフォルトビルドにはこの関数が入っていないようなので、カスタムビルドを作成する必要があるらしい。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Fisheye</title>
</head>
<body>
<h2>Fisheye</h2>
<p id="status">OpenCV.js is loading...</p>
<div>
<table cellpadding="0" cellspacing="0" width="0" border="0">
<tr>
<td>
<video id="videoInput" width=640 height=480></video>
</td>
<td>
<canvas id="canvasOutput" width=640 height=480></canvas>
</td>
<td></td>
<td></td>
</tr>
<tr>
<td>
<div class="caption">videoInput</div>
</td>
<td>
<div class="caption">canvasOutput</div>
</td>
<td></td>
<td></td>
</tr>
</table>
</div>
<script type="text/javascript">
var Module = {
onRuntimeInitialized() {
document.getElementById('status').innerHTML = 'OpenCV.js is ready.';
let video = document.getElementById('videoInput');
let src = new cv.Mat(video.height, video.width, cv.CV_8UC4);
let dst = new cv.Mat(video.height, video.width, cv.CV_8UC1);
let cap = new cv.VideoCapture(video);
navigator.mediaDevices.getUserMedia({
video: true,
audio: false
})
.then(stream => {
video.srcObject = stream;
video.play();
})
.catch(err => {
console.log("An error occurred! " + err);
});
const FPS = 30;
const processVideo = () => {
try {
const begin = Date.now();
cap.read(src);
// パラメータ
const K = cv.matFromArray(3, 3, cv.CV_64FC1, [350, 0, 250, 0, 350, 150, 0, 0, 1]);
const D = cv.matFromArray(1, 5, cv.CV_64FC1, [-0.4, 0.1, 0, 0, 0]);
// カメラ行列の計算
const newK = cv.getOptimalNewCameraMatrix(K, D, new cv.Size(src.cols, src.rows), 1);
let map1 = new cv.Mat(),
map2 = new cv.Mat();
cv.initUndistortRectifyMap(K, D, new cv.Mat(), newK, new cv.Size(src.cols, src.rows), cv.CV_16SC2, map1, map2);
// 魚眼補正
cv.remap(src, dst, map1, map2, cv.INTER_LINEAR, cv.BORDER_CONSTANT);
cv.imshow('canvasOutput', dst);
// OpenCV.jsではnewしたmatはdeleteで明示的に解放する必要がある
map1.delete();
map2.delete();
newK.delete();
// 次回実行のスケジュール
const delay = 1000 / FPS - (Date.now() - begin);
console.log(delay)
setTimeout(processVideo, delay);
} catch (err) {
console.log(err);
}
};
// 初回実行のスケジュール
setTimeout(processVideo, 0);
// 終了時処理
window.onunload = () => {
video.pause();
video.srcObject = null;
if (video.srcObject) video.srcObject.getVideoTracks()[0].stop();
if (src) src.delete();
if (dst) dst.delete();
};
}
};
</script>
<script async src="opencv.js" type="text/javascript"></script>
</body>
</html>
OpenCV.jsをデフォルト設定でビルド
cv.getOptimalNewCameraMatrix
を含めてビルドを行いたいが、その前にまずはデフォルト設定でビルドできることを確認したい
OpenCV.jsの公式サイトにDockerでビルドする方法が載っているのでこれを実行してみる
PowerShell前提のコマンドなのでLinux、macOSの場合は少し変わる。上のチュートリアル参照
git clone https://github.com/opencv/opencv.git
cd opencv
docker run --rm --workdir /src -v "$(get-location):/src" "emscripten/emsdk:2.0.10" emcmake python3 ./platforms/js/build_js.py build_js
ビルドは成功するもののOpenCVの関数を動かそうとすると動かない
調べてみるとemscriptenのバージョンが良くないらしい
とりあえずバージョンを変更して実行すると動いた。
docker run --rm --workdir /src -v "$(get-location):/src" "emscripten/emsdk:1.39.15" emcmake python3 ./platforms/js/build_js.py build_js
カスタムビルドを作成
あとは必要な関数を含めるようにする。
クローンしたリポジトリの中のopencv\platforms\js\opencv_js.config.py
を変更することで必要なモジュール、関数を絞ってビルドすることができる
今回の場合、190行目当たりのcalib3dのところに必要な関数を追加すればよい
いらない関数は削除した方が軽量化できるが、面倒なのでやっていない
calib3d = {
"": [
"findHomography",
"calibrateCameraExtended",
"drawFrameAxes",
"estimateAffine2D",
"getDefaultNewCameraMatrix",
"initUndistortRectifyMap",
"Rodrigues",
"solvePnP",
"solvePnPRansac",
"solvePnPRefineLM",
"projectPoints",
"undistort",
# cv::fisheye namespace
"fisheye_initUndistortRectifyMap",
"fisheye_projectPoints",
"getOptimalNewCameraMatrix", # この行を追加
],
}
さっきと同じコマンドを実行すればビルド完了。これで最初のコードが正常に動作した。
docker run --rm --workdir /src -v "$(get-location):/src" "emscripten/emsdk:1.39.15" emcmake python3 ./platforms/js/build_js.py build_js