2

AspireOneにUbuntuをインストールして使っている

春頃にTeleshadowの実装のために、修理品のAspireOneをNTTストアで2万ちょっとで買って使ってたんだけど、修論が終わってまあTeleshadow動かす為だけに置いておくのももったいないのでUbuntu9.04を入れてみた。
これけっこうよかった。最近は開発がLinux中心なので、持ち歩く事も多い。

せっかくなのでセットアップをまとめておく


■Windows
Windowsはもうあまり使ってない。
AspireOneの起動時に winlogon.exe がエラーを出してたんだけどこれは「アプリケーションの追加と削除」からLaunch Managerを削除したら出なくなった。
そもそもメモリが1GBだけなので、Windows動かすのがけっこうつらい。


■インストール
参考:AspireOneにUbuntu9.04インストール – 橋本詳解
CD-ROMドライブが無いのでUNetbootを使ってインストールした。
デスクトップ版Ubuntu9.04のisoイメージをダウンロードしてきて、UNetbootのWindows版で空のUSBメモリ(1GB)をUbuntuインストールディスク化した。
AspireOneに刺して、BIOS起動時にF12押してUSBデバイスからbootするようにすると、GUIのインストーラが出てくる。
パーティションをUbuntu 60GB、Windows 40GBに切り直してインストールした。

内臓カメラ、キーボード、無線もすぐ使えた。


インストール中
Ubuntuインストール中


■e-mobile
最近イーモバイルのD23HWを買った。
GUIとCUIそれぞれから使える。電波強度が表示できないけど実用上問題は無い。
pon, poffコマンドの場合:emobile D23HWを使う(CUIで) – 橋本詳解

gnome-pppの場合:emobile D23HWを使う(CUIで) – 橋本詳解



■デスクトップの設定
ワークスペースを2×2に増やして、ウィンドウをキーボードショートカットでワークスペース間移動できるようにする。

キーボードショートカットの設定から、ctrl+上下左右キーで上下左右のワークスペースに移動するよう設定。ctrl+shift+上下左右キーでウィンドウを上下左右のワークスペースに移動するように設定。

左上Firefoxとemacs、右上Terminal、左下ThunderbirdとSkypeとPidgin、右下予備というふうに置いておくと、全部フルスクリーンにしておいても画面切り替えでなんとかなるので小さい画面でも戦える。

あとapt関係のアップデートと、ssh, zsh, emacs、mercurial、ruby関連、opencv、boostなどのインストールをする。
参考:Thinkpad X32にUbuntu 8.10 Desktop版をCDから入れた – 橋本詳解



■キーボード
キーボードの設定から「capslockをもう一つのctrlキーに設定」を選択
日本語入力。SCIMの全体設定で開始に変換キー、終了に無変換キーを割り当てる。他は全部キーを削除する。

これでMacと同じように、ホームポジションに置いた時親指で日本語入力をON/OFFできるようになる。

キータッチはネットブックの中ではけっこういいと思う。キーも大きくて押しやすいし、記号キーの配列も変態じゃないからコード書くのにも支障ない。


■電源
バッテリーは2時間もつ。ACアダプタはinspiron miniのが同じ電圧だったのでそっちを持ち歩いている。


■WiFi
Gnome標準の最初からついてるやつでばっちり


■サスペンド
ちゃんとできる。よく使ってます


■Firefox
最初から入ってる。拡張のHideMenubarを入れると画面を有効活用できる。
あとはGreasemonkeyでLDRize、Autopagerize、minibuffer
拡張のIt’s all text、Adblock plus、SBMカウンタ、TabMixPlux、Tomblooなど。
ブックマークは普段使ってるMacbookProからHTML形式でエクスポート・インポートした。
FlashPlayer10もAdobeのサイトでインストールできる。

スムーズスクロールはoffにする。

■Thunerbird

sudo apt-get install thunderbird
Windowsからもってきたプロファイルがそのまま使えた。
~/.mozilla-thunderbird/の下にコピーすれば読み込まれる。


■IM
PidginでMSNメッセンジャーとGoogleTalkが使える。Skypeはダウンロードしてきてインストール。
Pidginはすぐ[ツール]→[個人情報]で「コンタクトリスとの人のみ許可」に設定しないとspamがいっぱい来る。


■Gyazo
Linux版がある。
/usr/local/bin/gyazo として置いて、画面の上のパネルにショートカットを追加してすぐ使えるようにする。


■DropBox
Linux版がある。インストール後に再起動すると使える。
メニューにも登録されるし、dropboxコマンドからも動かせる。
dropbox start
dropbox stop
dropbox status



■Office
OpenOffice.orgが入っている。Word/Excel/PowerPointを見るのは問題ない。excelの高度なマクロなどは再現されてるか不安で、結局あまり使ってない。


■プロジェクタ
設定が難しい。10分ぐらいかけても画面が出せなかった。
プレゼン前に確認しておいた方がいい。




というわけで長時間プログラミングはしたくない環境だけど、おでかけしながらサーバーのタスクを監視したり簡単なスクリプトを書いたりする環境はできた。
よかったよかった

0

昼飯を食いに行ったらカラオケルームに通された

定食屋に行ったらカラオケルームに通された


R0012649R0012650

歌いだしそうな瓜生さん
R0012644


カツ煮定食800円。おいしかった
R0012648


食後のインターネット
R0012651R0012652


ここ。画面と音響設備もあるし昼はミーティングルームとして貸し出したら面白いんじゃないか
IMG_0595IMG_0596

2

Monoで.NETアプリをLinux/Macで動かす

Monoはオープンソースの.NET Framework互換ランタイム。俺はUbuntuやMac OSXで自分の作ったWindowsアプリを動かすのに使っている。

Ubuntuのaptでインストールできるバージョンは2.1、Macのmacportsでは2.4がインストールできるが、Macだけはmono-projectからdmgダウンロードしてきてインストールして使ってる。


ちゃんとGUIも再現される。下はWindowsとVMWare上のUbuntuで同じアプリを起動したところ
C# – UDP/IPでチャットアプリより)
UDPの勉強


■アプリを起動する

mono App.exe
で起動する。DLLなども問題なく読み込まれる。


■Monoで動くように実装するポイント
Mono2.xは.NET Framework2.0と3.0の間ぐらいまで実装されているらしい。
  • DirectXは動かない、というか無い
  • Win32APIももちろん無いので使わない
  • 設定はレジストリではなく.configファイルに保存しよう
  • .NET3.0以降で増えた関数型言語由来の機能はあまり使わない方がいい。ラムダ式とか。
  • delegateやthreadは使える
  • Genericsのマイナーなメソッドは使わない。普通にAddとかRemoveとか使う分にはいいが、高階関数を使ってそうな機能(ソートとか)はあまり触らない方がいい
  • ディレクトリの区切り文字は \ ではなく Path.DirectorySeparatorChar を使う
  • 起動ディレクトリの取得は Application.StartupPath ではなく
    System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
    を使うと、System.Windows.Formsを読み込まなくても済む
基本的にWindowsネイティブな機能を使わないようにしていれば、Generics関連のメソッドが無い以外で困ったことは多分ない。


■開発の手順
  1. WindowsのVisualStudioで開発する。
  2. Monoで動くか確かめるために、VMWareでUbuntuを起動する。
  3. Dokan SSHFSでUbuntuのディスクをマウントする
  4. 適当にコードを書く。Windowsで問題なく動くところまで確認する。
  5. マウントしたディスクにRealSyncで転送する
  6. Ubuntuでも動かしてみる。デバッグの方法がよくわからない。特にMonoで実装されていない関数を実行してしまった時は原因特定に時間がかかってしまう・・・
  7. Ubuntuのapt版Monoで動けばmacportsのMonoでも動く。バージョンが高いので。

0

輪郭で表示

アメリカ出張中に、こういう輪郭だけの表示もありかなと思って作ってみたけどまだ実機で試してないからわからないんだった
そのうちやる。

輪郭表示


■ダウンロ〜ド



■参考
この2つを合体させた。


■boostでprintf風の文字列フォーマット
boost::formatを使う。
#include <boost/format.hpp>
using namespace std;
using namespace boost;
const int INIT_TIME = 50;
cout << str(format("輝度平均 %d/%d") % i % INIT_TIME) << endl;


■ソースコード
bgsubavg-contour/image.cpp
#include <cv.h>
#include <highgui.h>
#include <ctype.h>
#include <stdio.h>
#include <iostream>
#include <boost/format.hpp>

using namespace std;
using namespace boost;

int main(int argc, char **argv)
{
  const int INIT_TIME = 50;
  const double BG_RATIO = 0.02; // 背景領域更新レート
  const double OBJ_RATIO = 0.005; // 物体領域更新レート
  const double Zeta = 10.0;
  IplImage *img = NULL;

  CvMemStorage* storage_contour = cvCreateMemStorage(0);
  CvSeq* find_contour = NULL;

  CvCapture *capture = NULL;
  capture = cvCreateCameraCapture(0);
  //capture = cvCaptureFromAVI("test.avi");
  if(capture == NULL){
    cerr << "capture device not found!!" << endl;
    return -1;
  }

  img = cvQueryFrame(capture);
  CvSize size = cvSize(img->width, img->height);

  IplImage *imgAverage = cvCreateImage(size, IPL_DEPTH_32F, 3);
  IplImage *imgSgm = cvCreateImage(size, IPL_DEPTH_32F, 3);
  IplImage *imgTmp = cvCreateImage(size, IPL_DEPTH_32F, 3);
  IplImage *img_lower = cvCreateImage(size, IPL_DEPTH_32F, 3);
  IplImage *img_upper = cvCreateImage(size, IPL_DEPTH_32F, 3);
  IplImage *imgSilhouette = cvCreateImage(size, IPL_DEPTH_8U, 1);
  IplImage *imgSilhouetteInv = cvCreateImage(size, IPL_DEPTH_8U, 1);
  IplImage *imgResult = cvCreateImage(size, IPL_DEPTH_8U, 1);
  IplImage *imgContour = cvCreateImage(size, IPL_DEPTH_8U, 1);
  IplImage *imgSilhouette_p = cvCreateImage(size, IPL_DEPTH_8U, 1);

  cout << "背景初期化中..." << endl;
  cvSetZero(imgAverage);
  for(int i = 0; i < INIT_TIME; i++){
    img = cvQueryFrame(capture);
    cvAcc(img, imgAverage);
    cout << str(format("輝度平均 %d/%d") % i % INIT_TIME) << endl;
  }
  cvConvertScale(imgAverage, imgAverage, 1.0 / INIT_TIME);
  cvSetZero(imgSgm);
  for(int i = 0; i < INIT_TIME; i++){
    img = cvQueryFrame(capture);
    cvConvert(img, imgTmp);
    cvSub(imgTmp, imgAverage, imgTmp);
    cvPow(imgTmp, imgTmp, 2.0);
    cvConvertScale(imgTmp, imgTmp, 2.0);
    cvPow(imgTmp, imgTmp, 0.5);
    cvAcc(imgTmp, imgSgm);
    cout << str(format("輝度振幅 %d/%d") % i % INIT_TIME) << endl;
  }
  cvConvertScale(imgSgm, imgSgm, 1.0 / INIT_TIME);
  cout << "背景初期化完了" << endl;

  char winNameCapture[] = "Capture";
  char winNameSilhouette[] = "Silhouette";
  char winNameContour[] = "Contour";
  cvNamedWindow(winNameCapture, CV_WINDOW_AUTOSIZE);
  cvNamedWindow(winNameSilhouette, CV_WINDOW_AUTOSIZE);
  cvNamedWindow(winNameContour, CV_WINDOW_AUTOSIZE);
  
  bool isStop = false;
  while(1){
    if(!isStop){
      img = cvQueryFrame(capture);
      if(img == NULL) break;
      cvConvert(img, imgTmp);

      // 輝度範囲
      cvSub(imgAverage, imgSgm, img_lower);
      cvSubS(img_lower, cvScalarAll(Zeta), img_lower);
      cvAdd(imgAverage, imgSgm, img_upper);
      cvAddS(img_upper, cvScalarAll(Zeta), img_upper);
      cvInRange(imgTmp, img_lower, img_upper, imgSilhouette);

      // 輝度振幅
      cvSub(imgTmp, imgAverage, imgTmp);
      cvPow(imgTmp, imgTmp, 2.0);
      cvConvertScale(imgTmp, imgTmp, 2.0);
      cvPow(imgTmp, imgTmp, 0.5);

      // 背景領域を更新
      cvRunningAvg(img, imgAverage, BG_RATIO, imgSilhouette);
      cvRunningAvg(imgTmp, imgSgm, BG_RATIO, imgSilhouette);

      // 物体領域を更新
      cvNot(imgSilhouette, imgSilhouetteInv);
      cvRunningAvg(imgTmp, imgSgm, OBJ_RATIO, imgSilhouetteInv);

      cvErode(imgSilhouette, imgSilhouette, NULL, 2); // 収縮
      cvDilate(imgSilhouette, imgSilhouette, NULL, 4); // 膨張
      cvErode(imgSilhouette, imgSilhouette, NULL, 2); // 収縮
      cvAnd(imgSilhouette, imgSilhouette_p, imgResult);
      
      // 輪郭抽出、青線で囲む
      int contour_num = cvFindContours(cvCloneImage(imgResult), storage_contour, &find_contour,
       sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_NONE, 
       cvPoint(0,0));
      CvScalar white = CV_RGB(255,255,255);
      cvSetZero(imgContour);
      cvDrawContours(imgContour, find_contour, white, white, 2, 2, 8, cvPoint(0,0));
      cvNot(imgContour, imgContour);

      cvShowImage(winNameCapture, img);
      cvShowImage(winNameSilhouette, imgResult);
      cvShowImage(winNameContour, imgContour);
      cvCopy(imgSilhouette, imgSilhouette_p);
    }
    int waitKey = cvWaitKey(33);
    if(waitKey == 'q') break;
    if(waitKey == ' '){
      isStop = !isStop;
      if(isStop) cout << "stop" << endl;
      else cout << "start" << endl;
    }
  }  
    
  cvReleaseCapture(&capture);
  cvDestroyWindow(winNameCapture);
  cvDestroyWindow(winNameSilhouette);
  cvDestroyWindow(winNameContour);
  return 0;
}


