2017年1月1日日曜日

Octaveレッスン(16) - surf関数で3次元影付き表面プロット

明けましておめでとうございます。
surf関数で、新年の3Dグラフ表示をしてみました。

下記 bird_3d 関数を実行すると、ニワトリ風な物体が表示されます。
surf関数で複数の楕円体を表示しています。

---- bird_3d.m ----
function bird_3d
  t = 0:0.05*pi:pi;
  p = 0:0.05*pi:2*pi;
  figure
  %body
  a=b=c=10
  x = a*sin(t)'*cos(p);
  y = b*sin(t)'*sin(p);
  z = c*cos(t)'*ones(size(p));
  surf(x,y,z)
  xlabel('x'),ylabel('y'),zlabel('z')
  hold on
  %eyes
  a=b=c=1
  x = a*sin(t)'*cos(p);
  y = b*sin(t)'*sin(p);
  z = c*cos(t)'*ones(size(p));
  surf(x+9,y+3,z+3)
  surf(x+9,y-3,z+3)
  %foot
  a=8,b=2,c=1
  x = a*sin(t)'*cos(p);
  y = b*sin(t)'*sin(p);
  z = c*cos(t)'*ones(size(p));
  alpha=pi/8;
  x_org=x; y_org=y;
  y=cos(alpha)*y_org - sin(alpha)*x_org;
  x=sin(alpha)*y_org + cos(alpha)*x_org;
  surf(x+2,-y+4,z-9)
  surf(x+2, y-4,z-9)
  %crown
  a=b=2,c=4;
  x = a*sin(t)'*cos(p);
  y = b*sin(t)'*sin(p);
  z = c*cos(t)'*ones(size(p));
  surf(x+2,y,z+9)
  surf(x,y,z+10)
  surf(x-2,y,z+9)
  %bill
  a=6,b=2,c=1
  x = a*sin(t)'*cos(p);
  y = b*sin(t)'*sin(p);
  z = c*cos(t)'*ones(size(p));
  surf(x+10,y,z)
  surf(x+10,y,z-1)
  %wings
  t = 0:0.1*pi:pi;
  p = 0:0.1*pi:2*pi;
  a=4,b=8,c=1
  x = a*sin(t)'*cos(p);
  y = b*sin(t)'*sin(p);
  z = c*cos(t)'*ones(size(p));
  alpha=-pi/8;
  y_org=y;z_org=z;
  y=cos(alpha)*y_org - sin(alpha)*z_org;
  z=sin(alpha)*y_org + cos(alpha)*z_org;
  alpha=-pi/8;
  x_org=x;z_org=z;
  x=cos(alpha)*x_org - sin(alpha)*z_org;
  z=sin(alpha)*x_org + cos(alpha)*z_org;
  surf(x, y+8,z-1)
  surf(x,-y-8,z-1)
endfunction
----

参考:

2016年12月26日月曜日

Octaveレッスン(15) - plot3関数で3次元グラフ表示

plot3関数を使用すると3次元グラフを簡単に表示することができる。

plot3(x, y, z) のようにx、y、zそれぞれの座標データを引数に与えればよい。

Octaveレッスン(6) で見た、FFTの基底の原始N乗根 ωN を3次元グラフに表示してみる。

ここでは N=128とする。
>> N=128, omega=e^(i*(2*pi/N));
>> x=0:N*20-1;
>> f=omega .^ x;
>> plot3(x, real(f), imag(f))
>> grid on
>> xlabel('X'), ylabel('real'), zlabel('imag')

グラフウィンドウの回転アイコン(左上端の円状の矢印)をクリックすると、グラフを回転させて、さまざまな角度から眺めることができる。
ぐるぐるグラフを回転させて x-y(real)平面を表示させると、cos関数のグラフが現れる。
さらに回転させて x-z(imag)平面を表示させると、sin関数のグラフが現れる。
y(real)-z(image)平面では、単位円のグラフが現れる。


参考:


2016年12月24日土曜日

Octaveレッスン(14) - ltfat packageを利用する

ltfat packageには、fwt関数などwavelet解析に便利な関数が用意されている。

windows版Octaveでは、下記コマンドで ltfat のインストールに成功するが、 Mac版では失敗する。
>> pkg install -forge ltfat
ドキュメントを読む限り、Mac版もWindows版も同じ手順でインストールできないとおかしいのだが、別のMacや旧いOSバージョン、旧いOctaveバージョンでも同様に失敗する。

