2009年5月30日土曜日

mov(H.264)形式の動画をPowerPointに挿入する方法

PowerPointでスライドに挿入できる(ことになっている)動画は,大雑把には
  • AVI
  • WMV
  • MPEG1/2
の3種類です.今実験機の撮影に使っているカメラで得られる動画はmov形式(QuickTime)なので,プレゼンで使うPowerPointに挿入するためには変換が必要になります.
簡単なことかと思いきや,意外と苦労しているのでメモを残しておきます.プレゼンでは自分のじゃないマシンで発表しないといけなくなったりすることも多いので,特殊なコーデックの追加とかは試していません.
ちなみに環境はWindows Vista / Powerpoint 2007. 動画はCasio EX-F1でSTD画質(640x480,30fps)で撮影したH.264 mov形式のファイルです.
QuickTime Pro 単体ではうまくいかなかった
一番素直な解はQuickTime Pro を購入して.movファイルを.aviに変換することだと思われるので,とりあえずやってみました.PowerPointに挿入するところまではうまくいったのですが,クリックすると動画が黒くなって再生されません.無圧縮のAVIでは再生まで可能ですが,ファイルサイズが大き過ぎる上に,再生時のディスクアクセスによるコマ落ちが激しく,実用には耐えません.
Windows Media エンコーダを併用すると一応OK
QuickTime Pro を使って一旦無圧縮のAVIを作成した後で,Windows Media エンコーダを併用すると,PowerPointに挿入可能な高画質なwmvファイルを作成できます.画質にこだわらなければ,aviからの変換にWindowsムービーメーカーを使うこともできます.この方法は作業が二段階になる上に有償のQuickTime Proが必要です.研究室の学生全員に使ってもらうにはちょっと面倒すぎる気がします.
Mp4cam2aviではH.264動画を無圧縮aviにすることはできない
mov形式のファイルを無圧縮aviにできそうなフリーソフトとしてMp4cam2aviがあげられますが,今のところH.264の動画のデコードには対応していないようです.
FFmpegでの結果
FFmpegを使うと動画の多彩な変換が可能ですが,PowerPointへの挿入はなかなかうまくいきません.結果を以下の表にまとめます.ちなみに,いずれの形式に変換したときもWindows Media Playerでの再生はできています.
コマンドライン
結果
$ ffmpeg -i test.mov \
  -vcodec wmv1 test.avi
プレビューは正常
再生で暗転

$ ffmpeg -i test.mov \
-vcodec wmv2 test.avi
プレビューは正常
再生で暗転

$ ffmpeg -i test.mov \
-vcodec msmpeg4 test.wmv
挿入時から黒いまま
$ ffmpeg -i test.mov \
-vcodec msmpeg4v2 test.wmv
挿入時から黒いまま
$ ffmpeg -i test.mov \
  -vcodec wmv1 test.wmv
挿入時から黒いまま
$ ffmpeg -i test.mov \
  -vcodec wmv2 test.wmv
挿入時から黒いまま
$ ffmpeg -i test.mov test.mpeg
正常
$ ffmpeg -i test.mov test.m2v
正常(音声無し)
実質的にはmpegの一択ということになります.デフォルトではビットレートが低く(200kbps)画質が実用に耐えないので
$ ffmpeg –i test.mov –b 8192k test.mpeg
という感じでしょうか.
まとめ
色々やってみましたが,
  • ベストを尽くすなら
    1. QuickTime Pro で無圧縮AVIに変換
    (試してないですが,ffmpegでも同等の変換ができるはず?)
    2. Windows Media エンコーダでwmvに変換
    の2段階
  • そこそこでいいなら
    $ ffmpeg –i test.mov –b 8192k test.mpeg
という結論に達しました.あまり高画質な動画を作ってもプレゼンでの効果以上にマシンの要求スペックが上がりそうで怖いですし,ffmpegの変換サーバーを立てて,みんなで使うのが研究室レベルでは正解な気がします.
他にいい方法があればご教授いただければ幸いです.