Makefile
SRC = image.cpp
DST = image

prefix=/opt/local
INCPATH=$(prefix)/include
LIBPATH=$(prefix)/lib 

OPT= -lcv -lcvaux -lcxcore -lhighgui 

CC=g++ -O

CFLAGS= -I$(INCPATH)/opencv
LDFLAGS=-L. -L$(LIBPATH) 


all:
$(CC) $(SRC)  -o $(DST) $(CFLAGS)  $(LDFLAGS) $(OPT)

0

OpenCVのhaar-like特徴分類器の確認ツール

3週間ぐらい前に作った、OpenCVのhaarcascadeで認識した部分に円を描画するツールがけっこう便利なので公開する。
Mac OSXおよびUbuntu用でOpenCV1.0とboostが入ってれば動くはず。

OpenCVをダウンロードして付いてくるサンプルにも似た物が入ってるけど、カメラや動画や画像を入力としてGUIで表示するものだった。

俺は大量の静止画を判定して振り分けたりしたかったので、認識した物体の位置とサイズをスクリプト言語で受け取るために標準出力したり、マーキングした後の画像をファイル名を指定して保存したり、プレビューウィンドウを出すか選べるように改造した。

それと今後のためにboostとOpenCVを同時に使ってみたかったというのもある。
パラメータの解析にはboost::program_optionsを使って、画像まわりは全部OpenCVで書いてある。