そこで、別のアプローチを試してみたところ成功したので、メモを残しておく。

Mac版で動かすには、下記ページからltfat packageをダウンロードして展開する:
http://ltfat.sourceforge.net/download.php

Octaveのコマンドウィンドウにて、展開した ltfat ディレクトリへ移動して、
>> ltfatstart
と実行すれば、各種 ltfat の関数が利用可能になる。
waveletのデモを表示するには、
>> demo_wavelets
さらに処理速度を高速化したい場合には、
>> ltfatmex
と実行すればよい・・・ ハズなのだが失敗する。
fftw3ライブラリが見つからず、コンパイルエラーが発生する。

下記URLからfftw-3.3.5.tar.gzをダウンロードしてfftw3ライブラリをコンパイルする。
http://www.fftw.org/download.html

ターミナルを開いて、展開したfftw-3.3.5ディレクトリに移動して下記コマンドを実行:
$ ./configure --enable-threads
$ make 
libfftw3.a, libfftw3_threads.a が生成されるので、場所をfind等で探して、ltfat/lib へコピーする。
続いて、float対応版ライブラリをコンパイルする:
$ make clean
$ ./configure --enable-float --enable-threads
$ make 
libfftw3f.a, libfftw3f_threads.a が生成されるので、場所をfind等で探して、ltfat/lib へコピーする。

Octaveのコマンドウィンドウに戻って、
>> ltfatmex
と再実行すると、今度は成功する。

※ ltfatmex 実行すると、PortAudio が無いというコンパイル警告が出るため、私はPortAudioもダウンロード&インストールした。しかし、これは必須では無いはず。
 PartAudio関連サイト:

Octaveレッスン(13) - signal packageを利用する

fft関数などは、Octaveをインストールするだけで利用可能。
しかし、xcorr関数(相互相関)などを利用しようとすると、signal packageが必要になる。

windows版の場合は、Octaveインストール時にsignalパッケージもインストールされるので、下記コマンドでsignal packageをロードすれば利用可能になる
>> pkg load signal
Macの場合は、まずinstallする必要がある:
>> pkg install -forge signal
上記は、ネットワーク経由でダウンロード&インストールするコマンドだが、おそらくcontrol package が未インストールのため失敗するだろう。

そこで、まず control packageをinstallする:
>> pkg install -forge control
依存関係のあるcontrolをインストールできたので、signalをインストールしてロードする:
>> pkg install -forge signal
>> pkg load signal 
これで、xcorr関数などが利用可能になる。

なお、インストール済みパッケージは下記コマンドで一覧表示可能:
>> pkg list
また、別 package 無しで利用可能な関数の一覧は、[ドキュメント]タブ - 「Function Index」sectionで確認可能。

2016年11月27日日曜日

Octaveレッスン(12) - 区間ごとに異なる周波数の正弦波を生成する

* 区間ごとに異なる周波数の正弦波を生成する

「Octaveレッスン(11) - 自己相関から周波数を求める」で区間ごとに周波数(HzVector)を求めた。

以下、HzVectorで与えられる周波数情報を元に、区間ごと正弦波を生成する例
---- genWsSin.m ----
function WsSin = genWsSin(HzVector, Ws, Fs)
    if(nargin == 0)
      disp("usage: WsSin = genWsSin(HzVector, Ws, Fs)");
      return
    endif
    len=size(HzVector)(1,1);
    preHz=-1;
    dur=0;
    WsSin=[];
    for k = [1:len-1]
      crrHz=HzVector(k,1);
      if crrHz==preHz
        dur += Ws;
      else
        if dur > 0
          WsSin = horzcat(WsSin, gen_pre_sin(preHz, dur, Fs));
        endif
        preHz=crrHz;
        dur=Ws;
      endif      
    endfor

    if dur > 0
      WsSin = horzcat(WsSin, gen_pre_sin(preHz, dur, Fs));
    endif
endfunction

function y = gen_pre_sin(preHz, dur, Fs)
  x=[0:dur-1];
  y=sin(2*pi*preHz/Fs*x);

endfunction

----------------

実行例:
[y, Fs] =audioread('Ongen2/egypt_charmera.wav');
Ws=1024, R=corrWs(y, Ws);
[WsHzVector, HzVector] = findCorrHz(R, Ws, Fs);
WsSin = genWsSin(HzVector, Ws, Fs);
sound(WsSin,Fs)

