Skip to content

Instantly share code, notes, and snippets.

@denkiwakame
Created May 22, 2014 15:49
Show Gist options
  • Save denkiwakame/8ff7748ec14c557e97ec to your computer and use it in GitHub Desktop.
Save denkiwakame/8ff7748ec14c557e97ec to your computer and use it in GitHub Desktop.
わかめカメラ
<!DOCTYPE html>
<html lang="en">
<head>
<title>active background</title>
<style>
<!--
html body{
background-color: #cccccc;
height:100%;
}
#main {
position: relative;
}
div.continer {
height: 100%;
}
h4.pattern {
text-align: center;
}
#contents {
padding-top: 50px;
}
div.jumbotron {
margin-bottom:0px;
background-color: #00ddee;
height: 500px;
color: #ffffff;
}
#startbtn {
height: 80px;
}
.row .row {
margin:10px;
}
#canvas {
padding: 0;
margin: 0;
}
#js-live-preview {
width: 100%;
height: 100%;
margin: auto;
}
-->
</style>
<script src="http://code.jquery.com/jquery-1.9.0.js"></script>
<script src="http://code.jquery.com/jquery-migrate-1.0.0.js"></script>
<!--
<script type="text/javascript" src="ab.js"></script>
-->
<script type="text/javascript" src="wacam.js"></script>
<!-- Bootstrap -->
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<div id="main" class="container">
<div id="contents" class="row">
<div class="col-md-6">
<!-- Preview -->
<img id="js-live-preview-img" src="" width="100%" style="display:none"></img>
<canvas id="canvas-preview" style="display:none" width="480" height="360"></canvas>
<div class="js-live-preview-params" style="display:none"></div>
<!-- placeholder -->
<div id="logo" class="jumbotron">
<h1><strong>Active<br \> background</strong></h1>
<span class="glyphicon glyphicon-globe"> supports Google Chrome / Firefox</span>
</div>
</div>
<div class="col-md-6">
<div class="row well">
<div class="col-md-7">
<div class="input-group row">
<span class="input-group-addon">persec</span>
<select id="pv-persec" name="persec" class="form-control">
<option>0.2</option>
<option>0.5</option>
<option>1</option>
</select>
</div>
</div>
<div class="col-md-5">
<div class="row">
<button id="pv-capture" class="btn btn-default btn-md col-lg-12" type="button">
<span class="glyphicon glyphicon-facetime-video"></span> testcap
</button>
<button id="pv-capture-stop" class="btn btn-danger btn-md col-lg-12" type="button" style="display:none">
<span class="glyphicon glyphicon-ban-circle"></span> testcap
</button>
</div>
</div>
</div>
<div class="row well">
<div class="col-md-7">
<div class="input-group row">
<span class="input-group-addon">cam</span>
<select id="gc-cam" name="cam" class="form-control">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
<span class="input-group-addon">set</span>
<select id="gc-set" name="set" class="form-control">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
<option>6</option>
<option>7</option>
<option>8</option>
<option>9</option>
<option>10</option>
<option>11</option>
<option>12</option>
</select>
</div>
<div class="input-group row">
<span class="input-group-addon">width&nbsp;</span>
<select id="gc-width" name="width" class="form-control">
<option>2048</option>
<option>1024</option>
<option>512</option>
<option>4096</option>
</select>
</div>
<div class="input-group row">
<span class="input-group-addon">height</span>
<select id="gc-height" name="height" class="form-control">
<option>1024</option>
<option>512</option>
<option>4096</optionv>
<option>2048</option>
</select>
</div>
<div class="input-group row">
<span class="input-group-addon">shutter_speeds</span>
<select id="gc-shutter" name="shutter" class="form-control">
<option>multi</option>
<option>off</option>
</select>
</div>
</div> <!-- col-md-7 -->
<div class="col-md-5">
<div class="row">
<button id="gc-demo" class="btn btn-default btn-sm col-md-12" type="button">
<span class="glyphicon glyphicon-picture"></span> demo
</button>
</div>
<div class="row">
<button id="gc-capture" class="btn btn-primary btn-lg col-md-12" type="button">
<span class="glyphicon glyphicon-camera"></span> capture
</button>
</div>
</div> <!-- col-md-5 -->
</div>
<div class="row well">
<div class="col-md-7">
<div class="input-group row">
<span class="input-group-addon">row x col</span>
<select id="cb-row-col" name="rowxcol" class="form-control">
<option>5x8</option>
<option>7x10</option>
<option>11x16</option>
</select>
</div>
<div class="input-group row">
<span class="input-group-addon">unit [px]</span>
<select id="cb-unit-px" name="rowxcol" class="form-control">
<option>100</option>
<option>150</option>
<option>50</option>
</select>
</div>
<div class="input-group row">
<span class="input-group-addon">times</span>
<select id="cb-times" name="times" class="form-control">
<option>20</option>
<option>10</option>
<option>30</option>
</select>
</div>
</div>
<div class="col-md-5">
<div class="row">
<button id="cb-demo" class="btn btn-default btn-sm col-md-12" type="button">
<span class="glyphicon glyphicon-picture"></span> demo
</button>
</div>
<div class="row">
<button id="cb-capture" class="btn btn-success btn-lg col-md-12" type="button">
<span class="glyphicon glyphicon-camera"></span> capture
</button>
</div>
</div>
</div><!-- row -->
</div><!-- col-md-7 -->
</div><!-- row -->
</div><!-- container -->
<canvas id="canvas"></canvas>
</body>
</html>
$(function(){
// util func
Util = {
getParams: function(){
var params_str = location.search.slice(1); // delete "?"
if (!params_str) return {};
var params_ary = decodeURIComponent(params_str).split("&");
var params = {};
for(var i=0; i<params_ary.length; i++){
var key_val = params_ary[i].split("=");
params[key_val[0]] = key_val[1].split("/")[0];
}
return params;
},
// XXX イベントにつけないと作動しない
onFullscreen: function(){
canvas = document.getElementById("canvas");
if (canvas.webkitRequestFullscreen){
canvas.webkitRequestFullscreen();
} else if (canvas.mozRequestFullScreen){
canvas.mozRequestFullScreen();
} else {
alert("use google chrome or mozzila fire fox");
}
},
// XXX Exit するときはdocument要素でないとX
offFullscreen: function(){
if (document.webkitExitFullscreen){
document.webkitExitFullscreen();
}
},
errLog: function(log){
msg = "ERROR: " + log;
console.log(msg);
},
alignNum: function(num){
var num = Number(num);
if (num < 10) return "00" + num;
if (num < 100) return "0" + num;
return String(num);
},
};
// prepare canvas
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
if (!canvas || !canvas.getContext) alert('this browser does not support canvas');
var GrayCode = {
width: 2048,
height: 1024,
xSplitNum: Math.log(2048) / Math.log(2),
ySplitNum: Math.log(1024) / Math.log(2),
fill: function(col){
if (col){
ctx.fillStyle = 'rgb(255,255,255)';
} else {
ctx.fillStyle = 'rgb(0,0,0)';
}
ctx.fillRect(0,0,canvas.width,canvas.height);
},
_draw: function(x1,y1,w1,h1,x2,y2,w2,h2){
ctx.fillStyle = 'rgb(0,0,0)';
ctx.fillRect(x1, y1, w1, h1);
ctx.fillStyle = 'rgb(255,255,255)';
ctx.fillRect(x2, y2, w2, h2);
},
_drawGrayCode: function(x,y,w,h,XorY,bit,isAnti){
var gc = ( XorY == 'X' ) ? x ^ (x >> 1) : y ^ (y >> 1);
if(!isAnti){ // NORMAL
ctx.fillStyle = gc & (1 << (bit-1)) ? 'rgb(255,255,255)' : 'rgb(0,0,0)';
} else { // anti-pattern
console.log('anti pattern!');
ctx.fillStyle = gc & (1 << (bit-1)) ? 'rgb(0,0,0)' : 'rgb(255,255,255)';
}
ctx.fillRect(x, y, w, h);
},
splitY: function(y, width, height, num, target_num, isAnti) { // ===
if (target_num > num) {
this.splitY(y, width, height/2, num+1, target_num, isAnti);
this.splitY(y+height/2, width, height/2, num+1, target_num, isAnti);
} else {
//GrayCode._draw(0, y, width, height/2, 0, y+height/2, width, height/2);
GrayCode._drawGrayCode(0, y, width, height/2, 'Y', GrayCode.ySplitNum - target_num, isAnti);
GrayCode._drawGrayCode(0, y+height/2, width, height/2, 'Y', GrayCode.ySplitNum - target_num, isAnti);
}
},
splitX: function(x, width, height, num, target_num, isAnti) { // ||
if (target_num > num) {
this.splitX(x, width/2, height, num+1, target_num, isAnti);
this.splitX(x+width/2, width/2, height, num+1, target_num, isAnti);
} else {
//GrayCode._draw(x, 0, width/2, height, x+width/2, 0, width/2, height);
GrayCode._drawGrayCode(x, 0, width/2, height, 'X', GrayCode.xSplitNum - target_num, isAnti);
GrayCode._drawGrayCode(x+width/2, 0, width/2, height, 'X', GrayCode.xSplitNum - target_num, isAnti);
}
},
getImageData: function(){
try {
var imageData = canvas.toDataURL("image/png");
} catch(e) {
var imageData = "fails";
}
return imageData;
},
};
// TODO
var shutterSpeeds = {};
var mode = { 'blink': 0, 'splitX': 1, 'splitY': 2, 'antiSplitX': 3, 'antiSplitY':4, 'END': 5 };
var Sync = {
cam: 1,
set: 1,
capture: 0,
pattern_num: 0,
//shutter_speeds: [ 1, 10, 50, 100 ], // TODO
shutter_speeds: [ 1, 10, 50 ], // TODO
mode: mode['blink'], // init
//mode: mode['splitX'], // init
setUp: function(param_hash){
GrayCode.width = Number( $("#gc-width option:selected").val() );
GrayCode.height = Number( $("#gc-height option:selected").val() );
GrayCode.xSplitNum = Math.log(GrayCode.width) / Math.log(2);
GrayCode.ySplitNum = Math.log(GrayCode.height) / Math.log(2);
if (!canvas) { Util.errLog('canvas yet set'); return; };
canvas.width = GrayCode.width;
canvas.height = GrayCode.height;
Sync.cam = Number($("#gc-cam").val());
Sync.set = Number($("#gc-set").val());
Sync.capture = param_hash['capture'];
Sync.shutter_speeds = $("#gc-shutter option:selected").val() == "multi" ? 1 : 0;
Util.onFullscreen();
Sync.drawAndCapture();
},
evalMode: function(){
if (this.mode == mode['blink']) {
this.mode = Sync.pattern_num < 2 ? mode['blink'] : mode['splitX'];
// reset
if(this.mode == mode['splitX']) Sync.pattern_num = 0;
} else if (this.mode == mode['splitX']) {
// XXX confirm -------------------------
//TestDecoder.pushGrayCodes();
//console.log(TestDecoder.graycodes);
//TestDecoder.evalGrayCodes();
// XXX confirm -------------------------
this.mode = Sync.pattern_num < GrayCode.xSplitNum ? mode['splitX'] : mode['splitY'];
// reset
if(this.mode == mode['splitY']) Sync.pattern_num = 0;
} else if (this.mode == mode['splitY']) {
this.mode = Sync.pattern_num < GrayCode.ySplitNum ? mode['splitY'] : mode['antiSplitX'];
if(this.mode == mode['antiSplitX']) Sync.pattern_num = 0;
} else if (this.mode == mode['antiSplitX']){
this.mode = Sync.pattern_num < GrayCode.xSplitNum ? mode['antiSplitX'] : mode['antiSplitY'];
if(this.mode == mode['antiSplitY']) Sync.pattern_num = 0;
} else if (this.mode == mode['antiSplitY']){
this.mode = Sync.pattern_num < GrayCode.ySplitNum ? mode['antiSplitY'] : mode['END'];
}
},
drawAndCapture: function(){
this.evalMode();
if (this.mode == mode['blink']){
var col = Sync.pattern_num % 2 ? 0 : 1;
GrayCode.fill(col);
} else if (this.mode == mode['splitX']){
GrayCode.splitX(0, canvas.width, canvas.height, 0, Sync.pattern_num, false);
} else if (this.mode == mode['splitY']){
GrayCode.splitY(0, canvas.width, canvas.height, 0, Sync.pattern_num, false);
} else if (this.mode == mode['antiSplitX']){
GrayCode.splitX(0, canvas.width, canvas.height, 0, Sync.pattern_num, true); // anti-pattern
} else if (this.mode == mode['antiSplitY']){
GrayCode.splitY(0, canvas.width, canvas.height, 0, Sync.pattern_num, true); // anti-pattern
} else {
console.log("GRAYCODE END");
Util.offFullscreen();
//setTimeout(function(){ location.href = "/"; }, 1000);
return;
}
$.getJSON("/cgi-bin/capture.py", {
'dir_name' : "gray_code/set" + Sync.set + "/",
'fname' : Sync.mode + "_" + Util.alignNum(Sync.pattern_num) + ".pgm",
'shutter_speeds' : Sync.shutter_speeds,
'capture': Sync.capture,
}, function(data) {
console.log(data)
Sync.pattern_num++;
Sync.drawAndCapture();
//next_values = $.parseJSON(data)
//console.log(next_values)
});
},
}
// Add events:
// GrayCode::demo
$("#gc-demo").on('click', function(){
Sync.setUp({
'capture': 0,
});
});
// GrayCode::capture
$("#gc-capture").on('click', function(){
Sync.setUp({
'capture': 1,
});
});
TestDecoder = {
graycodes: [],
init: function() { // undefinedでもjavascriptは勝手に0が詰まってうれしい
for(var i=0; i<100; i++) { this.graycodes[i] = 0; }
},
getImageData: function() {
var image = ctx.getImageData(0,0,100,100);
return image;
},
getPixel: function(image,x,y) {
var pixel = {
'r': image.data[(image.width*y+x)*4 + 0],
'g': image.data[(image.width*y+x)*4 + 1],
'b': image.data[(image.width*y+x)*4 + 2],
'a': image.data[(image.width*y+x)*4 + 3],
};
return pixel;
},
pushGrayCodes: function() {
var image = this.getImageData();
// CASE:splitX
for(var i=0; i<100; i++) {
var pixel = this.getPixel(image,i,0);
var code = pixel['r'] == 0 ? 0 : 1;
this.graycodes[i] = (this.graycodes[i] << 1) + code;
}
},
evalGrayCodes: function() {
for (var i=0; i<100; i++) {
var binary = this.toBinary(this.graycodes[i]);
var gc = this.toGrayCode(i);
var gc_disp = this.graycodes[i];
console.log([i, binary, gc, gc_disp]);
}
},
toGrayCode: function(binary) {
return binary ^ (binary >> 1);
},
toBinary: function(graycode) {
var tmp = 0;
tmp |= graycode;
while (graycode > 0) {
graycode >>= 1;
tmp ^= graycode;
}
return tmp;
},
}
// Preview::testcapture
Preview = {
persec: 1,
run: false,
capture: function(){
if (!Preview.run) return;
$.getJSON("/cgi-bin/capture.py", {
'persec': Preview.persec,
'capture': 1,
}, function(data){
// TODO camera param返せたら便利?
console.log(data);
var timestamp = new Date().getTime();
// $("#js-live-preview-img").attr("src", "data/preview.png" + '?' + timestamp);
// 丁寧に.load() でonloadイベント待ったら非同期で無限にタイミング増えて笑った
// draw
var canvasPv = document.getElementById("canvas-preview");
var ctxPv = canvasPv.getContext('2d');
var img = new Image();
img.src = "data/preview.png" + '?' + timestamp;
img.onload = function(){
ctxPv.drawImage(img, 0, 0, canvasPv.width, canvasPv.height);
Preview.capture();
};
});
},
};
$("#pv-capture").on('click', function(){
$(this).hide();
$("#pv-capture-stop").show();
$("#logo").hide();
$("#js-live-preview-img").show();
$("#canvas-preview").show();
//$("#js-live-preview-params").show();
Preview.run = true;
Preview.persec = Number($("#pv-persec option:selected").val());
Preview.capture();
});
$("#pv-capture-stop").on('click', function(){
$(this).hide();
$("#pv-capture").show();
Preview.run = false;
$("#canvas-preview").hide();
$("#js-live-preview-img").hide();
//$("#js-live-preview-params").hide();
$("#logo").show();
});
// ChessBoard::Rendering
var ChessBoard = {
row: 5,
col: 8,
unitPx: 20,
shotCount: 0,
shotNum: 10,
capture: 1,
init: function(param_hash){
// chessboard size
var selected = $("#cb-row-col").val();
if (selected == "5x8") {
ChessBoard.row = 5;
ChessBoard.col = 8;
} else if (selected == "7x10"){
ChessBoard.row = 7;
ChessBoard.col = 10;
} else {
ChessBoard.row = 11;
ChessBoard.col = 16;
};
// unit
ChessBoard.unitPx = Number($("#cb-unit-px").val());
// capture number
ChessBoard.shotNum = Number($("#cb-times").val());
canvas.width = ChessBoard.unitPx * (ChessBoard.col+3);
canvas.height = ChessBoard.unitPx * (ChessBoard.row+3);
// shotNum
ChessBoard.shotNum = Number($("#cb-times option:selected").val());
// capture flag
ChessBoard.capture = param_hash['capture'];
// preview
ChessBoard.preview_img = new Image();
// toggleFullscreen
Util.onFullscreen();
},
render: function(){
ctx.fillStyle = 'rgb(255,255,255)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
var unitPx = ChessBoard.unitPx;
ctx.fillStyle = 'rgb(0,0,0)';
for (var row=0; row<=ChessBoard.row; row++) {
for (var col=0; col<=ChessBoard.col; col++) {
if (row % 2 != 0 && col % 2 == 0) {
ctx.fillRect(unitPx + col*unitPx, unitPx + row*unitPx, unitPx, unitPx);
} else if (row % 2 == 0 && col % 2 != 0){
ctx.fillRect(unitPx + col*unitPx, unitPx + row*unitPx, unitPx, unitPx);
}
}
}
},
putText: function(text){
ctx.fillStyle = 'rgb(0, 0, 0)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'rgb(255,255,255)';
ctx.font = "bold 32px sans-serif";
ctx.fillText(text, 100, 100);
},
onMove: false,
preview_img: undefined,
preview: function(){
if (!ChessBoard.onMove) {
ChessBoard.incalib(); // fire next
return;
}
$.getJSON("/cgi-bin/capture.py", {
'persec': 0,
'capture': 1,
}, function(data){
console.log(data);
var timestamp = new Date().getTime();
ChessBoard.preview_img = new Image();
ChessBoard.preview_img.src = "data/preview.png" + '?' + timestamp;
ChessBoard.preview_img.onload = function(){
ctx.drawImage(ChessBoard.preview_img, canvas.width/2-160,canvas.height/2-120,320,240);
ChessBoard.preview();
};
});
},
incalib: function(){
// END
if (ChessBoard.shotCount == ChessBoard.shotNum) {
Util.offFullscreen();
$.getJSON('/cgi-bin/incalib.py', {
'chessboard_col' : ChessBoard.col,
'chessboard_row' : ChessBoard.row,
'unit_size' : ChessBoard.unitPx,
'num_of_images' : ChessBoard.shotNum,
}, function(data){
alert(data['status']);
});
return;
}
ChessBoard.render();
$.getJSON('/cgi-bin/capture.py', {
'dir_name' : 'chessboard/',
'fname' : Util.alignNum(ChessBoard.shotCount) + ".pgm",
'capture' : ChessBoard.capture,
}, function(data){
console.log(data)
ChessBoard.putText("Captured..." + ++ChessBoard.shotCount + "/" + ChessBoard.shotNum + " goNext");
// preview
ChessBoard.onMove = true;
// preview captured
// ChessBoard.preview();
// next
setTimeout(function(){ ChessBoard.onMove = false; }, 5000);
});
}
};
//ChessBoard::demo
$("#cb-demo").on('click', function(){
ChessBoard.init({ 'capture': 0 });
// ChessBoard.incalib({ 'capture': 0 });
ChessBoard.render();
Util.onFullscreen();
setTimeout("Util.offFullscreen()", 3000);
});
$("#cb-capture").on('click', function(){
ChessBoard.init({ 'capture': 1 });
ChessBoard.incalib();
});
});
@denkiwakame
Copy link
Author

バッドノウハウの塊感ある

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment