oFのcolor trackingをAndroid上で動かす

Xcodeのスクリプトをほぼ無改造のままAndroidStudioでいけました

#pragma once

#include "ofMain.h"
#include "ofxAndroid.h"

#include "ofxOpenCv.h"

class ofApp : public ofxAndroidApp{
	
	public:
		
		void setup();
		void update();
		void draw();

		void keyPressed(int key);
		void keyReleased(int key);
		void windowResized(int w, int h);

		void touchDown(int x, int y, int id);
		void touchMoved(int x, int y, int id);
		void touchUp(int x, int y, int id);
		void touchDoubleTap(int x, int y, int id);
		void touchCancelled(int x, int y, int id);
		void swipe(ofxAndroidSwipeDir swipeDir, int id);

		void pause();
		void stop();
		void resume();
		void reloadTextures();

		bool backPressed();
		void okPressed();
		void cancelPressed();


	ofVideoGrabber movie;
	ofxCvColorImage rgb,hsb;
	ofxCvGrayscaleImage hue,sat,bri,filtered;
	ofxCvContourFinder contours;

	int w,h;
	int findHue;


};

メインはこちら

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){

	//add by me
	ofBackground(0,0,0);
	ofSetOrientation(OF_ORIENTATION_90_LEFT);
	w = 640;//default 320
	h = 480;//default 240

	movie.initGrabber(w, h, true);

	//reserve memory for cv images
	rgb.allocate(w, h);
	hsb.allocate(w, h);
	hue.allocate(w, h);
	sat.allocate(w, h);
	bri.allocate(w, h);
	filtered.allocate(w, h);
}

//--------------------------------------------------------------
void ofApp::update(){

	// add by me
	movie.update();

	if (movie.isFrameNew()) {

		//copy webcam pixels to rgb image
		rgb.setFromPixels(movie.getPixels(), w, h);

		//mirror horizontal
		rgb.mirror(false, false);//default (false, true)

		//duplicate rgb
		hsb = rgb;

		//convert to hsb
		hsb.convertRgbToHsv();

		//store the three channels as grayscale images
		hsb.convertToGrayscalePlanarImages(hue, sat, bri);

		//filter image based on the hue value were looking for
		for (int i=0; i<w*h; i++) {
			filtered.getPixels()[i] = ofInRange(hue.getPixels()[i],findHue-5,findHue+5) ? 255 : 0;
		}

		filtered.flagImageChanged();
		//run the contour finder on the filtered image to find blobs with a certain hue
		contours.findContours(filtered, 20, w*h/2, 2, false);//last numeric 2 is find maximum cout
	}

}

//--------------------------------------------------------------
void ofApp::draw(){
	//add by me
	ofSetColor(255,255,255);

	//draw all cv images
	rgb.draw(0,0);
	//hsb.draw(640,0);
	//hue.draw(0,240);
	//sat.draw(320,240);
	//bri.draw(640,240);
	//filtered.draw(0,480);
	//contours.draw(0,480);

	ofSetColor(255, 0, 0);
	ofFill();

	//draw red circles for found blobs
	for (int i=0; i<contours.nBlobs; i++) {
		ofCircle(contours.blobs[i].centroid.x, contours.blobs[i].centroid.y, 10);// last text 10 is radius od draw circle
	}

}

//--------------------------------------------------------------
void ofApp::keyPressed  (int key){ 
	
}

//--------------------------------------------------------------
void ofApp::keyReleased(int key){ 
	
}

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){

}

//--------------------------------------------------------------
void ofApp::touchDown(int x, int y, int id){

// add by me
//calculate local mouse x,y in image

int mx = x % w;
int my = y % h;

//get hue value on mouse position
findHue = hue.getPixels()[my*w+mx];

}

//--------------------------------------------------------------
void ofApp::touchMoved(int x, int y, int id){

}

//--------------------------------------------------------------
void ofApp::touchUp(int x, int y, int id){

}

//--------------------------------------------------------------
void ofApp::touchDoubleTap(int x, int y, int id){

}

//--------------------------------------------------------------
void ofApp::touchCancelled(int x, int y, int id){

}

//--------------------------------------------------------------
void ofApp::swipe(ofxAndroidSwipeDir swipeDir, int id){

}

//--------------------------------------------------------------
void ofApp::pause(){

}

//--------------------------------------------------------------
void ofApp::stop(){

}

//--------------------------------------------------------------
void ofApp::resume(){

}

//--------------------------------------------------------------
void ofApp::reloadTextures(){

}

//--------------------------------------------------------------
bool ofApp::backPressed(){
	return false;
}