⇒ 笛の音を録音した音声信号を1024サンプルの区間に分割して、区間ごとに周波数を検出。
  genWsSin()関数を使用して、区間ごとに正弦波で再構成した音WinSinを再生すると、元の音色に近い音階が聞こえる


2016年11月26日土曜日

Octaveレッスン(11) - 自己相関から周波数を求める

*自己相関から周波数を求める
自己相関のグラフに見られる極値の周期は、元の信号の周期と合致する。
Octaveでは、findpeaks関数を使用して、極値のリストを得ることができる。
[pks, locs] = findpeaks(S,"DoubleSided", "MinPeakHeight", 0.3);
と実行すると、pksに±0.3以上の極値(極大値&極小値)のリストが返され、locsに極値のインデクスが返される。
隣接する3つの極致のインデクスの差分を求めれば、周期が求まる。周波数は周期の逆数。

以下、極値の座標を求めて、周波数を求める関数の例
---- findCorrHz.m ----
function [WsHzVector, HzVector] = findCorrHz(R, Ws, Fs)
    if(nargin == 0)
      disp("usage: [WsHzVector, HzVector] = findCorrHz(R, Ws, Fs)");
      return
    end
    Thresh = 0.3;
    HzVector = [];
    WsHzVector = [];
    len=size(R)(1,1);
    for k = [1:Ws:len]
      if k+Ws > len
        break
      endif
      Sub=R(k:k+Ws-1);
      [pks, locs] = findpeaks(Sub,"DoubleSided", "MinPeakHeight", Thresh);
      locs_len = size(locs)(1,1);
      if locs_len <= 1
        Ts = -Ws;
        Hz = 0;
      else
        Ts = abs(locs(2,1) - locs(1,1));
        if locs_len == 2
          Ts += Ts;
        else
          Ts += abs(locs(3,1) - locs(2,1));
        endif
        Hz = Fs/Ts;
      endif
      HzVector=vertcat(HzVector, Hz);
      WsHzVector=vertcat(WsHzVector, repmat(Hz,1,Ws)');
    endfor     

endfunction
----------------
HzVectorには、自己相関をWsサンプルの区間ごとに求めた周波数が返る(長さR/Wsのベクトル)。
WsHzVectorには、求めた周波数を各サンプル区間ごとにWs個ずつ複製した、長さWsのベクトルが返る。

実行例:
[y, Fs] =audioread('Ongen2/egypt_charmera.wav');
Ws=1024, R=corrWs(y, Ws);
[WsHzVector, HzVector] = findCorrHz(R, Ws, Fs);
plot(R)
hold on, plot(y)
plot(WsHzVector/1000)

黄色:元の音声信号、青色:Wsサンプルごとに区切って求めた自己相関、紫色:周波数

参考:
  findpeaks 極大値の検出
  Octave - Forge - findpeaks

Octaveレッスン(10) - 自己相関を求める

*自己相関を求める

音声の周波数を求めるには、自己相関が用いられることが多い。
自己相関のグラフに現れる極大値の周期が、音声の周波数に該当する。

Octaveでは、xcorr() 関数を使用して、自己相関を簡単に求めることができる。

以下、音声信号SをWsサンプルずつに分割して、Wsサンプルごとに自己相関を求める関数の例

---- corrWs.m ----
function r = corrWs(S,Ws)
  if(nargin == 0)
    disp("usage: r = corrWs(S, Ws)");
    return
  end

  %% if stereo, use only 1ch.
  if size(S)(1,2) > 1
    S = S(:,1);
  endif
  len = size(S)(1,1);
  r = [];
  for k = [1:Ws:len]
    if (k+Ws-1) > len
      return
    end
    s1=S(k:k+Ws-1);
    r1 = xcorr(s1, s1, 'coeff');
    r = vertcat(r, r1(1:Ws-1));
  end
endfunction
----------------

【NOTE】
・xcorr() の引数に 'coeff' オプションを指定すると正規化した値が返される。xcorr() に 'coeff' 引数を指定して自己相関を求める場合には、同じ関数同士の相互相関として、引数を指定する必要がある。
・自己相関は左右対称なので、前半のみ使用している


実行例:
[y, Fs] =audioread('Ongen2/egypt_charmera.wav');
Ws=1024, R=corrWs(y, Ws);
plot(R)
hold on, plot(y)
黄色:元の音声信号、青色:1024サンプルごとに区切って求めた自己相関


参考: xcorr 相互相関