Forked from: t_furu's javascript だけでカメラから画像を取り込む View Diff (1) forked: javascript だけでカメラから画像を取り込む teetteet Follow 2013-02-26 21:31:20 License: MIT License Fork0 Fav0 View1136 Play Stop Reload Fullscreen Smart Phone Readme JavaScript 332 lines HTML 7 lines CSS 24 lines forked: javascript だけでカメラから画像を取り込む jQuery v1.9.0 // forked from t_furu's "javascript だけでカメラから画像を取り込む" http://jsdo.it/t_furu/fkXR var Page = function(){ this.localMediaStream = undefined; this.request = undefined; this.video = undefined; this.src_ctx = undefined; this.dest_ctx = undefined; //video の width,height this.video_size = {width:300,height:300}; this.init = function(){ this.browser_init(); $("#btnCapture").click($.proxy(this.clickBtnCapture,this)); $("#btnStop").click($.proxy(this.clickBtnStop,this)); }; // 各ブラウザ対応 this.browser_init = function(){ //リクエスト アニメーションフレーム window.requestAnimationFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback, element){ return window.setTimeout(callback, 1000 / 60); }; })(); //キャンセル アニメーションフレーム window.cancelRequestAnimFrame = ( function() { return window.cancelAnimationFrame || window.webkitCancelRequestAnimationFrame || window.mozCancelRequestAnimationFrame || window.oCancelRequestAnimationFrame || window.msCancelRequestAnimationFrame || clearTimeout } )(); window.URL = window.URL || window.webkitURL; window.navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; }; /* カメラが使えるかチェック**/ this.hasGetUserMedia = function() { return !!navigator.getUserMedia; }; /** キャプチャー開始 **/ this.clickBtnCapture = function(t){ var flg = this.hasGetUserMedia(); if(flg == false){ alert("getUserMedia に 未対応のブラウザ"); } navigator.getUserMedia({video: true},$.proxy(this.callbackGetUserMedia,this), this.onFailSoHard); }; /** キャプチャ ストップ**/ this.clickBtnStop = function(t){ if(!this.localMediaStream) return; this.localMediaStream.stop(); window.cancelRequestAnimFrame(this.request); }; /** カメラキャプチャ開始失敗 **/ this.onFailSoHard = function(e) { console.log('エラー!', e); }; this.callbackGetUserMedia = function(stream) { this.localMediaStream = stream; this.video =$("<video>")[0]; this.video.width = this.video_size["width"]; this.video.height = this.video_size["height"]; this.video.src = window.URL.createObjectURL(stream); this.video.play(); var src_canvas = $("<canvas>")[0]; src_canvas.width = this.video.width; src_canvas.height = this.video.height; this.src_ctx = src_canvas.getContext('2d'); var canvas = $("#canvas")[0]; canvas.width = this.video.width; canvas.height = this.video.height; this.dest_ctx = canvas.getContext('2d'); $.proxy(this.animloop,this)(); }; /* アニメーションループ */ this.animloop = function(){ this.capture(); this.request = window.requestAnimationFrame($.proxy(this.animloop,this)); }; /** キャプチャした画像をキャンバスに描画する */ this.capture = function(){ this.src_ctx.drawImage(this.video, 0, 0); var imgData = this.src_ctx.getImageData(0, 0, this.video.width, this.video.height); //ここで色んなフィルタ処理をしてみる //平滑化+白黒化 imgData = this.filterSmoothing(imgData); //二値化 imgData = this.filterThreshold(imgData,128); //ラベリング var m = ((this.video.width * this.video.height)/2)|0; var label = this.labeling(imgData,4, m); //ラベル毎に色分け してみる //imgData = this.filterTest1(imgData,label); //var imgDataColor = this.src_ctx.getImageData(0, 0, this.video.width, this.video.height); //this.dest_ctx.putImageData(imgDataColor,0,0); this.dest_ctx.putImageData(imgData,0,0); //ラベル毎に枠表示 してみる this.filterTest2(this.dest_ctx,imgData,label); }; /** 平滑化+白黒化 フィルタ **/ this.filterSmoothing = function(imgData){ var ret = imgData; var i=0,j=0,k=0,l=0,m=0,n=0; var r=0,g=0,b=0,v=0; var w = imgData.width; var h = imgData.height; for (i = 1; i < h-1; i++) { for (j = 1; j<w-1; j++) { r=0;g=0;b=0;v=0; k = (i * w + j) * 4; //3x3の合計値を求める for (l = -1; l <= 1; l++) { for ( m = -1; m <= 1; m++) { n = k + (l * w + m) * 4; r += ret.data[n]; g += ret.data[n + 1]; b += ret.data[n + 2]; } } //3x3の平均値を求める r = (r/9)|0; //Math.floor(r/9); g = (g/9)|0; //Math.floor(g/9); b = (b/9)|0; //Math.floor(b/9); //単色化 擬似輝度を計算 v = (3*r+6*g+b)/10; ret.data[k] = v; ret.data[k + 1] = v;//v ret.data[k + 2] = v;//v } } return ret; }; /** 2値化+反転 フィルタ**/ this.filterThreshold = function(imgData,t){ var ret = imgData; var i = 0,v = 0; var len = ret.data.length; for(i = 0;i<len;i+=4){ v = (ret.data[i] < t )?255:0; ret.data[i] = v; ret.data[i + 1] = v; ret.data[i + 2] = v; } return ret; }; /** ラベリング **/ this.labeling = function(imgData,min_dot,max_dot){ var w = imgData.width; var h = imgData.height; var data = imgData.data; var reflabel = [],label = []; var dot=[]; var x=[],y=[]; var width=[],height=[]; var i=0;j=0; var k=0,k1=0,k2=0,r1=0,r2=0,v1=0,v2=0; var l=1,d=0; for (i = 1; i < h-1; i++) { for (j = 1; j<w-1; j++) { //1,白を見つける k = (i * w + j)*4; label[k] = 0; if(data[k] == 255){ //2. 左,上 の色を習得 k1 = (i * w + (j-1)) * 4; r1 = data[k1]; k2 = ((i-1) * w + j) * 4; r2 = data[k2]; if((r1 == 0)&&(r2 == 0)){ //3. r1,r2が黒の場合 ラベルを1増やして配列に登録 l += 1; reflabel[l] = l; //left,top を保存 x[ reflabel[l] ] = j; y[ reflabel[l] ] = i; } else if((r1 == 0)&&(r2 == 255)){ //4. 上のみ白 //6. 上と同じラベル reflabel[l] = (typeof label[k2] == "undefined")?0:label[k2]; } else if((r1 == 255)&&(r2 == 0)) { //4. 左のみ白 //5. 左と同じラベル reflabel[l] = (typeof label[k1] == "undefined")?0:label[k1]; } else if((r1 == 255)&&(r2 == 255)){ //7.両方が白の場合 値が小さなラベル v1 = (typeof label[k1] == "undefined")?0:label[k1]; v2 = (typeof label[k2] == "undefined")?0:label[k2]; reflabel[l] = (v1<v2)?v1:v2; //TODO 大きな方を 小さい方の値でかきかえる label[k1] = label[k2] = reflabel[l]; } if(typeof reflabel[l] == "undefined") reflabel[l] = 0; label[k] = reflabel[l]; //ドット数の計算 dot[ reflabel[l] ] = (typeof dot[ reflabel[l] ] == "undefined")?1:dot[ reflabel[l] ]+1; //w,h 幅と高さを保存 width[ reflabel[l] ] = j - x[ reflabel[l] ]; height[ reflabel[l] ] = i - y[ reflabel[l] ]; } } } //console.log($.extend({}, reflabel)); //TODO 参照先整理 //console.log("reflabel"); //console.log(reflabel); var cx=0,cy=0; for(l in label){ var v = label[l]; //ラベル0 以外 , ドット数の確認 if((v == 0) || (dot[v] <= min_dot) || (dot[v] >= max_dot)){ delete label[l]; continue; } cx = x[v]+ ((width[v]/2)|0); //Math.floor(width[v]/2); cy = y[v]+ ((height[v]/2)|0); //Math.floor(height[v]/2); label[l] = {label:v, dot: dot[v], x:x[v],y:y[v], w:width[v],h:height[v], cx:cx,cy:cy}; } //console.log("label"); //console.log(label); return label; }; /** ラベル毎に色分けしてみる **/ this.filterTest1 = function(imgData,label){ var ret = imgData; var w = imgData.width; var h = imgData.height; var old=-1, r=0,g=0,b=0; var color = []; var i =0,j=0; for (i = 0; i < h; i++) { for (j = 0; j<w; j++) { k = (i * w + j) * 4; ret.data[k] = 255; ret.data[k+1] = 255; ret.data[k+2] = 255; if(typeof label[k] == "undefined") continue; var v = label[k].label; if(v !== 0){ if(old != v){ if( typeof color[v] == "undefined"){ //色を新規指定 r = Math.floor( Math.random() * 255 ); g = Math.floor( Math.random() * 255 ); b = Math.floor( Math.random() * 255 ); color[v] = {r:r,g:g,b:b}; //console.log( {label:label[k],color:color[v]} ); } old = v; } ret.data[k] = color[v].r; ret.data[k+1] = color[v].g; ret.data[k+2] = color[v].b; } } } return ret; }; /** ラベルのサイズに枠を描画してみる **/ this.filterTest2 = function(ctx,imgData,label){ var w = imgData.width; var h = imgData.height; ctx.strokeStyle = 'rgb(0, 0, 255)'; var old=-1,k=0; var i =0,j=0; for (i = 0; i < h; i++) { for (j = 0; j<w; j++) { k = (i * w + j) * 4; if(typeof label[k] == "undefined") continue; var v = label[k].label; if(v !== 0){ if(old != v){ ctx.strokeRect( label[k].x, label[k].y, label[k].w, label[k].h); old = v; } } } } }; }; var p = new Page(); $(document).ready(function(){ p.init(); }); <div id="btn_container"> <input type="button" id="btnCapture" value="Capture"> <input type="button" id="btnStop" value="Stop"> </div> <div id="canvas_container"> <canvas id="canvas" width="0" height="0"></canvas> </div> forked: javascript だけでカメラから画像を取り込む * { margin: 0; padding: 0; /* border: 0; */ } body { background: #fdf; } #btn_container{ width:100%; } #canvas_container{ width:100%; } #canvas{ /* margin: 0 auto; */ display:block; background: #000; } // forked from t_furu's "javascript だけでカメラから画像を取り込む" http://jsdo.it/t_furu/fkXR var Page = function(){ this.localMediaStream = undefined; this.request = undefined; this.video = undefined; this.src_ctx = undefined; this.dest_ctx = undefined; //video の width,height this.video_size = {width:300,height:300}; this.init = function(){ this.browser_init(); $("#btnCapture").click($.proxy(this.clickBtnCapture,this)); $("#btnStop").click($.proxy(this.clickBtnStop,this)); }; // 各ブラウザ対応 this.browser_init = function(){ //リクエスト アニメーションフレーム window.requestAnimationFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback, element){ return window.setTimeout(callback, 1000 / 60); }; })(); //キャンセル アニメーションフレーム window.cancelRequestAnimFrame = ( function() { return window.cancelAnimationFrame || window.webkitCancelRequestAnimationFrame || window.mozCancelRequestAnimationFrame || window.oCancelRequestAnimationFrame || window.msCancelRequestAnimationFrame || clearTimeout } )(); window.URL = window.URL || window.webkitURL; window.navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; }; /* カメラが使えるかチェック**/ this.hasGetUserMedia = function() { return !!navigator.getUserMedia; }; /** キャプチャー開始 **/ this.clickBtnCapture = function(t){ var flg = this.hasGetUserMedia(); if(flg == false){ alert("getUserMedia に 未対応のブラウザ"); } navigator.getUserMedia({video: true},$.proxy(this.callbackGetUserMedia,this), this.onFailSoHard); }; /** キャプチャ ストップ**/ this.clickBtnStop = function(t){ if(!this.localMediaStream) return; this.localMediaStream.stop(); window.cancelRequestAnimFrame(this.request); }; /** カメラキャプチャ開始失敗 **/ this.onFailSoHard = function(e) { console.log('エラー!', e); }; this.callbackGetUserMedia = function(stream) { this.localMediaStream = stream; this.video =$("<video>")[0]; this.video.width = this.video_size["width"]; this.video.height = this.video_size["height"]; this.video.src = window.URL.createObjectURL(stream); this.video.play(); var src_canvas = $("<canvas>")[0]; src_canvas.width = this.video.width; src_canvas.height = this.video.height; this.src_ctx = src_canvas.getContext('2d'); var canvas = $("#canvas")[0]; canvas.width = this.video.width; canvas.height = this.video.height; this.dest_ctx = canvas.getContext('2d'); $.proxy(this.animloop,this)(); }; /* アニメーションループ */ this.animloop = function(){ this.capture(); this.request = window.requestAnimationFrame($.proxy(this.animloop,this)); }; /** キャプチャした画像をキャンバスに描画する */ this.capture = function(){ this.src_ctx.drawImage(this.video, 0, 0); var imgData = this.src_ctx.getImageData(0, 0, this.video.width, this.video.height); //ここで色んなフィルタ処理をしてみる //平滑化+白黒化 imgData = this.filterSmoothing(imgData); //二値化 imgData = this.filterThreshold(imgData,128); //ラベリング var m = ((this.video.width * this.video.height)/2)|0; var label = this.labeling(imgData,4, m); //ラベル毎に色分け してみる //imgData = this.filterTest1(imgData,label); //var imgDataColor = this.src_ctx.getImageData(0, 0, this.video.width, this.video.height); //this.dest_ctx.putImageData(imgDataColor,0,0); this.dest_ctx.putImageData(imgData,0,0); //ラベル毎に枠表示 してみる this.filterTest2(this.dest_ctx,imgData,label); }; /** 平滑化+白黒化 フィルタ **/ this.filterSmoothing = function(imgData){ var ret = imgData; var i=0,j=0,k=0,l=0,m=0,n=0; var r=0,g=0,b=0,v=0; var w = imgData.width; var h = imgData.height; for (i = 1; i < h-1; i++) { for (j = 1; j<w-1; j++) { r=0;g=0;b=0;v=0; k = (i * w + j) * 4; //3x3の合計値を求める for (l = -1; l <= 1; l++) { for ( m = -1; m <= 1; m++) { n = k + (l * w + m) * 4; r += ret.data[n]; g += ret.data[n + 1]; b += ret.data[n + 2]; } } //3x3の平均値を求める r = (r/9)|0; //Math.floor(r/9); g = (g/9)|0; //Math.floor(g/9); b = (b/9)|0; //Math.floor(b/9); //単色化 擬似輝度を計算 v = (3*r+6*g+b)/10; ret.data[k] = v; ret.data[k + 1] = v;//v ret.data[k + 2] = v;//v } } return ret; }; /** 2値化+反転 フィルタ**/ this.filterThreshold = function(imgData,t){ var ret = imgData; var i = 0,v = 0; var len = ret.data.length; for(i = 0;i<len;i+=4){ v = (ret.data[i] < t )?255:0; ret.data[i] = v; ret.data[i + 1] = v; ret.data[i + 2] = v; } return ret; }; /** ラベリング **/ this.labeling = function(imgData,min_dot,max_dot){ var w = imgData.width; var h = imgData.height; var data = imgData.data; var reflabel = [],label = []; var dot=[]; var x=[],y=[]; var width=[],height=[]; var i=0;j=0; var k=0,k1=0,k2=0,r1=0,r2=0,v1=0,v2=0; var l=1,d=0; for (i = 1; i < h-1; i++) { for (j = 1; j<w-1; j++) { //1,白を見つける k = (i * w + j)*4; label[k] = 0; if(data[k] == 255){ //2. 左,上 の色を習得 k1 = (i * w + (j-1)) * 4; r1 = data[k1]; k2 = ((i-1) * w + j) * 4; r2 = data[k2]; if((r1 == 0)&&(r2 == 0)){ //3. r1,r2が黒の場合 ラベルを1増やして配列に登録 l += 1; reflabel[l] = l; //left,top を保存 x[ reflabel[l] ] = j; y[ reflabel[l] ] = i; } else if((r1 == 0)&&(r2 == 255)){ //4. 上のみ白 //6. 上と同じラベル reflabel[l] = (typeof label[k2] == "undefined")?0:label[k2]; } else if((r1 == 255)&&(r2 == 0)) { //4. 左のみ白 //5. 左と同じラベル reflabel[l] = (typeof label[k1] == "undefined")?0:label[k1]; } else if((r1 == 255)&&(r2 == 255)){ //7.両方が白の場合 値が小さなラベル v1 = (typeof label[k1] == "undefined")?0:label[k1]; v2 = (typeof label[k2] == "undefined")?0:label[k2]; reflabel[l] = (v1<v2)?v1:v2; //TODO 大きな方を 小さい方の値でかきかえる label[k1] = label[k2] = reflabel[l]; } if(typeof reflabel[l] == "undefined") reflabel[l] = 0; label[k] = reflabel[l]; //ドット数の計算 dot[ reflabel[l] ] = (typeof dot[ reflabel[l] ] == "undefined")?1:dot[ reflabel[l] ]+1; //w,h 幅と高さを保存 width[ reflabel[l] ] = j - x[ reflabel[l] ]; height[ reflabel[l] ] = i - y[ reflabel[l] ]; } } } //console.log($.extend({}, reflabel)); //TODO 参照先整理 //console.log("reflabel"); //console.log(reflabel); var cx=0,cy=0; for(l in label){ var v = label[l]; //ラベル0 以外 , ドット数の確認 if((v == 0) || (dot[v] <= min_dot) || (dot[v] >= max_dot)){ delete label[l]; continue; } cx = x[v]+ ((width[v]/2)|0); //Math.floor(width[v]/2); cy = y[v]+ ((height[v]/2)|0); //Math.floor(height[v]/2); label[l] = {label:v, dot: dot[v], x:x[v],y:y[v], w:width[v],h:height[v], cx:cx,cy:cy}; } //console.log("label"); //console.log(label); return label; }; /** ラベル毎に色分けしてみる **/ this.filterTest1 = function(imgData,label){ var ret = imgData; var w = imgData.width; var h = imgData.height; var old=-1, r=0,g=0,b=0; var color = []; var i =0,j=0; for (i = 0; i < h; i++) { for (j = 0; j<w; j++) { k = (i * w + j) * 4; ret.data[k] = 255; ret.data[k+1] = 255; ret.data[k+2] = 255; if(typeof label[k] == "undefined") continue; var v = label[k].label; if(v !== 0){ if(old != v){ if( typeof color[v] == "undefined"){ //色を新規指定 r = Math.floor( Math.random() * 255 ); g = Math.floor( Math.random() * 255 ); b = Math.floor( Math.random() * 255 ); color[v] = {r:r,g:g,b:b}; //console.log( {label:label[k],color:color[v]} ); } old = v; } ret.data[k] = color[v].r; ret.data[k+1] = color[v].g; ret.data[k+2] = color[v].b; } } } return ret; }; /** ラベルのサイズに枠を描画してみる **/ this.filterTest2 = function(ctx,imgData,label){ var w = imgData.width; var h = imgData.height; ctx.strokeStyle = 'rgb(0, 0, 255)'; var old=-1,k=0; var i =0,j=0; for (i = 0; i < h; i++) { for (j = 0; j<w; j++) { k = (i * w + j) * 4; if(typeof label[k] == "undefined") continue; var v = label[k].label; if(v !== 0){ if(old != v){ ctx.strokeRect( label[k].x, label[k].y, label[k].w, label[k].h); old = v; } } } } }; }; var p = new Page(); $(document).ready(function(){ p.init(); }); <div id="btn_container"> <input type="button" id="btnCapture" value="Capture"> <input type="button" id="btnStop" value="Stop"> </div> <div id="canvas_container"> <canvas id="canvas" width="0" height="0"></canvas> </div> * { margin: 0; padding: 0; /* border: 0; */ } body { background: #fdf; } #btn_container{ width:100%; } #canvas_container{ width:100%; } #canvas{ /* margin: 0 auto; */ display:block; background: #000; } use an iframe compat browser, deer Play on jsdo.it games Author Share ブログに埋め込む QR Tag Download Complete! Description What kind of game? Control Device Smartphone Controllerjsdo.it WebSocket Controller» Mouse Keyboard Touch Device Fullscreen Activated Inactivated jsdo.it games から削除する Submit Author teetteet Tweet Default Panel Auto play Screenshot Readme JavaScript HTML CSS Size Width: px Height: px code <script type="text/javascript" src="http://jsdo.it/blogparts/j9Ue/js"></script> Camera Canvas HTML5 html5_elements&api library&test Discussion Questions on this code? Tags Camera Canvas HTML5 html5_elements&api library&test