■ダウンロード

MacとUbuntu用のMakefileつき。それぞれportとaptでOpenCVとboostをインストールしてあればmakeできる


■実行例
ふつうに顔認識。プレビューウィンドウも出す。結果はresult.jpgに保存。
haartest -p -c ~/haarcascades/haarcascade_frontalface_default.xml -p -i ~/Pictures/faces/shokai-umbrella.jpg -o result.jpg
detect face


sourceforgeのOpenCVのリポジトリの開発版に、目にマッチするhaarcascadeファイルがあるので持ってきて使った。目に似ている部分がたくさんあるので誤認識した。
haartest -p -c ~/haarcascades/haarcascade_mcs_righteye.xml -p -i ~/Pictures/faces/emushi-2face.jpg -o --nogray
emushi-2face.jpg_haarcascade_mcs_mouth



■boost::program_optionsで引数を取る
引数なし、もしくは–helpで実行するとこんなのが出るようにしてある。
cascadeとinputが必要です
options:
-h [ --help ] ヘルプを表示
-c [ --cascade ] arg haarcascade設定ファイル
-i [ --input ] arg 入力画像ファイル名
-o [ --output ] arg 出力ファイル名
-p [ --preview ] プレビュー表示
-f [ --flip ] 左右反転した画像も判定(青でマーク)
--nogray グレースケール、ヒストグラム均一化せずに判定
–input shokai.jpgとか-i shokai.jpgとかで引数を指定。(inputもiも同じとみなすようにヒモ付けできる)
左右反転は、左右非対称なものを認識する時に使える。右手のひら判定で左手も検出したい時とか。


詳しくは下記へ

ちなみにprogram_optionsで取った引数をOpenCVに渡すときは
cvLoadImage( argmap["input"].as<string>().c_str() )
のような感じでC++のstring型からCのchar配列にcastして渡す。


■スクリプト言語から使う
Rubyのワンライナーからディレクトリ内のJPG画像をまとめて顔認識して結果をresultsディレクトリに保存する例
mkdir results
ruby -e 'Dir.glob("*.jpg").each{|f| `haartest -c ~/haarcascades/haarcascade_frontalface_default.xml -i #{f} -o results/#{f}`}'



■コード
haartest.cpp
#include "cv.h"
#include "highgui.h"
#include <boost/program_options.hpp>
#include <iostream>
using namespace boost;
using namespace std;

void detect_draw(IplImage *img, IplImage *dst, CvHaarClassifierCascade *cascade, CvScalar color, bool isFlip);

