OpenCV - iOS事始め
OpenCVのビルドから、OpenCVを使ったプロジェクトの作成まで。
ほぼ公式のチュートリアルの内容のままです。
http://docs.opencv.org/trunk/doc/tutorials/ios/table_of_content_ios/table_of_content_ios.html
OpenCVのビルドしてopencv2.frameworkを作る
適当なディレクトリでGitHubからcloneしてビルドします。ホームディレクトリにsrcというディレクトリを作って、そこでビルドしました。
% cd ~/src % git clone https://github.com/Itseez/opencv.git % cd / % sudo ln -s /Applications/Xcode.app/Contents/Developer Developer % cd ~/src % python opencv/ios/build_framework.py ios
src/iosにopencv2.frameworkが作成されます。OpenCVを使ってアプリを作る際には、このフレームワークをリンクすることになります。
プロジェクトを作成する
簡単なサンプルということでSingle View Applicationを選択してプロジェクトを作成します。
プロジェクトを作成したらSupporting Files/{PROJECT_NAME}-Prefix.pchを以下の様に編集します。
#import <Availability.h> #ifndef __IPHONE_5_0 #warning "This project uses features only available in iOS SDK 5.0 and later." #endif // 追加 #ifdef _cplusplus #import <opencv2/opencv.hpp> #endif #ifdef __OBJC__ #import <UIKit/UIKit.h> #import <Foundation/Foundation.h> #endif
次に、プロジェクトナビゲータのプロジェクトをクリックし、TARGETS->Build PhasesのLink Binary With Librariesのセクションにopencv2.frameworkを追加します。追加する際はAdd Other...から選択して追加します。
とりあえず、アプリをビルドできるか試すために、アラートを出すだけのアプリを作ることにします。
ViewController.m
- (void)viewDidLoad { [super viewDidLoad]; UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Hello" message:@"Welcome to OpenCV" delegate:self cancelButtonTitle:@"Continue" otherButtonTitles:nil]; [alert show]; }
MacBook Air買った
Air二台目となるMacBook Air 13インチ 1.7GHz dual-core Intel Core i5が
整備済品で安かったので買いました。
SSD128GBで入れるものを絞ったので、そのメモ。
開発系
ユーティリティ系
- TotalFinder
- Quicksilver
- MenuMeters (メニューバーにシステムの使用状況を表示する)
Core Graphics/Core Animation キソのキソ #1
Core Graphics / Core Animation入門
iOS/OS Xでのグラフィック・アニメーション関連のフレームワーク
Quartz 2D
iOSでグラフィックを描画する際に裏で働いてくれる奴。UIKitはQuartzを使っている。
Core Graphics
グラフィックのコンテキスト、画像の読み込み・描画などを手伝ってくれる奴
Core Animation
iOSでのアニメーション周りを手伝ってくれる奴
iOSでグラフィックを扱う際の基本
iPhoneでも世代によって解像度が違うので、pixelとpointの関係を考える必要がある。
- iPhone 3GS : 320*480 (px)
- iPhone 4 : 640*960 (px)
iPhone 4はiPhone 3GSの2倍の解像度を持つので、iPhone 3GSで320*480pxの四角形を描いてスクリーンを埋めようとしても、iPhone 4だとスクリーンの1/4のサイズの四角形になってしまうという違いが起こるので注意。
デバイスの解像度の違いに簡単に対応するため、pixelではなくpointを指定して描画するAPIを使う。
プロジェクトを作成する
以降のサンプルはSingle View Applicationテンプレートを使用する。 プロジェクトを作成したら、新規にUIViewを継承するクラスを作成。InterfaceBuilderでビューを選択し、Identity InspectorのCutom Classセクションのクラスを、作成したクラスに向ける。
UIViewを継承したクラスの.mには予め以下のコードが含まれる:
#import "MCGraphicsViewControllerView.h" @implementation MCGraphicsViewControllerView - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code } return self; } /* // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. - (void)drawRect:(CGRect)rect { // Drawing code } */ @end
drawRect:メソッドがはビューが描画される時に自動で呼び出されるので、このメソッドにグラフィックを描画するプログラムを書いていく。
テキストを描画する
- (void)drawRect:(CGRect)rect { // フォントをロードする UIFont *helveticaBold = [UIFont fontWithName:@"HelveticaNeue-Bold" size:40.0f]; NSString *myText = @"yay"; // フォントを指定して(40, 180)にテキストを描画 [myText drawAtPoint:CGPointMake(40, 180) withFont:helveticaBold]; }
フォントを指定
フォントは「ファミリー > フェイス」の様に管理されているので、HelveticaのBoldが使いたければ「Helvetica(ファミリー) -> Helvetica Bold(フェイス)」の様に指定する。
システムに組み込まれているフォントは以下の様にして確認できる。
for (NSString *familyName in [UIFont familyNames]) { NSLog(@"Font Family = %@", familyName); for (NSString *fontName in [UIFont fontNamesForFamilyName:familyName]) { NSLog(@"+-- %@", fontName); } }
他のフォントを追加したい場合にはプロジェクトにドラッグドロップしてコピーする。
ターゲットへキーを追加する
フォント名を出力させると追加したフォントが表示されるので、後は同じ。
UIFont *AliceReglar = [UIFont fontWithName:@"Alice-Regular" size:40.0f]; NSString *myText = @"almost Alice."; [myText drawAtPoint:CGPointMake(40, 180) withFont:AliceReglar];
色を指定
色の指定はUIColorオブジェクトを利用して指定することができる。
// ViewController.m - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // ビューの背景色を白にする self.view.backgroundColor = [UIColor whiteColor]; }
[UIColor whiteColor]の様に色を指定したり
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. self.view.backgroundColor = [UIColor colorWithRed:1.9f green:1.0f blue:1.0f alpha:1.0f]; }
赤・緑・青・透明を0~1.0の範囲で指定して色を作ることもできる。
テキストに色を設定するにはコンテキストで色をセットする。
- (void)drawRect:(CGRect)rect { // 暗めの青 UIColor *myColor = [UIColor colorWithRed:0.1f green:0.3f blue:0.5f alpha:1.0f]; UIFont *AliceReglar = [UIFont fontWithName:@"Alice-Regular" size:40.0f]; // 色を指定する [myColor set]; NSString *myText = @"almost Alice."; [myText drawAtPoint:CGPointMake(40, 180) withFont:AliceReglar]; } @end
テキストを描画するにはdrawAtPoint:withFontでポイントを指定して描画する位置を決める他に、drawInRect:withFontで指定した矩形の範囲内にテキストを描画することもできる。
- (void)drawRect:(CGRect)rect { // 暗めの青 UIColor *myColor = [UIColor colorWithRed:0.1f green:0.3f blue:0.5f alpha:1.0f]; UIFont *AliceReglar = [UIFont fontWithName:@"Alice-Regular" size:40.0f]; // 色を指定する [myColor set]; NSString *myText = @"almost Alice."; [myText drawInRect:CGRectMake(100, // x 120, // y 100, // width 200) // height withFont:AliceReglar]; }
Jam :: Require.jsを使う時に便利そうなパッケージマネージャ
Jam
クライアントサイドJSのパッケージマネージャ
インストール
npmから
% sudo npm install -g jamjs ... % jam -v 0.1.11
パッケージをインストールしてみる
ダウンロードされるパッケージはカレントディレクトリに"jam"というディレクトリを作って配置されるので、プロジェクトのルートディレクトリを作る。
% mkdir jam-sandbox % cd jam-sandbox/
% jam install jquery installing from repositories jquery Building version tree... repositories checking "jquery" installing jquery@1.7.2 (cached) updating jam/jam.json updating jam/require.config.js updating jam/require.js OK % tree ./ ./ └── jam ├── jam.json ├── jquery │   ├── jquery.js │   └── package.json ├── require.config.js └── require.js 2 directories, 5 files
一度に複数のライブラリをインストールしたい場合には
% jam install jquery underscore
みたいにする。
パッケージの検索は
% jam search domready domReady 2.0.0-jam.1 An AMD loader plugin for detecting DOM ready OK: 1 total results (limit: 10) % jam install domReady
インストールしたパッケージを使う。
require.jsを同時に用意してくれるので、require()でロードする
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>::Jam Sandbox::</title> <script src="jam/require.js"></script> <script> require(['domReady', 'jquery'], function(domReady, $) { domReady(function() { $('#out').text('Hello, world'); }); }); </script> </head> <body> <div id="out"></div> </body> </html>
Jamで用意されたrequire.jsにはパッケージ情報が追加されているので、オリジナルのrequire.jsを使いたい場合には、別に"require.config.js"もロードする必要あり。
使わなくなったパッケージはドキュメントでは"jam remove {PACKAGE}" -> "jam clean"の手順で書いてあるが、"jam remove {PACKAGE}"のみで行けた。
% jam remove jquery removing jquery Building local version tree... updating jam/jam.json updating jam/require.config.js updating jam/require.js OK
// jam/jam.json { "jam_version": "0.1.11", "dependencies": { "domReady": null } }
コンパイルする
% jam compile vendor.js compiling vendor.js include domReady, jquery OK: vendor.js % ls index.html jam vendor.js
require.jsを含むパッケージを1つのファイルにしてくれる。
<script src="vendor.js"></script>
または、jam/require.jsをコンパイルすると、jam/require.jsに使うパッケージを含むようにコンパイルしてくれるので、HTMLを書き換える必要が無くなる。
% jam compile jam/require.js compiling jam/require.js include domReady, jquery OK: jam/require.js
ただ、この後、"jam install hoge"すると、jam/require.jsは元に戻ってしまうので、別にビルド用のスクリプトを作るかする必要あり。
インストールしたパッケージのうち、特定のものだけコンパイルしたい場合には"-i"オプションを付ける。
% jam install jquery underscore backbone couchr . . OK % jam compile -i jquery -i underscore -o vendor.js compiling vendor.js include jquery, underscore OK: vendor.js
サンプルアプリ
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>::Jam Sandbox::</title> <script src="jam/require.js"></script> <script> require(['js/app'], function(app) { app.initialize(); }); </script> </head> <body> <div id="out"></div> </body> </html>
js/app.js
define('js/app', [ 'jquery', 'js/utils' ], function( $, utils ) { var initialize = function() { utils.greeting(); console.log('app ready'); }; return { initialize: initialize }; });
js/utils.js
define('js/utils', [], function() { var greeting = function() { console.log('initialize utility module.'); }; return { greeting: greeting } });
パッケージをダウンロード
% jam install jquery installing from repositories jquery Building version tree... repositories checking "jquery" installing jquery@1.7.2 (cached) updating jam/jam.json updating jam/require.config.js updating jam/require.js OK
モジュールで依存するパッケージと定義しておくと、コンパイル時に解決してくれるので、
% jam compile -i js/app -o allinone.js
とすれば、js/app.jsが必要としているjqueryとjs/utilsをallinone.jsに含めてくれる
<script src="allinone.js"></script>
D3 メモ [SVG基礎編]
基本
SVGでグラフィックを描画する場合には、まずHTMLにsvg要素を追加して
描きたいシェイプを追加していく感じ。
function draw(json) { var w = 500, h = 500; var svg = d3.select('#container') .append('svg:svg') .attr('width', w) .attr('height', h); svg.append('svg:circle') .attr('cx', 50) .attr('cy', h/2) .attr('r', 30) .attr('fill', '#333'); svg.append('svg:rect') .attr('x', 100) .attr('y', h/2 - 15) .attr('width', 50) .attr('height', 30) .attr('rx', 5) // x方向の角丸 .attr('ry', 5); // y方向の角丸 svg.append('svg:ellipse') .attr('cx', 190) .attr('cy', h/2) .attr('rx', 30) // x方向の半径 .attr('ry', 20); // y方向の半径 svg.append('svg:line') .attr('x1', 10) .attr('y1', h/2 + 50) .attr('x2', 490) .attr('y2', h/2 + 50) .attr('stroke', '#333'); svg.append('svg:polyline') .attr('points', function() { var points = [], baseY = 50; for(var x = 10; x <= 300; x+=10) { var point = ''; if(x / 10 % 2) { point = x + ',' + baseY; } else { point = x + ',' + (baseY + 20); } points.push(point); } return points.join(' '); }) .attr('fill', 'none') .attr('stroke', '#333'); svg.append('svg:polygon') .attr('points', '10,100 10,130 50,150, 90,130 90,100') .attr('fill', 'none') .attr('stroke', '#333'); svg.append('svg:path') .attr('d', 'M10,160 L10,190 L50,190') .attr('fill', 'none') .attr('stroke', '#333'); }
データをバインドして表示してみる。
function init() { d3.json('data/videos.json', draw); } function initData(json) { return _.map(json.data.items, function(item) { return { title: item.title, fav: item.favoriteCount, view: item.viewCount, uploaded: item.uploaded }; }); } function draw(json) { var data = initData(json), canvW = 800, canvH = 495, ch = canvH/data.length, max, w, svg; max = d3.max(data, function(d) { return d.view; }); w = d3.scale.linear().domain([0, max]).range([10, ch]); svg = d3.select('#container') .append('svg:svg') .attr('width', canvW) .attr('height', canvH); // view数で半径を決める svg.selectAll('circle') .data(data) .enter() .append('svg:circle') .attr('cx', '50px') .attr('cy', function(d, idx) { return idx * ch + w(max) / 2; }) .attr('r', function(d) { return w(d.view) / 2; }) .attr('fill', '#333') .attr('stroke', '#3b93ff') .attr('stroke-width', function(d) { return w(d.view) / 4; }); // titleテキスト svg.selectAll('text') .data(data) .enter() .append('svg:text') .text(function(d) { return d.title; }) .attr('x', function(d) { return 50 + ch; }) .attr('y', function(d, idx) { return idx * ch + w(max) / 2 + 5; }) .attr('font-size', '12px'); }
D3 メモ[API]
API Reference
https://github.com/mbostock/d3/wiki/API-Reference
Event
<div id="container"> <button class="clickme">click me</button> <button class="clickme">click me</button> <button class="clickme">click me</button> <button class="clickme">click me</button> </div>
var data = [ { name: 'button1' }, { name: 'button2' }, { name: 'button3' }, { name: 'button4' } ]; d3.selectAll('.clickme') .data(data) .on('click', function(d, idx) { var posX = d3.event.pageX + 'px'; d3.select('#out') .style('left', posX) .text(function() { return d.name; }); }, false);
選択した要素にはonでイベントハンドラを登録できる。
イベントオブジェクトはグローバルなd3.eventを参照することで、対象の要素のイベントを取得できる。
イベントの発生時のマウス位置を、特定の要素内の相対位置で取得したい場合には
d3.mouse(document.getElementById('container'))のようにすると[x, y]の配列で取得できる。d3.touch()もある。
d3.selectAll('.clickme') .data(data) .on('click', function(d, idx) { var pos = d3.mouse(document.getElementById('container')); d3.select('#out') .text(function() { return d + ' at (' + pos[0] + ',' + pos[1] + ')'; }); }, false);
データ取得
- d3.xhr(url, callback)
- d3.text(url, callback)
- d3.json(url, callback)
- d3.xml(url, callback)
- d3.html(url, callback)
- d3.csv(url, callback)
サンプルなので、予め取得しておいたデータをjsonファイルとしてローカルに置いておく
http://gdata.youtube.com/feeds/api/videos?v=2&alt=jsonc&max-results=20&category=Music&format=5&orderby=rating&q=sonicyouth
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Demo</title> <!--[if lt IE 9]> <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <style> .item { margin: 10px 0; } h5 { margin: 0; } ul { list-style: none; margin: 0; padding: 0; } .info { font-size: 85%; } </style> <script src="js/libs/d3/d3.v2.js" type="text/javascript"></script> <script src="js/libs/underscore-min.js" type="text/javascript"></script> <script src="js/app.js" type="text/javascript"></script> </head> <body onload="init();"> <div id="container"> <ul></ul> </div> </body> </html>
function init() { d3.json('data/videos.json', draw); } function draw(json) { d3.select('#container ul') .selectAll('li') .data(json.data.items) .enter() .append('li') .attr('class', 'item') .html(function(d, idx) { return buildHTML(d); }); } function buildHTML(d) { var tmpl = _.template( '<h5><%- title %></h5>' + '<img src="<%- thumbnail %>" />' + '<div class="info">' + '<span class="like"><%- like %></span> | ' + '<span class="view"><%- view %></span>' + '</div>' ); return tmpl({ title: d.title, thumbnail: d.thumbnail.sqDefault, like: d.likeCount, view: d.viewCount, }); }
配列操作
d3で扱うデータは配列が基本なので、配列操作のメソッドが色々用意されている。
function init() { d3.json('data/videos.json', draw); } // 欲しいデータだけ取り出す function initData(json) { return _.map(json.data.items, function(item) { return { title: item.title, fav: item.favoriteCount, view: item.viewCount, thumbnail: item.thumbnail.sqDefault }; }); } function draw(json) { var data = initData(json), fMax, fMin, split; // fav数の最小・最大値 fMax = d3.max(data, function(d) { return d.fav; }); fMin = d3.min(data, function(d) { return d.fav; }); // min/maxは一度に取得できる [min, max] fExtent = d3.extent(data, function(d) { return d.fav; }); // fav数の昇順ソート favAsc = data.sort(function(a, b) { return d3.ascending(a.fav, b.fav); }); // view数が10,0000以上のアイテムだけ取り出して降順にソート split = d3.merge(d3.split(data, function(d) { return d.view < 100000; })) .sort(function(a, b) { return d3.descending(a.view, b.view); }); }
d3.scale
fav数に対応した大きさの図形を描きたい時など
実際のデータの値のまま使うと
function init() { d3.json('data/videos.json', draw); } function initData(json) { return _.map(json.data.items, function(item) { return { title: item.title, fav: item.favoriteCount }; }); } function draw(json) { var data = initData(json); d3.select('#container') .selectAll('div') .data(data) .enter() .append('div') .attr('class', 'bar') .style('background', '#0088ff') .style('font-size', '12px') .style('height', '16px') .style('width', function(d) { return d.fav + 'px'; }) .text(function(d) { return d.title; }); }
fav数によっては、windowサイズを大きくはみ出してしまうので
例えば、最大でもwindow幅以内にして全データをスケールしたい場合にはScales APIを使う。
d3.scale.linear()
デフォルトで変域・範囲が0~1のスケールを作るので、必要な変域と、範囲を指定する
max = d3.max(data, function(d) { return d.fav; }); // 「0~fav数の最大値」を「0~windowサイズ」に収める w = d3.scale.linear() .domain([0, max]) .range([0, document.body.clientWidth]); d3.select('#container') .selectAll('div') .data(data) .enter() .append('div') .attr('class', 'bar') .style('background-color', '#0088ff') .style('font-size', '12px') .style('height', '16px') .style('width', function(d) { return w(d.fav) + 'px'; }) .text(function(d) { return d.title + ':' + d.fav + '(' + w(d.fav) + 'px)'; });
グラフの軸を描くときにも使える。
D3 メモ [基礎編]
D3
データを元にHTMLを操作するためのライブラリ。データを元にしたヴィジュアライズもできるし、jQueryの様な使い方で、動的なページを作ることもできる。
基本的な使い方(DOM操作)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>d3 demo</title> <!--[if lt IE 9]> <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <script src="js/libs/d3.v2.js" type="text/javascript"></script> <script src="js/app.js" type="text/javascript"></script> </head> <body onload="draw();"> <div id="container"></div> </body> </html>
// app.js function draw() { d3.select('#container') .append('p') .text('Hello world'); }
jQueryみたいにセレクタで要素を選択(select)し、メソッドのチェイニング(append, text)もできる。(セレクタに使えるのは、デフォルトだとSelectors API LV.1に準拠した文字列)
selectは、要素が複数見つかった場合には先頭の要素しか返さないので
<div class="multiple"></div> <div class="multiple"></div> <div class="multiple"></div> <div class="multiple"></div>
の様な、複数の要素を選択したい場合にはselectAll()を使う。
d3.selectAll('.multiple') .append('p') .text('Hello world');
データとDOMのバインド
上の例の様なjQuery的な使い方だけではなく、d3ではデータとDOM要素をバインドすることができる。
// ダミーデータ var dummy = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; d3.select('#container') .selectAll('li') // 後で作る要素を選択しておく .data(dummy) // バインドするデータを指定する .enter() // データと関連付けした要素を生成する(プレースホルダになる) .append('li') // プレースホルダに要素を追加 .text(function(d, idx) { // 各<li>にバインドされたデータをテキストノードとして挿入 return 'index: ' + idx + ', data: ' + d; });
配列の要素数文のli要素が生成される。
データと要素が関連付けされているかどうかは、ブラウザのデベロッパーツール等で生成されたli要素をみると、"__data__"というキーに配列の要素が値としてバインドされている事が確認できる。
SVGを使う
データを使ってSVGでグラフィックを描く
var dummy = [4, 2, 6, 1, 8, 3, 5, 9, 5, 10]; // SVG要素を作る var svg = d3.select('#container') .append('svg') .attr('width', 500) .attr('height', 310); console.log(svg);
以下のようなHTMLができる
<div id="container"> <svg width="500" height="310"></svg> </div>
変数svgには生成したsvg要素(の配列)が入ってるので、selectやappendを使って更に要素を追加できる。
// データに応じた半径の円を描く function draw() { var dummy = [4, 2, 6, 1, 8, 3, 5, 9, 5, 10], svg, w = 500, h = 310; // SVG要素を作る svg = d3.select('#container') .append('svg') .attr('width', w) .attr('height', h); svg.selectAll('circle') .data(dummy) .enter() .append('circle') .attr('cx', function(d, idx) { return idx * 20 + 5 }) .attr('cy', h / 2) .attr('r', function(d) { return d; }) .attr('fill', function(d) { var color = '#0000ff'; if(d > 5) { color = '#ff0000'; } return color; }); svg.selectAll('text') .data(dummy) .enter() .append('text') .text(function(d) { return d; }) .attr('x', function(d, idx) { return idx * 20; }) .attr('y', function(d, idx) { return h / 3 * 2; }) .attr('font-size', '12px') .attr('fill', '#333'); }