-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbinary_preloader.html
More file actions
156 lines (142 loc) · 6.12 KB
/
Copy pathbinary_preloader.html
File metadata and controls
156 lines (142 loc) · 6.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>OpenCV.js 画像ステッチング プロトタイプ</title>
<style>
body { font-family: sans-serif; }
canvas { border: 1px solid #ccc; margin-top: 10px; }
</style>
<!-- OpenCV.js の読み込み。URLはバージョン 4.x 系の場合 -->
<script async src="https://docs.opencv.org/4.x/opencv.js" onload="onOpenCvReady();"></script>
</head>
<body>
<h2>OpenCV.js 画像ステッチング プロトタイプ</h2>
<p>
画像 1: <input type="file" id="imageInput1" accept="image/*"><br>
画像 2: <input type="file" id="imageInput2" accept="image/*">
</p>
<button id="stitchButton">画像を合成する</button>
<p id="status">OpenCV.js のロードを待っています…</p>
<canvas id="canvasOutput"></canvas>
<script type="text/javascript">
// OpenCV.js のロード完了を待つためのフラグ
let cv;
let cvReady = false;
async function onOpenCvReady() {
cv = await window.cv
cvReady = true;
document.getElementById('status').innerHTML = 'OpenCV.js のロードが完了しました。';
}
// FileReader を用いて画像ファイルを読み込み、Image オブジェクトとして返す
function loadImage(fileInput, callback) {
let file = fileInput.files[0];
if (!file) return;
let reader = new FileReader();
reader.onload = function(e) {
let img = new Image();
img.onload = function() {
callback(img);
}
img.src = e.target.result;
}
reader.readAsDataURL(file);
}
// ボタン押下時の処理
document.getElementById('stitchButton').addEventListener('click', function() {
if (!cvReady) {
alert('OpenCV.js がまだロードされていません!');
return;
}
let input1 = document.getElementById('imageInput1');
let input2 = document.getElementById('imageInput2');
if (input1.files.length === 0 || input2.files.length === 0) {
alert('2 枚の画像ファイルを選択してください。');
return;
}
// 並列に画像を読み込み
loadImage(input1, function(img1) {
loadImage(input2, function(img2) {
stitchImages(img1, img2);
});
});
});
// 2 枚の画像を読み込み、ORB で特徴抽出・マッチング、ホモグラフィ求めて合成する関数
function stitchImages(img1, img2) {
// 画像を一旦 Canvas に描画して cv.imread で読み込めるようにする
let canvas1 = document.createElement('canvas');
let canvas2 = document.createElement('canvas');
canvas1.width = img1.width; canvas1.height = img1.height;
canvas2.width = img2.width; canvas2.height = img2.height;
let ctx1 = canvas1.getContext('2d');
let ctx2 = canvas2.getContext('2d');
ctx1.drawImage(img1, 0, 0);
ctx2.drawImage(img2, 0, 0);
// cv.Mat に変換
let mat1 = cv.imread(canvas1);
let mat2 = cv.imread(canvas2);
// グレースケール変換
let gray1 = new cv.Mat();
let gray2 = new cv.Mat();
cv.cvtColor(mat1, gray1, cv.COLOR_RGBA2GRAY);
cv.cvtColor(mat2, gray2, cv.COLOR_RGBA2GRAY);
// ORB による特徴点検出と記述子抽出
let orb = new cv.ORB(); // ※ OpenCV.js のバージョンによっては cv.ORB_create() の形式の場合もあります
let keypoints1 = new cv.KeyPointVector();
let keypoints2 = new cv.KeyPointVector();
let descriptors1 = new cv.Mat();
let descriptors2 = new cv.Mat();
orb.detectAndCompute(gray1, new cv.Mat(), keypoints1, descriptors1);
orb.detectAndCompute(gray2, new cv.Mat(), keypoints2, descriptors2);
// BFMatcher(Hamming 距離)を用いてマッチング
let bf = new cv.BFMatcher(cv.NORM_HAMMING, true);
let matches = new cv.DMatchVector();
bf.match(descriptors1, descriptors2, matches);
// マッチング結果から上位 N 件(ここでは 50 件)を選択
let goodMatches = [];
for (let i = 0; i < matches.size(); i++) {
goodMatches.push(matches.get(i));
}
goodMatches.sort((a, b) => a.distance - b.distance);
let numGoodMatches = Math.min(50, goodMatches.length);
// マッチした特徴点から対応点の配列を作成
let srcPoints = [];
let dstPoints = [];
for (let i = 0; i < numGoodMatches; i++) {
let kp1 = keypoints1.get(goodMatches[i].queryIdx).pt;
let kp2 = keypoints2.get(goodMatches[i].trainIdx).pt;
// 画像1側の座標、画像2側の座標
srcPoints.push(kp1.x);
srcPoints.push(kp1.y);
dstPoints.push(kp2.x);
dstPoints.push(kp2.y);
}
// cv.matFromArray(row, col, type, array) で変換
let srcMat = cv.matFromArray(numGoodMatches, 1, cv.CV_32FC2, srcPoints);
let dstMat = cv.matFromArray(numGoodMatches, 1, cv.CV_32FC2, dstPoints);
// RANSAC を用いてホモグラフィ行列を求める
let mask = new cv.Mat();
let homography = cv.findHomography(dstMat, srcMat, cv.RANSAC, 5, mask);
// 結果画像用のキャンバスサイズを決定(ここでは横に並べられるように大きめに)
let resultWidth = mat1.cols + mat2.cols;
let resultHeight = Math.max(mat1.rows, mat2.rows);
let result = new cv.Mat();
cv.warpPerspective(mat2, result, homography, new cv.Size(resultWidth, resultHeight));
// mat1 を結果画像にコピー(座標は (0,0) とする)
let roi = result.roi(new cv.Rect(0, 0, mat1.cols, mat1.rows));
mat1.copyTo(roi);
roi.delete();
// 合成結果を画面上の Canvas に表示
cv.imshow('canvasOutput', result);
// 使用したリソースを解放
mat1.delete(); mat2.delete();
gray1.delete(); gray2.delete();
orb.delete(); keypoints1.delete(); keypoints2.delete();
descriptors1.delete(); descriptors2.delete();
bf.delete(); matches.delete();
srcMat.delete(); dstMat.delete();
mask.delete(); homography.delete(); result.delete();
}
</script>
</body>
</html>