int main(int argc, char* argv[]) {
  program_options::options_description opts("options");
  opts.add_options()
    ("help,h", "ヘルプを表示")
    ("cascade,c", program_options::value<string>(), "haarcascade設定ファイル")
    ("input,i", program_options::value<string>(), "入力画像ファイル名")
    ("output,o", program_options::value<string>(), "出力ファイル名")
    ("preview,p", "プレビュー表示")
    ("flip,f","左右反転した画像も判定(青でマーク)")
    ("nogray", "グレースケール、ヒストグラム均一化せずに判定");
  program_options::variables_map argmap;
  program_options::store(parse_command_line(argc, argv, opts), argmap);
  program_options::notify(argmap);
  if (argmap.count("help") || !argmap.count("cascade") || !argmap.count("input")) {
    cerr << "cascadeとinputが必要です" << endl;
    cerr << opts << endl;
    return 1;
  }
  
  CvHaarClassifierCascade *cascade;
  cascade = (CvHaarClassifierCascade*)cvLoad(argmap["cascade"].as<string>().c_str(), 0, 0, 0);
  if(!cascade){
    cerr << "error! Cascade not Found" << endl;
    return -1;
  }
  
  IplImage *image = cvLoadImage(argmap["input"].as<string>().c_str());
  IplImage *image_orig = cvCreateImage(cvSize(image->width, image->height), 8, 3);
  cvCopy(image, image_orig);
  if(!image){
    cerr << "error! Image File not Found" << endl;
    return -11;
  }

  if(!argmap.count("nogray")){
    IplImage* gray = cvCreateImage(cvSize(image->width, image->height), 8, 1);
    cvCvtColor(image, gray, CV_BGR2GRAY);
    cvEqualizeHist(gray, gray);
    image = gray;
  }
  
  if(argmap.count("preview")||argmap.count("output")){
    detect_draw(image, image_orig, cascade, CV_RGB(255, 0, 0), false);
    if(argmap.count("flip")){
      cvFlip(image, image);
      detect_draw(image, image_orig, cascade, CV_RGB(0, 0, 255), true);
    }
  }

  if(argmap.count("output")){
    string out_filename = argmap["output"].as<string>();
    cout << "save! " << out_filename << endl;
    cvSaveImage(out_filename.c_str(), image_orig);
  }
  
  if(argmap.count("preview")){
    char winName[] = "haarcascade test";
    cvNamedWindow(winName, CV_WINDOW_AUTOSIZE);
    cvShowImage(winName, image_orig);
    while (1) {
      if (cvWaitKey(1) == 'q') break;
    }
    cvDestroyWindow(winName);
  }
  
  cvReleaseImage(&image);
  cvReleaseImage(&image_orig);
  return 0;
}

void detect_draw(IplImage *img, IplImage *dst, CvHaarClassifierCascade *cascade, CvScalar color, bool isFlip = false){
  CvMemStorage *storage = 0;
  storage = cvCreateMemStorage(0);
  CvSeq* faces = cvHaarDetectObjects(img, cascade, storage,
                                     1.1, 2, CV_HAAR_DO_CANNY_PRUNING,
                                      cvSize(30, 30));

  for(int i = 0; i < faces->total; i++){
    CvRect *rect = (CvRect*)cvGetSeqElem(faces, i);
    cout << "x:" << rect->x << ", y:" << rect->y
         << ", width:" << rect->width << ", height:" << rect->height << endl;
    CvPoint center;
    center.x = rect->x + rect->width/2.0;
    center.y = rect->y + rect->height/2.0;
    if(isFlip){
      center.x = dst->width - center.x;
      center.y = dst->height - center.y;
    }
    int r = (rect->width + rect->height)/4.0;
    cvCircle(dst, center, r, color, 2, CV_AA, 0);
  }
}


Makefile (Mac用)
# Mac用Makefile
SRC = haartest.cpp
DST = haartest

prefix=/opt/local
INCPATH=$(prefix)/include
LIBPATH=$(prefix)/lib

CV_LIBS= -lcv -lcvaux -lcxcore -lhighgui
BOOST_LIBS= $(LIBPATH)/libboost_program_options-mt.dylib

all:
g++ -O $(SRC) -o $(DST) -I$(INCPATH)/opencv -L. -L$(LIBPATH) $(CV_LIBS) -I$(INCPATH)/boost $(BOOST_LIBS)


Makefile (Linux用)
# Ubuntu用Makefile
SRC = haartest.cpp
DST = haartest

prefix=/usr
INCPATH=$(prefix)/include
LIBPATH=$(prefix)/lib

CV_LIBS= -lcv -lcvaux -lcxcore -lhighgui
BOOST_LIBS= $(LIBPATH)/libboost_program_options-mt.a

all:
g++ -O $(SRC) -o $(DST) -I$(INCPATH)/opencv -L. -L$(LIBPATH) $(CV_LIBS) -I$(INCPATH)/boost $(BOOST_LIBS)