2009年5月26日火曜日

Arduinoで簡易オシロスコープ

2013/4/22 ライセンスを修正BSDライセンスとしました
Arduinoで簡単に使える簡易オシロスコープもどきを作ってみました.ちょっとした実験にはなかなか便利なので公開しておきます.

スクリーンショット

全体図scr1
マウスで周波数などの読み取りも可能
scr2

操作法

  • [↑][↓] 時間軸の変更
  • [space] データ取得の停止/再開
  • [s] データをcsv形式で保存
  • [マウス操作] 値の読み取り・ドラッグで周波数読み取り

インストール

  • 下記のソースの末尾・Arduino用のプログラムの部分をArduinoに書き込んでおく.
  • 下記のソースをProcessingにコピーし,シリアルポートの設定とフォントの生成/設定を行う.
  • 実行してみる

ソース


// ArduinoScope v1.0.0
// 
// Note:
//  1. please confirm serial port setting
//  2. prepare font
//  3. Arduino code is attached at the end of this code

// Copyright (c) 2009-2013 I. Maruta <ichiro.maruta@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Neither the name of the author nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.

 

import processing.serial.*;

Serial ArduinoPort;  // Create object from Serial class

int NumOfScopes,NumOfInput=2;
int data_span=10000;
Strage dfs = new Strage();
Scope[] sp;

int fontsize=16;
PFont myFont;

void setup() 
{
  // Serial Port 
  println(Serial.list());
  String portName = Serial.list()[0]; // TODO: automatic detection?
  ArduinoPort = new Serial(this, portName, 38400);
  ArduinoPort.bufferUntil(10);

  // Screen
  size(800, 600);
  NumOfScopes=2;
  sp = new Scope[NumOfScopes];
  sp[0]= new Scope(0,50,10,width-100,height/2-35,512,-512,1000);
  sp[1]= new Scope(1,50,height/2+15,width-100,height/2-35,1024,0,1000);

  myFont = loadFont("Dotum-16.vlw");
  textFont(myFont,fontsize);
}

class Scope{
  int input_id;    // corresponding input
  int posx,posy;   // screen position of the scope
  int sizex,sizey; // pixel size of the scope
  float yu,yl;     // range of y is [yl,yu]
  int tspan;       //
  int ngx,ngy; // number of grids
  float maxposx,maxposy,minposx,minposy,maxx,minx,maxy,miny;

  Scope(int did,int px,int py,int sx,int sy,float syu,float syl,int ts){
    input_id=did;
    posx=px; 
    posy=py;
    sizex=sx; 
    sizey=sy;
    yu=syu; 
    yl=syl;
    tspan=ts;
    ngx=10; 
    ngy=4;
  }

  void grid(){
    pushStyle();
    fill(255,196);
    stroke(0,0,150);
    for(float gx=sizex; gx>=0; gx-= (float)sizex/ngx){
      line(posx+gx,posy,posx+gx,posy+sizey);
      textAlign(CENTER,TOP);
      text((int)map(gx,sizex,0,0,-tspan),posx+gx,posy+sizey+2);    
    }
    for(float gy=sizey; gy>=0; gy-= (float)sizey/ngy){
      line(posx,posy+gy,posx+sizex,posy+gy);
      textAlign(RIGHT,CENTER);
      text((int)map(gy,0,sizey,yu,yl),posx,posy+gy);    
    }
    popStyle(); 
  }

  int curx,cury;

  // draw cursor
  void cur()
  {
    // return if mouse cursor is not in this scope
    if(constrain(mouseX,posx,posx+sizex)!=mouseX 
      || constrain(mouseY,posy,posy+sizey)!=mouseY) return;

    pushStyle();

    // draw cross cursor
    stroke(255,0,0,196);
    fill(255,0,0,196);
    line(mouseX,posy,mouseX,posy+sizey);
    line(posx,mouseY,posx+sizex,mouseY);

    // draw measure if mouse is dragged
    if(mousePressed){
      line(curx,posy,curx,posy+sizey);
      line(posx,cury,posx+sizex,cury);
      textAlign(RIGHT,BOTTOM);
      text((int)map(curx,posx,posx+sizex,-tspan,0)+"ms, "+(int)map(cury,posy,posy+sizey,yu,yl),curx,cury);
      textAlign(LEFT,TOP); 
      text("("+nfp((int)map(mouseX-curx,0,sizex,0,tspan),1)+"ms, "+nfp((int)map(mouseY-cury,0,sizey,0,-(yu-yl)),1)+")\n"+nf(1000/map(mouseX-curx,0,sizex,0,tspan),1,2)+"Hz\n"+nf(TWO_PI*1000/map(mouseX-curx,0,sizex,0,tspan),1,2)+"rad/sec",mouseX,mouseY+2);
    }
    else{
      curx=mouseX;
      cury=mouseY;
      textAlign(RIGHT,BOTTOM);
      text((int)map(curx,posx,posx+sizex,-tspan,0)+"ms, "+(int)map(cury,posy,posy+sizey,yu,yl),curx,cury);
    }
    popStyle();
  }

  // draw min&max tick
  void minmax(){
    pushStyle();
    fill(255,128);
    stroke(0,0,100);
    textAlign(RIGHT,CENTER);
    line(posx,maxposy,posx+sizex,maxposy);
    text((int)maxy,posx,maxposy); 
    line(posx,minposy,posx+sizex,minposy);
    text((int)miny,posx,minposy); 
    textAlign(LEFT,CENTER);
    textAlign(CENTER,TOP);
    text("max",maxposx,maxposy); 
    textAlign(CENTER,BOTTOM);
    text("min",minposx,minposy+20); 
    popStyle();
  }

  // draw scope
  void Plot(){
    float sx,sy,ex,ey;
    int nof=0;
    DataFrame df_last = dfs.get(0);

    maxy=-1e10; // -inf
    miny=1e10;  // +inf

    // draw background (for transparency)
    pushStyle();
    noStroke();
    fill(0,0,64,64);
    rect(posx,posy,sizex,sizey);
    popStyle();

    // draw data plot
    pushStyle();
    stroke(0,255,0);
    smooth();
    strokeWeight(1);
    for(int idx=0;(dfs.get(idx).t>max(df_last.t-tspan,0)) && -idx<data_span;idx--){
      DataFrame df_new=dfs.get(idx);
      DataFrame df_old=dfs.get(idx-1);
      sx=(float) map(df_new.t, df_last.t, df_last.t - tspan, posx+sizex,posx);
      ex=(float) map(df_old.t, df_last.t, df_last.t - tspan, posx+sizex,posx);
      sy=(float) map((float)df_new.v[input_id],(float) yu,(float) yl,(float) posy,(float) posy+sizey );
      ey=(float) map((float)df_old.v[input_id],(float) yu,(float) yl,(float) posy,(float) posy+sizey );
      if(ex<posx){
        ey+=(sy-ey)*(posx-ex)/(sx-ex);
        ex=posx;
      }
      line(sx,sy,ex,ey);
      maxy=max(maxy,df_new.v[input_id]);
      if(maxy==df_new.v[input_id]){
        maxposx=sx;
        maxposy=sy;
      }
      miny=min(miny,df_new.v[input_id]);
      if(miny==df_new.v[input_id]){
        minposx=sx;
        minposy=sy;
      }
      nof++;
    }
    popStyle();

    //    minmax();    

    // draw current value of input
    pushStyle();
    textAlign(LEFT,CENTER);
    stroke(0,0,64);
    fill(0,255,0,196);
    text(df_last.v[input_id],posx+sizex,map(df_last.v[input_id], yu, yl, posy, posy+sizey ));
    popStyle();   

    grid();
    cur();    
  }
}

void draw() 
{ 
  background(0);

  for(int i=0;i<NumOfScopes;i++){
    sp[i].Plot();
  }
}

// input data buffer class
// (now using ring buffer)
class Strage{
  int cur;
  DataFrame[] DataFrames;