//--------------------------------------------------------------
void ofApp::okPressed(){

}

//--------------------------------------------------------------
void ofApp::cancelPressed(){

}

8行目で横向きモードにしています

35行目でカメラのミラー設定を無効にしています.実験がインカメラでしたが,バックカメラの場合は不要ですね.

実行結果は以下

ofcvtest3

 

 

 

 

 

oFをAndroidStudioでビルドのエラー

いくつかのエラーが起きた.

まずはNDKの場所について

(後述)なんかNDKの場所をoFのAndroidのプロジェクトのなんかのファイルを,絶対パスで指定した

 

次にビルドは通っても,仮装デバイスへインストールができないエラー.

エラーメッセージは

android Installation error: INSTALL_FAILED_CONTAINER_ERROR

とでる.

この解決方法としては,

AndroidManifest.XMLの中の

android:installLocation=”preferExternal”

android:installLocation=”auto”

に変更することでクリアできました.

この設定はプロジェクト毎に設定する必要があります.

oFでopenCV

open frame worksでopenCVをやっちゃいます.

open frame works(以下oF)はプログラミング自体がとても簡略化されているのでプロトタイプの作成向きです.動作は重いとのことですが,リリースするものでなければそれほど難しくありません.

なおかつiPhone,Androidようにテンプレートがあり,そこに書き込めばスマホアプリが作れてしまいます.(AndroidやopenCVは未確認)

これはXCODEでのテストです

#pragma once

#include "ofMain.h"
#include "ofxOpenCv.h"

class ofApp : public ofBaseApp{

	public:
		void setup();
		void update();
		void draw();

		void keyPressed(int key);
		void keyReleased(int key);
		void mouseMoved(int x, int y );
		void mouseDragged(int x, int y, int button);
		void mousePressed(int x, int y, int button);
		void mouseReleased(int x, int y, int button);
		void mouseEntered(int x, int y);
		void mouseExited(int x, int y);
		void windowResized(int w, int h);
		void dragEvent(ofDragInfo dragInfo);
		void gotMessage(ofMessage msg);
    
    ofVideoGrabber movie;
    
    ofxCvColorImage rgb,hsb;
    ofxCvGrayscaleImage hue,sat,bri,filtered;
    ofxCvContourFinder contours;
    
    int w,h;
    int findHue;
		
};

そしてこっちも必要

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
    ofBackground(0,0,0);
    
    w = 320;
    h = 240;
    
    movie.initGrabber(w, h, true);
    
    //reserve memory for cv images
    rgb.allocate(w, h);
    hsb.allocate(w, h);
    hue.allocate(w, h);
    sat.allocate(w, h);
    bri.allocate(w, h);
    filtered.allocate(w, h);
}

//--------------------------------------------------------------
void ofApp::update(){
    movie.update();
    
    if (movie.isFrameNew()) {
        
        //copy webcam pixels to rgb image
        rgb.setFromPixels(movie.getPixels(), w, h);
        
        //mirror horizontal
        rgb.mirror(false, true);
        
        //duplicate rgb
        hsb = rgb;
        
        //convert to hsb
        hsb.convertRgbToHsv();
        
        //store the three channels as grayscale images
        hsb.convertToGrayscalePlanarImages(hue, sat, bri);
        
        //filter image based on the hue value were looking for
        for (int i=0; i<w*h; i++) {
            filtered.getPixels()[i] = ofInRange(hue.getPixels()[i],findHue-5,findHue+5) ? 255 : 0;
        }
        
        filtered.flagImageChanged();
        //run the contour finder on the filtered image to find blobs with a certain hue
        contours.findContours(filtered, 20, w*h/2, 2, false);//last numeric 2 is find maximum cout
    }
}

//--------------------------------------------------------------
void ofApp::draw(){
    ofSetColor(255,255,255);
    
    //draw all cv images
    rgb.draw(0,0);
    hsb.draw(640,0);
    hue.draw(0,240);
    sat.draw(320,240);
    bri.draw(640,240);
    filtered.draw(0,480);
    contours.draw(0,480);
    
    ofSetColor(255, 0, 0);
    ofFill();
    
    //draw red circles for found blobs
    for (int i=0; i<contours.nBlobs; i++) {
        ofCircle(contours.blobs[i].centroid.x, contours.blobs[i].centroid.y, 10);// last text 10 is radius od draw circle
    }

}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){

}

//--------------------------------------------------------------
void ofApp::keyReleased(int key){

}

