全部盛りのサンプル
最後は、ほぼすべての機能を盛り込んだサンプルを紹介します。というわけでまずは、「crocro.sample」ができる機能を掲載しておきます。
- Webカメラからの画像の取得(生データ、SWF上の表示データ、2値化データ)
- Canvasに取得した画像の、画像ファイルへの書き出し
- カメラ開始時のコールバック
- 2値化
- ラベリング
- ARマーカー検知
- ドット・パターン検知
- 各種処理内容の描画
- ラベルとして認識するドット数の上限と下限の設定(ラベル・カットオフ設定)
- ARマーカーのパターンの設定
- ドット・パターンの設定
- 何パーセント以上の一致でマーカーを検出するかの設定(マーカー一致閾値の設定)
- ラベル・データの取得
- ラベルの2次元配置データの取得
- ARマーカー情報の取得
- ドットパターン情報の取得
- Webカメラのキャプチャ
- 検出時のコールバック
- ソースコードを見やすくするための配列のXY交換
- ARマーカーの作成(ユーザーがクリックで編集可能)
- ドット・パターンの作成(ユーザーがクリックで編集可能)
ざっとこういったことができます。以下のサンプルでは、この機能を切り替えながら実行できます。
それでは実際にソースコードを見てもらいます。
<!DOCTYPE html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>「crocro.jscam」 - サンプル 基本 - クロノス・クラウン</title>
<script
type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"
></script>
<script type="text/javascript" src="jscam.js"></script>
<script language="javascript">
// 変数
var cap;
// 起動時処理
$(document).ready(function() {
// JSカメラ初期化
crocro.jscam.initSwfCam();
cap = crocro.jscam.makeCaptureObject("cnvsCap");
// コールバックの設定
cap.setBackDataType(["pixelSwf", "pixelRaw", "pixelBin",
"labelData", "label2dArr", "dotsData", "markerData"]);
cap.setExtrFnc(function(resObj){ // ………【1】追加関数
var resArr = [];
if ("pixelSwf" in resObj) {
resArr.push("pixelSwf : " + resObj.pixelSwf.length);
}
if ("pixelRaw" in resObj) {
resArr.push("pixelRaw : " + resObj.pixelRaw.length);
}
if ("pixelBin" in resObj) {
resArr.push("pixelBin : " + resObj.pixelBin.length);
}
if ("label2dArr" in resObj) {
resArr.push();
resArr.push("<hr>label2dArr : " + resObj.label2dArr.length
+ " (16ドットずつスキップさせてラベルを表示)");
var miniMtrxArr = [];
for (var y = 0; y < cap.h; y += 16) {
var miniMtrx = "";
for (var x = 0; x < cap.w; x += 16) {
var p = y * cap.w + x;
var i = resObj.label2dArr[p];
miniMtrx += (i < 10 ? "0" : "") + i + " ";
}
miniMtrxArr.push(miniMtrx);
}
resArr.push(miniMtrxArr.join("<br>"));
}
if ("labelData" in resObj) {
var ld = resObj.labelData;
resArr.push("<hr>labelData.length : " + ld.length);
for (var i = 0; i < ld.length; i ++) {
var s = "";
$.each(ld[i], function(k, v) {s += k + ":" + v + ", "});
resArr.push("labelData[" + i + "] : " + s);
}
}
if ("dotsData" in resObj) {
var s = "";
var dd = resObj.dotsData;
$.each(dd, function(k, v) {s += k + ":" + v + ", "});
s += "<br>"
s += "lt(" + dd.ltX + ", " + dd.ltY + ") / "
+ "rt(" + dd.rtX + ", " + dd.rtY + ")<br>";
s += "lb(" + dd.lbX + ", " + dd.lbY + ") / "
+ "rb(" + dd.rbX + ", " + dd.rbY + ")";
resArr.push("<hr>dotsData : " + s);
}
if ("markerData" in resObj) {
var s = "";
var md = resObj.markerData;
$.each(md, function(k, v) {s += k + ":" + v + ", "});
s += "<br>"
s += "lt(" + md.ltX + ", " + md.ltY + ") / "
+ "rt(" + md.rtX + ", " + md.rtY + ")<br>";
s += "lb(" + md.lbX + ", " + md.lbY + ") / "
+ "rb(" + md.rbX + ", " + md.rbY + ")";
resArr.push("<hr>markerData : " + s);
}
$("#resData").html(resArr.join("<br>"));
})
// 検出
cap.detect(function() {
alert("最初の検出!");
});
// ドット・パターンの設定
viewDots();
// マーカーの設定
viewMarker();
});
// キャプチャ
function captureImg() {
cap.capture(); // キャプチャ
}
// 画像出力
function outputImg() {
cap.capture(function() {
var dId = "img" + new Date().getTime();
$("#output").append($("<img>").attr("id", dId));
cap.outputImage(dId, ""); // ………【2】画像出力
});
}
// 画像削除
function delImg() {
$("#output").empty();
}
// 種類切り替え
function swicthTyp() {
var val = $("input:radio[@name='swtchTyp']:checked").val();
var fltr = [];
if (val == "onlyDots") {
fltr.push(
"bin", "label"
,"detectDots", "drawDots", "drawClipDots"
,"hidePaint");
crocro.jscam.setFilter(fltr);
return;
}
if (val == "onlyMrkr") {
fltr.push(
"bin", "label"
,"detectMarker", "drawMarker", "drawClipMarker"
,"hidePaint");
crocro.jscam.setFilter(fltr);
return;
}
if (val.indexOf("bin") >= 0) fltr.push("bin");
if (val.indexOf("lbl") >= 0) fltr.push("label");
if (val.indexOf("prm") >= 0) fltr.push("drawLabel");
if (val.indexOf("dotPtrn") >= 0) {
fltr.push("detectDots", "drawDots", "drawClipDots");
}
if (val.indexOf("mrkr") >= 0) {
fltr.push("detectMarker", "drawMarker", "drawClipMarker");
}
if (val == "onlyPrm") fltr.push("hidePaint", "bin", "label", "drawLabel");
if (val != "onlyPrm") fltr.push("showPaint");
if (val.indexOf("hidePaint") >= 0) fltr.push("hidePaint");
crocro.jscam.setFilter(fltr);
}
// ラベル・カットオフ設定
function chngLblRng() {
crocro.jscam.setsetLabelCutOff($("#lblMin").val(), $("#lblMax").val());
// ………【3】ラベル・カットオフ設定
}
// ドット・パターン設定
var dotsArr = crocro.jscam.util.swapXY(4, 4, [
[1,1,0,1],
[1,0,0,1],
[0,0,0,1],
[1,1,1,1]
]);
// ドットパターン表示
function viewDots() {
// ドットパターンの作成
crocro.jscam.util.makeDots("dots", dotsArr, 50, true,
function(x, y, prm){
dotsArr[x][y] = prm;
crocro.jscam.setDots(dotsArr);
}
);
}
// マーカー設定
var mrkrArr = crocro.jscam.util.swapXY(16, 16, [
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1],
[1,1,1,0,1,1,1,0,0,0,0,0,0,1,1,1],
[1,1,1,0,1,1,1,0,0,0,0,0,0,1,1,1],
[1,1,1,0,1,1,1,0,0,0,0,0,0,1,1,1],
[1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1],
[1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1],
[1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1],
[1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1],
[1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1],
[1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
]);
// マーカー表示
function viewMarker() {
// マーカーの作成
crocro.jscam.util.makeMarker("marker", mrkrArr, 13, true,
function(x, y, prm){
mrkrArr[x][y] = prm;
crocro.jscam.setMarker(mrkrArr);
}
);
}
// マーカー一致閾値の変更
function chngMtchThrshld() {
crocro.jscam.setMarkerMatchThreshold($("#setMtchThrshld").val());
// ………【4】マーカー一致閾値の変更
}
</script>
</head>
<body bgcolor="#ffffff">
<div id="jscamAS" class="boxOut">
<table border=0><tr><td valign="top">
<script language="javascript">
crocro.jscam.write();
</script>
</td><td valign="top" style="padding-left: 32px;">
●ドットパターン形式<br>
<br>
<div id="dots"></div>
</td><td valign="top" style="padding-left: 32px;">
●マーカー形式<br>
<br>
<div id="marker"></div>
</td></tr></table>
</div>
<div id="cntrl" style="line-height: 170%;" class="boxOut">
<br>
<input type="radio" value="non" name="swtchTyp" id="swtchTypNon" checked
onClick="swicthTyp()"><label for="swtchTypNon">なし</label>
<input type="radio" value="bin" name="swtchTyp" id="swtchTypBin"
onClick="swicthTyp()"><label for="swtchTypBin">2値化</label>
<input type="radio" value="bin+lbl" name="swtchTyp" id="swtchTypBinLbl"
onClick="swicthTyp()"><label for="swtchTypBinLbl">2値化+ラベリング</label>
<input type="radio" value="bin+lbl+prm" name="swtchTyp" id="swtchTypBinLblPrm"
onClick="swicthTyp()"><label for="swtchTypBinLblPrm">2値化+ラベリング+情報</label>
<input type="radio" value="onlyPrm" name="swtchTyp" id="swtchTypOnlyPrm"
onClick="swicthTyp()"><label for="swtchTypOnlyPrm">情報のみ</label>
<br>
<span style="font-weight: bold;">ラベル認識範囲</span> :
<input type="text" value="10" style="width: 3em;" id="lblMin">
~
<input type="text" value="57600" style="width: 3em;" id="lblMax">
ドット
<input type="button" onClick="chngLblRng()" value="範囲変更">
(57600は縦×横×3/4、縦×横は76800)
<br>
<span style="font-weight: bold;">ドットパターン検出</span> :
<input type="radio" value="bin+lbl+dotPtrn+prm" name="swtchTyp" id="swtchTypBinLblDotPtrnPrm"
onClick="swicthTyp()"><label for="swtchTypBinLblDotPtrnPrm">デバッグ表示</label>
<input type="radio" value="onlyDots" name="swtchTyp" id="swtchTypDotPtrnPrm"
onClick="swicthTyp()"><label for="swtchTypDotPtrnPrm">ドットパターンのみ</label>
<br>
<span style="font-weight: bold;">マーカー検出</span> :
<input type="radio" value="bin+lbl+mrkr+prm" name="swtchTyp" id="swtchTypBinLblMrkrPrm"
onClick="swicthTyp()"><label for="swtchTypBinLblMrkrPrm">デバッグ表示</label>
<input type="radio" value="onlyMrkr" name="swtchTyp" id="swtchTypMrkrPrm"
onClick="swicthTyp()"><label for="swtchTypMrkrPrm">マーカーのみ</label>
<input type="text" value="80" style="width: 3em;" id="setMtchThrshld">
%以上一致で検出
<input type="button" onClick="chngMtchThrshld()" value="閾値変更">
<hr>
<input type="button" onClick="captureImg()" value="1回ずつ描画">
<input type="button" onClick="outputImg()" value="画像出力">
<input type="button" onClick="delImg()" value="画像削除">
<hr>
<div id="jscamJS" style="">
<table border=0><tr><td valign="top">
<canvas id="cnvsCap" width="320" height="240"></canvas>
</td><td valign="top">
<div id="resData" style="height: 240px; overflow-y: scroll;"></div>
</td></tr></table>
</div>
<div id="output"></div>
<div id="dbg"></div>
</body>
</html>
これまで出てきていない部分を中心に、簡単に解説しておきます。
【1】の setExtrFnc メソッドに関数を登録しておくと、キャプチャ時の引数のコールバック関数とは別に、キャプチャごとに自動でコールバック処理が実行されます。常時行う処理はこのメソッドを利用するとよいです。
【2】の outputImage メソッドでは、 img タグのIDを指定することで、その場所に画像を出力させることができます。第2引数は画像のタイプです。空文字の場合はPNGになります。
【3】の「ラベル・カットオフ設定」は、「小さいドットを除外したい」「大きな領域は除外したい」といった場合用のものです。デフォルトの状態で、小さすぎる領域や大きすぎる領域は、自動で無視するようになっています。特殊な環境で使う場合には、この値を自分で設定するとよいでしょう。
【4】の「マーカー一致閾値」は、何パーセント以上の一致でマーカーと見なすかというものです。通常は80%です。検出をゆるくしたり、厳しくしたりしたい場合は、この値を変えるとよいです。また、 setBackDataType メソッドで markerData を指定しておけば、実際にどのぐらいの一致率だったのかを数値で確認することができます。