  Strage(){
    cur=0;
    DataFrames=new DataFrame[data_span];
    for(int idx=0;idx<data_span;idx++){
      int ret_v[] = new int[NumOfInput];
      DataFrames[idx] = new DataFrame(0,ret_v);
    }
  }

  void push(DataFrame d){
    cur = ((cur+1) %data_span);
    DataFrames[cur]=d;
  }

  DataFrame get(int idx)
  {
    int num=(cur+idx);
    for(; num<0; num+= data_span);
    return((DataFrame) DataFrames[num]);
  }

  void save()
  {
    String savePath = selectOutput();  // Opens file chooser
    if (savePath == null) {
      // If a file was not selected
      println("No output file was selected...");
    }else{
      PrintWriter output;
      output = createWriter(savePath); 
      DataFrame df_last = this.get(0);
      for(int idx=0;-idx<data_span;idx--){
        if(this.get(idx).t==0) break;
        output.print(this.get(idx).t-df_last.t);
        for(int k=0;k<NumOfInput;k++){
          output.print(","+this.get(idx).v[k]);
        } 
        output.println("");
      }
      output.flush();
      output.close();
    }
  }
}

class DataFrame{
  int t;
  int[] v;
  DataFrame(int st, int[] sv){
    t=st;
    v=sv.clone();
  }
}

boolean isactive=true;

// buffering data from serial port
void serialEvent(Serial myPort)
{
  int[] vals=new int[NumOfInput];
  int timestamp;
  int[] splitdata;
  if( myPort.available() > 0) { 
    String datline=myPort.readString();
    splitdata=parseInt(datline.split(","));
    if((splitdata.length==NumOfInput+2)){
      timestamp=splitdata[0];
      for(int idx=0;idx<NumOfInput;idx++){
        vals[idx]=splitdata[idx+1];
      }
      if(isactive){
        if((timestamp-dfs.get(0).t)<0){
          dfs.cur--;
        }
        if((timestamp-dfs.get(0).t) > ((float)sp[0].tspan / sp[0].sizex/2.0) ){
          dfs.push( new DataFrame(timestamp,vals));
        }
      }
    }
  }
}

// keyboard user interface
void keyPressed(){
  switch(key){
    // activate/deactivate scope update
  case ' ':
    isactive=!isactive;
    break;
    // save record
  case 's':
    dfs.save();
    break;
  case CODED:
    switch(keyCode){
      // Increse time span
    case UP:
      for(int i=0;i<NumOfScopes;i++){
        sp[i].tspan*=2;
      }
      break;
      // Decrease time span
    case DOWN:
      for(int i=0;i<NumOfScopes;i++){
        sp[i].tspan/=2;
      }
      break;
    }
    break;
  }
}

/* Arduino code
void setup()
{
  Serial.begin(38400);  
}

void loop()
{
  Serial.print(millis());
  Serial.print(",");
  Serial.print(analogRead(0));
  Serial.print(",");
  Serial.print(analogRead(1));
  Serial.println(",");
}
*/

たぬき?

きょう宇治構内の水路にたぬきみたいな動物が出没しました.思わず写真を撮ってしまった・・・

たぬき正面

夜中には結構いるんですが,昼間に見るのは初めてです.よほど飢えていたのか・・・

水路内たぬき たぬき振り向き

たぬき手を洗ってる

たぬき隠れた

2009年5月22日金曜日

SyntaxHighlighter2でMATLABソースを表示 in Blogger

以前SyntaxHighlighterをMATLAB対応にしました.その後,ファイルを置いてあるサーバーの更新があったり,SyntaxHighlighterがバージョン2になったりで使えない状態で放置していたので,改めて作りなおしてみました.

BloggerでSyntaxHighlighter2を使う

まず最初にBloggerでSyntaxHighlighter2をつかうときの設定ですが,テンプレートに