//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){

}

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){
    //calculate local mouse x,y in image
    int mx = x % w;
    int my = y % h;
    
    //get hue value on mouse position
    findHue = hue.getPixels()[my*w+mx];
}

//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y){

}

//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y){

}

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){

}

//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){

}

//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){ 

}

 

混乱を防ぐために,テンプレデフォルトのまま書き足しています

実行結果はこんな様子

cvtest

実行して,抽出したい色をクリックするとそこに赤丸が表示されます

抽出数はofApp.cppの49行目

contours.findContours(filtered, 20, w*h/2, 2, false);

のfalseの前の2で変えられます.2なら2個検出.

占いのようなスクリプト2

ちょっと加工して,CSSをつけるとこうなります.

<!doctype html>
 <html>
  <head>
   <meta charset="utf-8">
   <title>uranai2</title>
      <link rel="stylesheet" type="text/css" href="uranai.css" />
  </head>
  <body>
  
 利き腕動物占い
 <div class="myselect">
<select name="hand_select" onChange="result(this.value)">
<option value="none" selected>利き腕を選んで</option>
<option value="right">右きき</option>
<option value="left">左きき</option>
</select>
</div>

<div class="spacer"><!—-></div>

<div id="type_" class="block"></div>
  
    <script type="text/javascript">
  
 var txt = 	{ //javascriptの配列を作りtxtに入れる
 	"none":{
		0:"利き腕を選んで",
		1:"利き腕を選んで",
		2:"利き腕を選んで"
	},
	"right":{
		0:"あなたは犬タイプです",
		1:"あなたは猫タイプです",
		2:"あなたは牛タイプです"
	},
	"left":{
		0:"あなたはタヌキタイプです",
		1:"あなたはキジタイプです",
		2:"あなたはキリンタイプです"
	},
 }
 
 
 function result(myvalue){//上のselectでonChangeで関数を呼びthis.valueに入ったvalue値がmyvalueに入る

	 var randnum = Math.floor( Math.random() * 3); //0〜2の乱数を発生させよ
 var mytxt = "" + txt[myvalue][randnum];//txt配列のmyvalueの乱数値randomで配列の中身を取り出し,ダブルクォーテーションをつけて,mytxtに入れる
document.getElementById("type_").innerHTML = mytxt;
	//html内のtype_というIDのついたタグを探し,mytxtの中身を入れなさい
}
 
 
  </script>
 </body>
</html>

CSSはこちら

@charset "UTF-8";

body    {  
    background-color: #f0f8ff;  
} 



