質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

304閲覧

動画の輪郭の抽出について

west826

総合スコア14

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2017/12/17 04:35

やりたいこと
動画の輪郭を抽出して選択した色により
輪郭線の色が変わるようなものを作ろうとしています。

うまくいかない点
カラーを選択すると、一瞬は選択した色になるのですが
すぐにデフォルトの色に戻ってしまいます。
こういう場合はどうしたら良いのでしょうか。
教えていただけると幸いです。

HTML

1<!DOCTYPE html> 2<html lang="ja"> 3 <head> 4 <meta charset="utf-8"> 5 <title>test</title> 6 7 <style type="text/css"> 8 #v{ 9 display:none; 10 } 11 #screen{ 12 width:480px; 13 height:270px; 14 background-color:#000000; 15 } 16 button{ 17 width:100px; 18 height:50px; 19 } 20 21 </style> 22 </head> 23 24 <body> 25 <video controls id="v" width="480" height="270"> 26 <source src="movie.mp4" width="480" height="270"> 27 </video> 28 <div id="screen"> 29 <canvas id="c" width="480" height="270"></canvas> 30 </div> 31 <button onClick="playVideo()">play/stop</button> 32 <button onClick="restart()">restart</button> 33 <button onClick="heatmap()">疑似カラー</button> 34 35 <input id="color" type="color" value="#ff0000" /> 36 <input id="distance" type="number" value="10" /> 37 38 39 40 <script type="text/javascript"> 41 42 //play/stopボタンの設定 43function playVideo(){ 44 var video = document.getElementById("v"); 45 if(video.paused){ 46 video.play(); 47 }else{ 48 video.pause(); 49 } 50 setInterval(function(){ 51 var canvas = document.getElementById("c"); 52 canvas.getContext("2d").drawImage(video, 0, 0, 480, 270); 53 }, 1000/30); 54} 55 //restartボタンの設定 56function restart() { 57 var video = document.getElementById("v"); 58 video.currentTime = 0; 59} 60 61 //疑似カラー 62function heatmap(){ 63 setInterval(function(){ 64(function () { 65 //Canvasの準備 66 var canvas = document.getElementById("c"); 67 var context = canvas.getContext("2d"); 68 var video = document.getElementById("v"); 69 70 // 境界線とする閾値 71 var outlineColor = {r: 255, g: 0, b: 0}, 72 complementaryColor = {r: 0, g: 255, b: 255}, 73 colorDistance = 10; 74 75 draw(); 76 77 78 // videoの映像をcanvasに描画する 79 function draw() { 80 context.drawImage(video, 0, 0, canvas.width, canvas.height); 81 // ここでアウトライン処理をする 82 outline(); 83 requestAnimationFrame(draw); 84 }; 85 86 // 境界線とする閾値 87 var outlineColor = {r: 255, g: 0, b: 0}, 88 complementaryColor = {r: 0, g: 255, b: 255}, 89 colorDistance = 10; 90 91 // アウトライン処理 92 function outline() { 93 var imageData = context.getImageData(0, 0, canvas.width, canvas.height), 94 data = imageData.data; 95 96 // dataはUint8ClampedArray 97 // 長さはcanvasの width * height * 4(r,g,b,a) 98 // 先頭から、一番左上のピクセルのr,g,b,aの値が順に入っており、 99 // 右隣のピクセルのr,g,b,aの値が続く 100 // n から n+4 までが1つのピクセルの情報となる 101 102 var currentOutlineColor = outlineColor; 103 104 for (var i = 0, l = data.length; i < l; i += 4) { 105 106 // この条件の時、currentは右端の色、nextは1px下の段の左端の色になるので透明にしてスキップする 107 if ((i / 4 + 1) % canvas.width === 0) { 108 data[i + 3] = 0; 109 continue; 110 } 111 112 // 段が変わったら色を変える 113 // 一段ずつoutlineColorからcomplementaryColorにグラデーションにする 114 if ((i / 4) % canvas.width === 0) { 115 var row = (i / 4) / canvas.width, 116 r = (outlineColor.r - complementaryColor.r) / canvas.height, 117 g = (outlineColor.g - complementaryColor.g) / canvas.height, 118 b = (outlineColor.b - complementaryColor.b) / canvas.height; 119 120 currentOutlineColor = { 121 r: outlineColor.r - (r * row), 122 g: outlineColor.g - (g * row), 123 b: outlineColor.b - (b * row) 124 }; 125 } 126 127 var currentIndex = i, 128 nextIndex = currentIndex + 4, 129 underIndex = currentIndex + (canvas.width * 4), 130 // チェックするピクセルの色 131 current = { 132 r: data[currentIndex], 133 g: data[currentIndex + 1], 134 b: data[currentIndex + 2] 135 }, 136 // 右隣の色 137 next = { 138 r: data[nextIndex], 139 g: data[nextIndex + 1], 140 b: data[nextIndex + 2] 141 }, 142 // 下の色 143 under = { 144 r: data[underIndex], 145 g: data[underIndex + 1], 146 b: data[underIndex + 2] 147 }; 148 149 // 現在のピクセルと右隣、下の色の三次元空間上の距離を閾値と比較する 150 // 閾値より大きい(色が遠い)場合、境界線とみなしそのピクセルをcurrentOutlineColorに変更 151 // 閾値より小さい(色が近い)場合、そのピクセルを消す 152 if (getColorDistance(current, next) > colorDistance || getColorDistance(current, under) > colorDistance) { 153 data[i] = currentOutlineColor.r; 154 data[i + 1] = currentOutlineColor.g; 155 data[i + 2] = currentOutlineColor.b; 156 } else { 157 // alpha値を0にすることで見えなくする 158 data[i + 3] = 0; 159 } 160 } 161 162 // 書き換えたdataをimageDataにもどし、描画する 163 imageData.data = data; 164 context.putImageData(imageData, 0, 0); 165 }; 166 167 // r,g,bというkeyを持ったobjectが第一引数と第二引数に渡される想定 168 function getColorDistance(rgb1, rgb2) { 169 // 三次元空間の距離が返る 170 return Math.sqrt( 171 Math.pow((rgb1.r - rgb2.r), 2) + 172 Math.pow((rgb1.g - rgb2.g), 2) + 173 Math.pow((rgb1.b - rgb2.b), 2) 174 ); 175 }; 176 177 var color = document.getElementById('color'); 178 color.addEventListener('change', function () { 179 // フォームの値は16進カラーコードなのでrgb値に変換する 180 outlineColor = color2rgb(this.value); 181 complementaryColor = getComplementaryColor(outlineColor); 182 }); 183 184 var color2rgb = function (color) { 185 color = color.replace(/^#/, ''); 186 return { 187 r: parseInt(color.substr(0, 2), 16), 188 g: parseInt(color.substr(2, 2), 16), 189 b: parseInt(color.substr(4, 2), 16) 190 }; 191 }; 192 193 // 補色の計算 194 var getComplementaryColor = function (rgb) { 195 var max = Math.max(rgb.r, rgb.g, rgb.b), 196 min = Math.min(rgb.r, rgb.g, rgb.b), 197 sum = max + min; 198 return { 199 r: sum - rgb.r, 200 g: sum - rgb.g, 201 b: sum - rgb.b 202 }; 203 }; 204 205 var distance = document.getElementById('distance'); 206 distance.style.textAlign = 'right'; 207 distance.addEventListener('change', function () { 208 colorDistance = this.value; 209 }); 210 211})(); 212 }, 1000/30); 213} 214 215 </script> 216 </body> 217</html> 218

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

setIntervalメソッドとrequestAnimationFrameメソッドの呼び出しを複数ヶ所で行っていることにより, 目的としたグラフィック出力を意図しないスケジューラ処理で上書きしています.

これは完全にプログラム設計上の問題ですから, どこで何をさせるのかについてもう一度整理したほうが良いでしょう.

HINT:

  • setInterval/requestAnimationFrameによるvideo映像のcanvas要素への転写処理のスケジューリングは一箇所で済むはずです.
  • 現在playVideo関数とheatmap関数内部でそれぞれ転写処理を追加スケジュールしているので, このままだとビデオの開始・再開を繰り返すことでいずれクラッシュします.

投稿2017/12/17 05:21

編集2017/12/17 05:24
defghi1977

総合スコア4756

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

west826

2017/12/17 06:51

ありがとうございます。 出来ました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問