<!--ここから -->
    <script src='http://alexgorbatchev.com/pub/sh/2.0.278/scripts/shCore.js' type='text/javascript'/>
    <script src='http://alexgorbatchev.com/pub/sh/2.0.278/scripts/shBrushPlain.js' type='text/javascript' />
    <script src='http://alexgorbatchev.com/pub/sh/2.0.278/scripts/shBrushXml.js' type='text/javascript' />
    <script src='http://alexgorbatchev.com/pub/sh/2.0.278/scripts/shBrushCss.js' type='text/javascript' />
    <script src='http://alexgorbatchev.com/pub/sh/2.0.278/scripts/shBrushJScript.js' type='text/javascript' />
    <script src='http://maru.bonyari.jp/storage/shBrushMatlab.js' type='text/javascript'/>
<!--ここまで -->
</head>
...
<!-- end outer-wrapper -->
<!--ここから -->
<script type='text/javascript'>
//<![CDATA[ 
    SyntaxHighlighter.config.bloggerMode = true;
    SyntaxHighlighter.all();
//]]>
</script>
<!--ここまで -->

のように追加するといいようです.config.bloggerModeの設定がないと改行の位置にBRタグの出来そこないが出現します.

MATLABのソースをSyntaxHighlighterで成形する

【使い方】

上記のファイルを通常の”shBrush*.js”と同じように読み込んで,class=”brush:matlab”で使えます.上のサーバーに置いてあるのを読み込んでも構いませんが,無保証自己責任でお願いします.ちなみにSyntaxHighlighter本体も本家でホスティングされているようですね.(リンク)
上の設定例はこのホストを使う設定にしてます.

2009年5月18日月曜日

MATLABで並列計算(PSO)

素のMATLABは並列計算に弱いようで,最近のデュアルコアのPCだと,プロセッサ使用量が50%で頭打ちします.

ただ,最近学校の MATLAB ライセンスに Parallel Computing Toolbox が加わって一部関数が置き換えられたためか,単にバージョンアップの恩恵なのかは知りませんが,一部の計算ルーチンがマルチコア用にコンパイルされているらしく,計算によっては50%を超えるプロセッサ使用率を見ることもできるようになりました.

でも依然として,大半の処理は片方のコアのみで処理されるようです.
本来は並列計算できる部分は処理系が自動的に判別して最適に処理してくれるのが理想なんですが,なかなか実現されないですね.また,最近はコア1つ当たりの性能向上は収束気味で,今後はコアの数が増える方向にPCは進化するようなので,今後MATLABでPCの進化の恩恵を享受するためには,並列計算のスキルを身につけておく必要がありそうです.

MATLAB で Parallel Computing Toolbox が使える場合,単一マシンで並列処理をするのはとても簡単で
matlabpool 2 %デュアルコアの場合
parfor idx=1:100
   % 並列化する処理
end
matlabpool close

とやれば並列に処理されます.matlabpool の後の数字は生成されるスレッド数です.

結構並列化のオーバーヘッドがあるようで,単位処理の計算量があまりに小さい場合,かえって遅くなることもあるようです.結果が芳しくないときはプロファイラでチェックしてみるべきですね.

Particle Swarm Optimization (PSO) なんかは並列計算の恩恵を最大限に受けられるアルゴリズムなので,目的関数の評価に時間がかかる問題であれば,処理速度がほぼコア数倍になります.(といってもまだデュアルコアでしか試してませんが・・・)

もうちょっと手間をかけると複数のマシンをつないでクラスタリングとかもできるようなので,人がいない夜中とかに実験室のマシンを束ねて試してみようかな〜

・・・・と思ったらクラスタリングには MATLAB Distributed Computing Server っていう別製品が必要なようですね.
また,Parallel Computing Toolbox ではスレッドの数も4つまでに制限されてるようです.
つまり現状ではクアッドコアのXeon W3570以上のCPUを使っても性能向上は期待できないということかな?
値段を考えると2.66GHzのXeon W3520か,近々更新される見込みなCore i7あたりが計算用マシンのCPUとしてはベストになるのだろうか・・・