teratail header banner
teratail header banner
質問するログイン新規登録

質問編集履歴

2

ブログラムの修正

2017/12/22 03:15

投稿

west826
west826

スコア14

title CHANGED
File without changes
body CHANGED
@@ -60,7 +60,7 @@
60
60
  <button onClick="restart()">restart</button>
61
61
  <button onClick="no()" id="gray">なし</button>
62
62
  <button onClick="grayscale()" id="gray">モノクロ</button>
63
- <button onClick="heatmap()">疑似カ</button>
63
+ <button onClick="outline()">アウトイン</button>
64
64
 
65
65
  <input id="color" type="color" value="#ff0000" />
66
66
  <input id="distance" type="number" value="10" />
@@ -94,7 +94,7 @@
94
94
  cancelRender();
95
95
  grayFilter();
96
96
  };
97
- //疑似カ
97
+ //アウトイン
98
98
  function heatmap(){
99
99
  cancelRender();
100
100
  heatmap1();
@@ -138,7 +138,7 @@
138
138
  }
139
139
  }
140
140
 
141
- //疑似カ
141
+ //アウトイン
142
142
  function heatmap1(){
143
143
  (function () {
144
144
  //Canvasの準備

1

コードの追加

2017/12/22 03:15

投稿

west826
west826

スコア14

title CHANGED
File without changes
body CHANGED
@@ -25,7 +25,275 @@
25
25
  その為
26
26
  requestAnimationFrame を使用しようと思っています。
27
27
 
28
+ ```HTML
29
+ <!DOCTYPE html>
30
+ <html lang="ja">
31
+ <head>
32
+ <meta charset="utf-8">
33
+ <title>test</title>
34
+
35
+ <style type="text/css">
36
+ #v{
37
+ display:none;
38
+ }
39
+ #screen{
40
+ width:480px;
41
+ height:270px;
42
+ background-color:#000000;
43
+ }
44
+ button{
45
+ width:100px;
46
+ height:50px;
47
+ }
28
48
 
49
+ </style>
50
+ </head>
51
+
52
+ <body>
53
+ <video controls id="v" width="480" height="270">
54
+ <source src="movie.mp4" width="480" height="270">
55
+ </video>
56
+ <div id="screen">
57
+ <canvas id="c" width="480" height="270"></canvas>
58
+ </div>
59
+ <button onClick="playVideo()">play/stop</button>
60
+ <button onClick="restart()">restart</button>
61
+ <button onClick="no()" id="gray">なし</button>
62
+ <button onClick="grayscale()" id="gray">モノクロ</button>
63
+ <button onClick="heatmap()">疑似カラー</button>
64
+
65
+ <input id="color" type="color" value="#ff0000" />
66
+ <input id="distance" type="number" value="10" />
67
+
68
+ <script type="text/javascript">
69
+
70
+ //play/stopボタンの設定
71
+ function playVideo(){
72
+ var video = document.getElementById("v");
73
+ if(video.paused){
74
+ video.play();
75
+ }else{
76
+ video.pause();
77
+ }
78
+ setInterval(function(){
79
+ var canvas = document.getElementById("c");
80
+ canvas.getContext("2d").drawImage(video, 0, 0, 480, 270);
81
+ }, 1000/30);
82
+ }
83
+ //restartボタンの設定
84
+ function restart() {
85
+ var video = document.getElementById("v");
86
+ video.currentTime = 0;
87
+ }
88
+
89
+
90
+ var reqId = null;
91
+
92
+ //モノクロ
93
+ function grayscale(){
94
+ cancelRender();
95
+ grayFilter();
96
+ };
97
+ //疑似カラー
98
+ function heatmap(){
99
+ cancelRender();
100
+ heatmap1();
101
+ };
102
+
103
+ function cancelRender() {
104
+ if(reqId) {
105
+ cancelAnimationFrame(reqId);
106
+ }
107
+ }
108
+
109
+ //モノクロ
110
+ function grayFilter(){
111
+ loop();
112
+
113
+ function loop(){
114
+ monokuro();
115
+ reqId = requestAnimationFrame(loop);
116
+ };
117
+
118
+ // 描画内容に対して、式を当てはめながらrgbの値を計算する。
119
+ function monokuro(){
120
+ // 対象のCanvasを取得し、contextも取得する。
121
+ var canvas = document.getElementById("c");
122
+ var context = canvas.getContext("2d");
123
+ video = document.getElementById("v");
124
+
125
+ // Canvasから描画内容を保持するimageDataを取得する。
126
+ var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
127
+
128
+ var d = imageData.data;
129
+ for (var i = 0; i < d.length; i+=4) {
130
+ var g = d[i] * 0.2126 + d[i+1] * 0.7152 + d[i+2] * 0.0722;
131
+ d[i] = d[i+1] = d[i+2] = g;
132
+ // d[i+3]に格納されたα値は変更しない
133
+ }
134
+
135
+
136
+ // 計算結果でCanvasの表示内容を更新する。
137
+ context.putImageData(imageData, 0, 0);
138
+ }
139
+ }
140
+
141
+ //疑似カラー
142
+ function heatmap1(){
143
+ (function () {
144
+ //Canvasの準備
145
+ var canvas = document.getElementById("c");
146
+ var context = canvas.getContext("2d");
147
+ var video = document.getElementById("v");
148
+
149
+ // 境界線とする閾値
150
+ var outlineColor = {r: 255, g: 0, b: 0},
151
+ complementaryColor = {r: 0, g: 255, b: 255},
152
+ colorDistance = 10;
153
+
154
+ draw();
155
+
156
+
157
+ // videoの映像をcanvasに描画する
158
+ function draw() {
159
+ //context.drawImage(video, 0, 0, canvas.width, canvas.height);
160
+ // ここでアウトライン処理をする
161
+ outline();
162
+ requestAnimationFrame(draw);
163
+ };
164
+
165
+ // 境界線とする閾値
166
+ var outlineColor = {r: 255, g: 0, b: 0},
167
+ complementaryColor = {r: 0, g: 255, b: 255},
168
+ colorDistance = 10;
169
+
170
+ // アウトライン処理
171
+ function outline() {
172
+ var imageData = context.getImageData(0, 0, canvas.width, canvas.height),
173
+ data = imageData.data;
174
+
175
+ // dataはUint8ClampedArray
176
+ // 長さはcanvasの width * height * 4(r,g,b,a)
177
+ // 先頭から、一番左上のピクセルのr,g,b,aの値が順に入っており、
178
+ // 右隣のピクセルのr,g,b,aの値が続く
179
+ // n から n+4 までが1つのピクセルの情報となる
180
+
181
+ var currentOutlineColor = outlineColor;
182
+
183
+ for (var i = 0, l = data.length; i < l; i += 4) {
184
+
185
+ // この条件の時、currentは右端の色、nextは1px下の段の左端の色になるので透明にしてスキップする
186
+ if ((i / 4 + 1) % canvas.width === 0) {
187
+ data[i + 3] = 0;
188
+ continue;
189
+ }
190
+
191
+ // 段が変わったら色を変える
192
+ // 一段ずつoutlineColorからcomplementaryColorにグラデーションにする
193
+ if ((i / 4) % canvas.width === 0) {
194
+ var row = (i / 4) / canvas.width,
195
+ r = (outlineColor.r - complementaryColor.r) / canvas.height,
196
+ g = (outlineColor.g - complementaryColor.g) / canvas.height,
197
+ b = (outlineColor.b - complementaryColor.b) / canvas.height;
198
+
199
+ currentOutlineColor = {
200
+ r: outlineColor.r - (r * row),
201
+ g: outlineColor.g - (g * row),
202
+ b: outlineColor.b - (b * row)
203
+ };
204
+ }
205
+
206
+ var currentIndex = i,
207
+ nextIndex = currentIndex + 4,
208
+ underIndex = currentIndex + (canvas.width * 4),
209
+ // チェックするピクセルの色
210
+ current = {
211
+ r: data[currentIndex],
212
+ g: data[currentIndex + 1],
213
+ b: data[currentIndex + 2]
214
+ },
215
+ // 右隣の色
216
+ next = {
217
+ r: data[nextIndex],
218
+ g: data[nextIndex + 1],
219
+ b: data[nextIndex + 2]
220
+ },
221
+ // 下の色
222
+ under = {
223
+ r: data[underIndex],
224
+ g: data[underIndex + 1],
225
+ b: data[underIndex + 2]
226
+ };
227
+
228
+ // 現在のピクセルと右隣、下の色の三次元空間上の距離を閾値と比較する
229
+ // 閾値より大きい(色が遠い)場合、境界線とみなしそのピクセルをcurrentOutlineColorに変更
230
+ // 閾値より小さい(色が近い)場合、そのピクセルを消す
231
+ if (getColorDistance(current, next) > colorDistance || getColorDistance(current, under) > colorDistance) {
232
+ data[i] = currentOutlineColor.r;
233
+ data[i + 1] = currentOutlineColor.g;
234
+ data[i + 2] = currentOutlineColor.b;
235
+ } else {
236
+ // alpha値を0にすることで見えなくする
237
+ data[i + 3] = 0;
238
+ }
239
+ }
240
+
241
+ // 書き換えたdataをimageDataにもどし、描画する
242
+ imageData.data = data;
243
+ context.putImageData(imageData, 0, 0);
244
+ };
245
+
246
+ // r,g,bというkeyを持ったobjectが第一引数と第二引数に渡される想定
247
+ function getColorDistance(rgb1, rgb2) {
248
+ // 三次元空間の距離が返る
249
+ return Math.sqrt(
250
+ Math.pow((rgb1.r - rgb2.r), 2) +
251
+ Math.pow((rgb1.g - rgb2.g), 2) +
252
+ Math.pow((rgb1.b - rgb2.b), 2)
253
+ );
254
+ };
255
+
256
+ var color = document.getElementById('color');
257
+ color.addEventListener('change', function () {
258
+ // フォームの値は16進カラーコードなのでrgb値に変換する
259
+ outlineColor = color2rgb(this.value);
260
+ complementaryColor = getComplementaryColor(outlineColor);
261
+ });
262
+
263
+ var color2rgb = function (color) {
264
+ color = color.replace(/^#/, '');
265
+ return {
266
+ r: parseInt(color.substr(0, 2), 16),
267
+ g: parseInt(color.substr(2, 2), 16),
268
+ b: parseInt(color.substr(4, 2), 16)
269
+ };
270
+ };
271
+
272
+ // 補色の計算
273
+ var getComplementaryColor = function (rgb) {
274
+ var max = Math.max(rgb.r, rgb.g, rgb.b),
275
+ min = Math.min(rgb.r, rgb.g, rgb.b),
276
+ sum = max + min;
277
+ return {
278
+ r: sum - rgb.r,
279
+ g: sum - rgb.g,
280
+ b: sum - rgb.b
281
+ };
282
+ };
283
+
284
+ var distance = document.getElementById('distance');
285
+ distance.style.textAlign = 'right';
286
+ distance.addEventListener('change', function () {
287
+ colorDistance = this.value;
288
+ });
289
+ })();
290
+ }
291
+ </script>
292
+ </body>
293
+ </html>
294
+
295
+ ```
296
+
29
297
  ---
30
298
  requestAnimationFrame を初めて使用しているため
31
299
  何度も質問してしまうかもしれませんが