オラ!
久しぶりに更新してみる。最近ずっとやっていたことがある。OpenCV。物体検知?というか1枚目の画像と2枚目の画像が一致しているかどうかを見たかったのだけどまあとりあえずやってみた。
まずはこれ読んだ方がいいって思ってざっと読んでみた(必要そうなところだけ)
OpenCVだけど世の中殆どのサンプルがpyton。pyさんでもなんでもいいんだけどちょっと今回はブラウザだけでやりたかったのでOpenCV.jsを使ってみた。
OpenCV.jsは正式にそのものが配布されているわけではなくOpenCV本家からコンパイルするってらしいけど面倒なので調べたら本家のサーバにこっそりあるらしい。とりあえずそれをcurlで持ってきた。
(最近はWindowsにもcurlあって便利ね)
やりたいことはこれね。OpenCVの本家のサンプルにあるやつ。これをOpenCV.jsだけでやりたかったわけ。
まあ。ハマりますね。npってなに?reshapeって?pyさんってなんか便利そう。けどJavascript一択でやろうと決めたのでOpenCV.jsだけでやってみた。
画像の読み込み
特に問題ない。呼んでグレースケールにしておくだけ。素人なのでグレースケールにする理由はちゃんとは答えられない。きっと必要なんだよ。
let templ = cv.imread(templateSrcElement);
let src = cv.imread(imgSrcElement);
let srcgray = new cv.Mat();
let templgray = new cv.Mat();
cv.cvtColor(src, srcgray, cv.COLOR_RGBA2GRAY);
cv.cvtColor(templ, templgray, cv.COLOR_RGBA2GRAY);
画像の特徴点の検出
特徴点の検出には色々な方式があるらしいけど、なんか良さげなAKAZEを利用"A風"ってらしい。2枚の画像の特徴点を検出して描画してみる。
var akaze = new cv.AKAZE();
var templkp = new cv.KeyPointVector();
var templdas = new cv.Mat();
let templmask = new cv.Mat();
akaze.detectAndCompute(templgray, templmask, templkp, templdas);
let templview = new cv.Mat();
cv.drawKeypoints(templgray, templkp, templview);
cv.imshow('opencvCanvas1', templview);
var srckp = new cv.KeyPointVector();
var srcdas = new cv.Mat();
let srcmask = new cv.Mat();
akaze.detectAndCompute(srcgray, srcmask, srckp, srcdas);
let srcview = new cv.Mat();
cv.drawKeypoints(srcgray, srckp, srcview);
cv.imshow('opencvCanvas2', srcview);
特徴点の一致を検出
はい。この辺からハマってきます。サンプルどおりに動かないしOpenCV.jsのドキュメントもイマイチよくわかんない。メンテされてないのか?
2つの画像の特徴点を比較するってことらしい。検出された点はいらないものがあるので結果を絞り込むようにするらしい。
普通のmatchの場合はDMatchVector。
knnMatchの場合はDMatchVectorVector()らしい。
で最後に一致してる点をつなぐ描画をする。これは呼ぶだけ。おお。
var bf = new cv.BFMatcher();
var matches = new cv.DMatchVectorVector();
bf.knnMatch(templdas, srcdas, matches, 2);
var arr = [];
var good_matches = new cv.DMatchVector();
for (let i = 0; i < matches.size(); ++i) {
let match = matches.get(i);
let dMatch1 = match.get(0);
let dMatch2 = match.get(1);
if (dMatch1.distance <= dMatch2.distance * 0.8) {
good_matches.push_back(dMatch1);
}
}
console.log("good_matches : " + good_matches.size());
var img_matches = new cv.Mat();
cv.drawMatches(templgray, templkp, srcgray, srckp, good_matches, img_matches);
cv.imshow('opencvCanvas3', img_matches);
ホモグラフィーする
ホモグラフィーってので一致した点の周辺を矩形?にする?ごめんなさい素人で用語が解らない。
if(good_matches.size() > 10){ //十分な点があるか?
let srcPoints = [];
let dstPoints = [];
for (let k = 0; k < good_matches.size(); ++k) {
srcPoints.push(templkp.get(good_matches.get(k).queryIdx).pt.x);
srcPoints.push(templkp.get(good_matches.get(k).queryIdx).pt.y);
dstPoints.push(srckp.get(good_matches.get(k).trainIdx).pt.x);
dstPoints.push(srckp.get(good_matches.get(k).trainIdx).pt.y);
}
let srcPointsMatArr = cv.matFromArray(srcPoints.length/2, 1, cv.CV_32FC2, srcPoints);
let dstPointsMatArr = cv.matFromArray(dstPoints.length/2, 1, cv.CV_32FC2, dstPoints);
const homo = cv.findHomography(srcPointsMatArr, dstPointsMatArr, cv.RANSAC, 5.0);
console.log("findHomography done");
}else{
console.log("no try findHomography");
}
ホモした点を整理する?
整理してくれるんです。homoした結果は沢山の点の集まりのデータ。これを4つ角?にしてくれる。
let obj_corners_matarr = cv.matFromArray(4, 1, cv.CV_32FC2, [0, 0, templgray.cols - 1, 0, templgray.cols - 1, templgray.rows - 1, 0, templgray.rows - 1]); //
let scene_corners_matarr = cv.matFromArray(4, 1, cv.CV_32FC2, [0, 0, 0, 0, 0, 0, 0, 0]); //
cv.perspectiveTransform(obj_corners_matarr, scene_corners_matarr, homo);
let corner1 = new cv.Point(scene_corners_matarr.data32F[0], scene_corners_matarr.data32F[1]);
let corner2 = new cv.Point(scene_corners_matarr.data32F[2], scene_corners_matarr.data32F[3]);
let corner3 = new cv.Point(scene_corners_matarr.data32F[4], scene_corners_matarr.data32F[5]);
let corner4 = new cv.Point(scene_corners_matarr.data32F[6], scene_corners_matarr.data32F[7]);
ってな感じ。サンプルはpyさんのnpを使っていたり、便利そうな型使ってたりしますがとりあえずJavaScriptで表現できるように素人が書いてみた。google先生に聞いても外人さんが同じような質問してますがレスがついてなかったりしたのでとりあえず記録しておきます。
アディオス