.myselect select {
	position: relative;
	width: 90%;
	padding: 10px;
	-webkit-appearance: none;
	-moz-appearance: none;
	appearance: none;
	border: 1px solid #999;
	background: #eee;
	background: -webkit-linear-gradient(top, #fff 0%,#efebe1 100%);
	background: linear-gradient(to bottom, #fff 0%,#efebe1 100%);
	    font-size: 1.4em;

    /* 文字の太さをboldに指定 */
    font-weight: bold;
}

.block {
	    font-size: 1.0em;
    color: #fff;
    background: #3cb371;
	width: 90%;
    padding: 10px;
    border: 2px dashed rgba(255,255,255,0.5);
    border-radius: 6px;
    -moz-border-radius: 6px;
    -webkit-border-radius: 6px;
    box-shadow: 0 0 0 5px #3cb371, 0 2px 3px 5px rgba(0,0,0,0.5);
    -moz-box-shadow: 0 0 0 5px #3cb371, 0 2px 3px 5px rgba(0,0,0,0.5);
    -webkit-box-shadow: 0 0 0 5px #3cb371, 0 2px 3px 5px rgba(0,0,0,0.5);
}
.spacer {
    clear: both;
    width: auto;
    height: 10px;
    }

/* CSS Document */

実際の動作はこちら

http://filmm.info/liub/uranai2.html

占いのようなスクリプト

利き腕を選ぶと,占いの結果が表示されるものです.

<!doctype html>
 <html>
  <head>
   <meta charset="utf-8">
   <title>array test3</title>
  </head>
  <body>
  
 利き腕動物占い
<select name="hand_select" onChange="result(this.value)">
<option value="area0" selected>利き腕を選んで</option>
<option value="right">右きき</option>
<option value="left">左きき</option>
</select>


<div id="type_"></div>
  
    <script type="text/javascript">
  
 var txt = 	{ //javascriptの配列を作りtxtに入れる
	"right":{
		0:"犬",
		1:"猫",
		2:"牛"
	},
	"left":{
		0:"タヌキ",
		1:"キジ",
		2:"キリン"
	},
 }
 
 
 function result(myvalue){//上のselectでonChangeで関数を呼びthis.valueに入ったvalue値がmyvalueに入る

	 var randnum = Math.floor( Math.random() * 3); //0〜2の乱数を発生させよ
 var mytxt = "" + txt[myvalue][randnum];//txt配列のmyvalueの乱数値randomで配列の中身を取り出し,ダブルクォーテーションをつけて,mytxtに入れる
document.getElementById("type_").innerHTML = mytxt;
	//html内のtype_というIDのついたタグを探し,mytxtの中身を入れなさい
}
 
 
  </script>
 </body>
</html>

実働例はこれです

http://filmm.info/liub/uranai.html

ボタンを押すとJavascriptを実行してHTMLを入れる

ボタンをクリックすると,jsを実行して,htmlに文字を入れるサンプルです

<!doctype html>
 <html>
  <head>
   <meta charset="utf-8">
   <title>test4</title>
   <link rel="stylesheet" type="text/css" href="test4.css" />
  </head>
  <body>
  
<button type=button onclick="showtest1()">
1つめのボタン
</button> 

<div id="1st"></div>


<button type=button onclick="showtest2()">
2つめのボタン
</button> 
<div id="2nd"></div>
  
<script type="text/javascript">
 
 
 function showtest1(){
	var mytxt = "test1";
	document.getElementById("1st").innerHTML = mytxt;
	 
 }


function showtest2(){
	var mytxt2 = '<a href="http://nantoka.filmm.info/blog/">ひみつのページへ</a>';
	document.getElementById("2nd").innerHTML = mytxt2;
	
}
 
 
  </script>
 </body>
</html>

このボタンはcssでデザインしています

@charset "UTF-8";

body    {  
    background-color: #fff799;  
} 


button{
    /* 文字サイズを1.4emに指定 */
    font-size: 1.4em;

    /* 文字の太さをboldに指定 */
    font-weight: bold;

    /* 縦方向に10px、
     * 横方向に30pxの余白を指定 */
    padding: 10px 30px;

    /* 文字色を白色に指定 */
    color: #fff;

    /* ボーダーをなくす */
    border-style: none;

    /* ボタンの影の指定
     * 影の横幅を2px
     * 縦長を2px
     * ぼかしを3px
     * 広がりを1px
     * 色を#666(グレー)に指定 */
    box-shadow: 2px 2px 3px 1px #666;
    -moz-box-shadow: 2px 2px 3px 1px #666;
    -webkit-box-shadow: 2px 2px 3px 1px #666;

    /* テキストの影の指定
     * 影の横幅を1px
     * 縦長を1px
     * ぼかしを2px
     * 色を#000(黒)に指定 */
    text-shadow: 1px 1px 2px #000;

    /* グラデーションの指定 */
    background: -moz-linear-gradient(bottom, #36d, #248 50%, #36d);
    background: -webkit-gradient(linear, left bottom, left top, from(#36d), color-stop(0.5, #248), to(#36d));
}

button:hover {
    /* 透明度を20%に指定 */
    opacity: 0.8;
}
div {
    background-color:#ddeeff;
    margin:20px;
    padding:5px;
    border-radius:5px;
    -webkit-border-radius:5px;
    -moz-border-radius:5px;
    box-shadow:0px 0px 0px 6px #aedaf2;
    -moz-box-shadow:0px 0px 0px 6px #aedaf2;
    -webkit-box-shadow:0px 0px 0px 6px #aedaf2;
}

/* CSS Document */

 

実際の動作の様子

http://filmm.info/liub/test4.html

角度で色が変わる(加速度)

スマホの角度で色が変わります

<!DOCTYPE html>
<html>
<head>
<title>touch color</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">
<!-- metaタグで スマホの挙動を制限している -->
<meta http-equiv="Content-Style-Type" content="text/css">
<!--CSSで余白をゼロにしている -->
<style type="text/css">
body {
margin: 0px;
}
</style>
</head>
<body>

<canvas id="myCanvas"></canvas>

<!-- ↓ここからJavascript -->
<script type="text/javascript">

//変数系を初期設定
var canvas;
var context;
var myHue;


window.onload = function() {//これ読み込み終わったら
syokika();//syokikaを実行してね
}

function syokika(){//これがsyokikaね
canvas = document.getElementById("myCanvas");//どのcanvasにするか指定
canvas.width = window.innerWidth;//幅は全幅ですよ
canvas.height = window.innerHeight;//高さもフルで
context = canvas.getContext("2d");//2次元仕様で

context.fillStyle = "rgb(0, 0, 0)";//塗る準備よ!色はRGBモデルで量はそれぞれ0,0,0ね
context.fillRect(0, 0, canvas.width, canvas.height);//はい,0,0(左上)から,全幅,全高で塗って!
}

var grX = 0;
var grY = 0;
window.addEventListener("devicemotion", function(evt){//これの反応をみててください,その反応がdevidemotionという種類であればその値をevtに入れなさい
grX = evt.accelerationIncludingGravity.x;  // evtのx軸の傾きを出してgrXに入れなさい
grY = evt.accelerationIncludingGravity.y;  // evtのY軸の傾きを出してgrXに入れなさい
//重力なので出力される値は-9.8~9.8(G)です

});


setInterval(mydraw, 100);//10msec毎に描いてね

function mydraw(){
    var myHue = (((grY + 9.8)/19.6)*180)*2;//横位値をスクリーン幅で割った値に360を掛けて色相つくりmyHueに入れる
    var myBri = ((grX + 9.8)/19.6)*100;//縦位置をスクリーン高で割った値に100を掛けてmyBriに明るさを入れる
    
    ///////---ここに描画する要素を書く
   // context.fillStyle = "hsl(" + myHue + ", 100%, 50% )"; //HSL値(色相、彩度、輝度)を指定
   context.fillStyle = "hsl(" + myHue + ", 100%, " + myBri + "% )"; //HSL値(色相、彩度、輝度)を指定
	context.beginPath();
  	context.arc(canvas.width/2, canvas.height/2, 150, 0, Math.PI*2, true);
	context.fill();
	}


</script><!-- ↑ここまでJavascript -->
</body>
</html>

サンプル−>http://filmm.info/touch/touchcolor6.html

データ検索2(セレクトを使った例)

プルダウンメニューでエリアのみセレクトします.

<!doctype html>
 <html>
  <head>
   <meta charset="utf-8">
   <title>JSON Test</title>
  </head>
  <body>
  
  
<select name="arae_select" onChange="showtxt(this.value)">
<option value="area0" selected>エリアを選んでください</option>
<option value="area1">エリア1</option>
<option value="area2">エリア2</option>
</select>


<div id="zip_"></div>
  
    <script type="text/javascript">
  
 var txt = 	{ //javascriptの配列をここで作りtxtに入れる
	"area1":{
		"地名1":"470-0000",
		"地名2":"470-0001",
		"地名3":"470-0002"
	},
	"area2":{
		"地名4":"470-0003",
		"地名5":"470-0004",
		"地名6":"470-0005"
	},
 }
 
 function showtxt(myvalue){//onChangeで選ばれた内容をmyvalueで受信する
	 console.log(myvalue);
 var mytxt = "" + txt[myvalue]['地名3'];//txt配列のarea1の中の地名3の中身を取り出し,ダブルクォーテーションをつけて,mytxtに入れる
// console.log(mytxt); //ここはデバッグ用の行
 
 document.getElementById("zip_").innerHTML = mytxt;//このhtml内のzip_ というidのついたタグをさがし,そこにmytxtを流し込む
 } 
  </script>
 </body>
</html>

サンプル−>http://filmm.info/liub/jsArray2.html

自分で持ってるデータを検索する

短いデータなどはHTML内に書いて置いて,検索をさせるようなことができます

まずは基本のスクリプトです.

<!doctype html>
 <html>
  <head>
   <meta charset="utf-8">
   <title>JSON Test</title>
  </head>
  <body>
  
  <div id="zip_"></div>
    <script type="text/javascript">
  
 var txt = 	{ //javascriptの配列をここで作りtxtに入れる
	"area1":{
		"地名1":"470-0000",
		"地名2":"470-0001",
		"地名3":"470-0002"
	},
	"area2":{
		"地名4":"470-0003",
		"地名5":"470-0004",
		"地名6":"470-0005"
	},
 }
 
 var mytxt = "" + txt['area1']['地名3'];//txt配列のarea1の中の地名3の中身を取り出し,ダブルクォーテーションをつけて,mytxtに入れる
// console.log(mytxt); //ここはデバッグ用の行
 
 document.getElementById("zip_").innerHTML = mytxt;//このhtml内のzip_ というidのついたタグをさがし,そこにmytxtを流し込む

  </script>
 </body>
</html>

これは郵便番号表としての使用を想定していますが,時刻表などにも使用できますね.

25行目で配列の中身を呼び出していますが,このサンプルは固定値です.

そこで,これを動的に呼び出す方法を検討しましょう.