Pコード化

関数pcodeを使うとMatlabのスクリプト/関数Mファイルを隠蔽化することができます。 Fiffファイルの構造は、一般には公開されていないので、FiffファイルをMatlabで読み込む関数を pcode化することとしました。

hns_loadfiff4.mというファイルをpcode(hns_loadfiff4.m)としてhns_loadfiff4.pというファイルを作成しました。 ファイルは http://meg.aalip.jp/matlab/data/hns_loadfiff4.p から入手してください。

Fiff2MatFileはMatlab Compiler Runtime(MCR)が必要で、いろいろ煩雑なのですが、 hns_loadfiff4.pだと、Matlab上で直接データの読み込みができるので便利だと思います。

Contents

raw_dataで保存した自発波形(てんかん)のFIFFファイルを読む

hns_loadiff4はFiff2MatFileの元になったファイルです。 Fiff2MatFileではMEX化することで高速化していますが、 hns_loadfiff4.pは高速化してないので、処理速度が遅くなっています。 結果はFの構造体としてFiff2MatFileとほぼ同じものが出力されています。

meg='epilepsy1.fif';
tic;
F=hns_loadfiff4(meg)
toc;
F = 

          ID: [1x1 struct]
        file: 'epilepsy1.fif'
        size: 245404646
        list: [5x1357 int32]
       nlist: 1357
    ExamDate: '17-Dec-2012 13:34:22'
     subject: [1x1 struct]
    Examinee: 'case 1328'
    KindCoil: [2x336 double]
    CalXRnge: [1x337 double]
    ChInform: [12x336 double]
    ChanName: {1x336 cell}
    SSPChanl: {1x306 cell}
    PCA_0001: [306x1 double]
    PCA_0002: [306x1 double]
    PCA_0003: [306x1 double]
    PCA_0004: [306x1 double]
    PCA_0005: [306x1 double]
    PCA_0006: [306x1 double]
    PCA_0007: [306x1 double]
    PCA_0008: [306x1 double]
    PCA_0009: [306x1 double]
    PCA_0010: [306x1 double]
    bandpass: [0.1000 172.1763]
    DigitPts: [247x3 double]
    MEG2HEAD: [3x8 single]
       sfreq: 600.6150
    raw_data: [336x360600 int16]

経過時間は74.927801秒です

TSSSで32bit浮動小数点でencodeした自発波形(てんかん)のFIFFファイルを読む

TSSS処理に伴い、いろいろな変数がでています。 使い方は・・・よくわかりません。

meg='epilepsy1_tsss_float.fif';
tic;
G=hns_loadfiff4(meg)
toc;
G = 

          ID: [1x1 struct]
        file: 'epilepsy1_tsss_float.fif'
        size: 487822517
        list: [5x1308 int32]
       nlist: 1308
    ExamDate: '17-Dec-2012 13:34:22'
     subject: [1x1 struct]
    Examinee: 'case 1328'
    KindCoil: [2x336 double]
    CalXRnge: [1x337 double]
    ChInform: [12x336 double]
    ChanName: {1x336 cell}
    SSPChanl: {1x306 cell}
    bandpass: [0.1000 172.1763]
    DigitPts: [247x3 double]
    MEG2HEAD: [3x8 single]
       sfreq: 600.6150
    CrosTalk: [306x306 double]
    CTMagSns: [2x306 double]
    CTChInfo: [14x306 double]
    raw_data: [336x360600 single]

経過時間は131.281215秒です

Magnes 2500 WHの体性感覚誘発磁場のFIFFファイルを読む

Magnes 2500 WHの体性感覚誘発磁場のデータを岡山理科大学の畑中啓作先生の好意でいただきました。 148chのmagnetometer、11chのreference coilで構成されますが、reference coilの情報はFIFFに含まれていていません。 コイル半径はあてずっぽうで12mmとしました。赤丸がコイル、 青線は磁場計算用のコイル中心から(±5.75,±5.75,0)mmの点の法線成分、 黒の点はPolhemusで記録した頭皮の点です。

clear;
meg='MagnesSEF.fif';
% meg='fujii_teruaki_1-2ym-m.fif';
tic;
F=hns_loadfiff4(meg)
toc
F = 

          ID: [1x1 struct]
        file: 'MagnesSEF.fif'
        size: 650561
        list: [5x1930 int32]
       nlist: 1930
     subject: [1x1 struct]
    Examinee: 'Hiroki Nakama'
    KindCoil: [2x148 double]
    CalXRnge: [2x148 double]
    ChInform: [12x148 double]
    ChanName: {1x148 cell}
    bandpass: [0 813.8040]
    DigitPts: [1602x3 double]
    MEG2HEAD: [3x8 single]
       sfreq: 2.0345e+003
    ave_0001: [916x149 double]

経過時間は0.694556秒です

Magnes 2500 WHの体性感覚誘発磁場のFIFFファイルを表示

figure('color',[1,1,1]);
M=F.MEG2HEAD(1:3,1:4);
megch=find(F.KindCoil(1,:)==1);
P=M(:,1:3)*F.ChInform(1:3,megch)+M(:,4)*ones(1,length(megch));
X=M(:,1:3)*F.ChInform(4:6,megch);
Y=M(:,1:3)*F.ChInform(7:9,megch);
Z=M(:,1:3)*F.ChInform(10:12,megch);
r=0.012;% コイル半径を12mmとする。
seg=37;
t=linspace(0,2*pi,seg);
x=r*cos(t);
y=r*sin(t);
weight=5.75*1e-3;
for sns=1:length(megch)
    coil=P(:,sns)*ones(1,seg)+X(:,sns)*x+Y(:,sns)*y;
    plot3(coil(1,:),coil(2,:),coil(3,:),'r-');
    if sns==1;hold on;end;
    meas=P(:,sns)*ones(1,4)+weight*X(:,sns)*[1,1,-1,-1]+weight*Y(:,sns)*[1,-1,1,-1];
    z=Z(:,sns)*ones(1,4);
    quiver3(meas(1,:),meas(2,:),meas(3,:),z(1,:),z(2,:),z(3,:),'b-');
end;
D=F.DigitPts;
plot3(D(:,1),D(:,2),D(:,3),'k.');
view([-80,30]);
daspect([1,1,1]);axis tight;rotate3d on;

megch=find(F.KindCoil(1,:)==1);
P=F.ChInform(1:3,megch)';
[azimuth,elevation]=cart2sph(P(:,1),P(:,2),P(:,3));
k=1;
elevation=(1-elevation/pi*2).^k;
ChPos(:,1)=cos(azimuth).*elevation;
ChPos(:,2)=sin(azimuth).*elevation;
smp=size(F.ave_0001,1);
time=F.ave_0001(:,end);

span=find(time<-5);% 5msより前をbaselineとする
MEG=F.ave_0001(:,megch);
MEG=MEG-ones(smp,1)*mean(MEG(span,:),1);% baseline補正
xscale=5e-2;
yscale=5e-5;
X=ChPos(:,1)*ones(1,smp)+xscale*ones(size(ChPos,1),1)*linspace(-1,1,length(time));
Y=ChPos(:,2)*ones(1,smp)+yscale*MEG';
figure('color',[1,1,1]);
plot(X',Y','color',[0,0,1]);
axis tight;axis off;

t=find(time>19,1);% 19msec以上の最初のindex
seg=200;
xx=linspace(min(ChPos(:,1)),max(ChPos(:,1)),seg);
yy=linspace(min(ChPos(:,2)),max(ChPos(:,2)),seg);
[xx,yy]=meshgrid(xx,yy);
zz=griddata(ChPos(:,1),ChPos(:,2),MEG(t,:)',xx,yy);
figure('color',[1,1,1]);
step=200;
contour(xx,yy,zz,'LevelList',(fix(min(zz(:))/step)*step):step:-step,'color',[0,0,1]);hold on;
contour(xx,yy,zz,'LevelList',0,'color',[1,1,1]*0.1);
contour(xx,yy,zz,'LevelList',step:step:(fix(max(zz(:))/step)*step),'color',[1,0,0]);
plot(ChPos(:,1),ChPos(:,2),'k.');
for sns=1:size(ChPos,1)
    str=F.ChanName(megch(sns));
    str=strrep(str,'MEG ','');
    text(ChPos(sns,1),ChPos(sns,2),str);
end;
axis tight;
axis off;

横河の脳磁図データをMatlabで読むPコード

実はPコードの存在を知ったのは、横河の脳磁図データをMatlabで読むためのツールがあるのに気づいてからでした。 で、横河のPコード、横河電機のホームページに登録すると、software downloadのところから入手できるそうです。 宮崎県都城市の藤元総合病院の大坪俊昭先生のご好意で頂いたSEFのデータを使ってみます。 aveは加算データ、rawは元データですが、 conという拡張子のデータがあります。rawより時間が短い元データのようですが、よくわかりません。

clear F;
meg='SEF左1_n402.ave';
mrk='SEFマーカ左1.mrk';
mri='subject_mri_volume.mri';
F.data    =getYkgwData(meg);     % 計測データ
F.system  =getYkgwHdrSystem(meg);% 横河の脳磁計や脳磁図のデータファイルの情報
F.channel =getYkgwHdrChannel(meg);% チャンネル情報
F.acqcond =getYkgwHdrAcqCond(meg);% 計測条件など
F.event   =getYkgwHdrEvent(meg);% 加算回数やトリガーチャンネルなど
F.coregist=getYkgwHdrCoregist(mrk);% 頭皮上のマーカー座標を抽出
F.digitize=getYkgwHdrDigitize(meg);
F.subject =getYkgwHdrSubject(meg);% 患者情報など
F.bookmark=getYkgwHdrSource(meg);
F.MRI     =getYkgwMriHdr(mri);% MRIのヘッダー情報の抽出
F.version =getYkgwVersion(meg);

F
F.acqcond % 計測条件
F.channel.channel(1).data % ch1の情報

clear G;
con='SEF左1.con';
G.data =getYkgwData(con);
G.acqcond=getYkgwHdrAcqCond(con);
G.channel=getYkgwHdrChannel(con);
G.event=getYkgwHdrEvent(con);
G
G.acqcond
G.channel.channel(1).data

clear H;
raw='SEF左1のエポック.raw';
tic;
H.data=getYkgwData(raw);
toc;
H.acqcond=getYkgwHdrAcqCond(raw);
H.channel=getYkgwHdrChannel(raw);
H.event=getYkgwHdrEvent(raw);
H
H.acqcond
H.channel.channel(1).data
F = 

        data: [224x2000 double]
      system: [1x1 struct]
     channel: [1x1 struct]
     acqcond: [1x1 struct]
       event: [1x1 struct]
    coregist: [1x1 struct]
    digitize: [1x1 struct]
     subject: [1x1 struct]
    bookmark: []
         MRI: [1x1 struct]
     version: [1x1 struct]


ans = 

                   acq_type: 2
                sample_rate: 2000
               frame_length: 2000
          pretrigger_length: 1000
              average_count: 402
    specified_average_count: 450
              multi_trigger: [1x1 struct]


ans = 

           x: 0.1162
           y: 0.0225
           z: 0.0433
        zdir: 70.9840
        xdir: 15.3399
    baseline: 0.0500
        size: 0.0155
        name: ''


G = 

       data: [224x820000 double]
    acqcond: [1x1 struct]
    channel: [1x1 struct]
      event: []


ans = 

                  acq_type: 1
               sample_rate: 2000
              sample_count: 820000
    specified_sample_count: 820000


ans = 

           x: 0.1162
           y: 0.0225
           z: 0.0433
        zdir: 70.9840
        xdir: 15.3399
    baseline: 0.0500
        size: 0.0155
        name: ''

経過時間は20.017364秒です

H = 

       data: [224x804000 double]
    acqcond: [1x1 struct]
    channel: [1x1 struct]
      event: [1x402 struct]


ans = 

                   acq_type: 3
                sample_rate: 2000
               frame_length: 2000
          pretrigger_length: 1000
              average_count: 402
    specified_average_count: 450
              multi_trigger: [1x1 struct]


ans = 

           x: 0.1162
           y: 0.0225
           z: 0.0433
        zdir: 70.9840
        xdir: 15.3399
    baseline: 0.0500
        size: 0.0155
        name: ''

横河電機社製脳磁計 PQ1160Cの体性感覚誘発磁場とPQ1400Rの聴覚誘発磁場ファイルを表示

変数をなるべくhns_loadfiffのものになるように書き換えています。 横河電機には脳磁図番号がないみたいなので適当につけてます。 等磁場線図はaxial gradiometerの成分のみを用いています。

for nmeg=1:2
    clear F;
    switch nmeg
        case 1;meg='SEF左1_n402.ave';
        case 2;meg='A008a_BC_0.5HPF_100LPF_trig437_label4.ave';
    end;
    F.channel =getYkgwHdrChannel(meg);
    F.system  =getYkgwHdrSystem(meg);
    F.ave_0001=getYkgwData(meg)';% 行・列をMatlab仕様に
    F.acqcond =getYkgwHdrAcqCond(meg);
    sfreq=F.acqcond.sample_rate;
    time=((1:size(F.ave_0001,1))-F.acqcond.pretrigger_length)/sfreq*1000;% msec
    F.ave_0001(:,end+1)=time(:);
    nch=F.channel.channel_count;
    F.KindCoil=zeros(1,nch);
    F.ChInform=zeros(9,nch);
    str=F.system.model_name;
    megno=1;
    nullno=1;
    refno=1;
    stimno=1;
    eegno=1;
    for n=1:nch
        sns=F.channel.channel(n).type;
        F.KindCoil(n)=sns;
        ch=F.channel.channel(n).data;
        if ismember(sns,[1,2,3])% 1 magnetometer 2 axial 3 planar gradiometer
            F.ChInform([1:3,9],n)=[ch.x,ch.y,ch.z,ch.size];
            F.ChanName{n}=sprintf('MEG %03.0f',megno);megno=megno+1;
        end;
        switch sns
            case 2;
                F.ChInform(4:8,n)=[ch.zdir,ch.xdir,ch.zdir,ch.xdir,ch.baseline];
            case 3;
                F.ChInform(4:8,n)=[ch.zdir1,ch.xdir1,ch.zdir2,ch.xdir2,ch.baseline];
            case 0;
                F.ChanName{n}=sprintf('NULL %03.0f',nullno);nullno=nullno+1;
            case {257,258,259}
                F.ChanName{n}=sprintf('REF %03.0f',refno);refno=refno+1;
            case -1;
                F.ChanName{n}=sprintf('STI %03.0f',stimno);stimno=stimno+1;
            case {-2,-3,-4}
                name=F.channel.channel(n).data.name;
                F.ChanName{n}=[sprintf('EEG %03.0f ',eegno),name];
                eegno=eegno+1;
        end;
    end;
    megch=find(F.KindCoil>0 & F.KindCoil<4);
    axch=find(F.KindCoil(megch)==2);
    plch=find(F.KindCoil(megch)==3);
    P0=F.ChInform(1:3,megch);
    phi=F.ChInform(6,megch)/180*pi;
    theta=F.ChInform(7,megch)/180*pi;
    Z=[cos(theta).*sin(phi);sin(theta).*sin(phi);cos(phi)];
    P1=P0+(ones(3,1)*F.ChInform(8,megch)).*Z;% baseline
    phi=F.ChInform(4,megch)/180*pi;
    theta=F.ChInform(5,megch)/180*pi;
    Z=[cos(theta).*sin(phi);sin(theta).*sin(phi);cos(phi)];
    X=[cos(theta).*cos(phi);sin(theta).*cos(phi);-sin(phi)];
    Y=[-sin(theta);cos(theta);0*phi];
    % Neuromagと同じ座標系にする
    P0(1:2,:)=[-P0(2,:);P0(1,:)];
    P1(1:2,:)=[-P1(2,:);P1(1,:)];
    Z(1:2,:)=[-Z(2,:);Z(1,:)];
    X(1:2,:)=[-X(2,:);X(1,:)];
    Y(1:2,:)=[-Y(2,:);Y(1,:)];
    seg=37;
    t=linspace(0,2*pi,seg);
    figure('color',[1,1,1]);
    quiver3(P0(1,:),P0(2,:),P0(3,:),Z(1,:),Z(2,:),Z(3,:),'r-');hold on;
    quiver3(P1(1,:),P1(2,:),P1(3,:),Z(1,:),Z(2,:),Z(3,:),'r-');
    for sns=1:length(megch)
        r=F.ChInform(9,megch(sns))/2;% コイルの内径/2
        x=r*cos(t);
        y=r*sin(t);
        if F.KindCoil(megch(sns))==2;color=[0,0,1];else color=[0,0.5,0];end;
        for nn=0:1
            switch nn
                case 0;P=P0;
                case 1;P=P1;
            end;
            coil=P(:,sns)*ones(1,seg)+X(:,sns)*x+Y(:,sns)*y;
            plot3(coil(1,:),coil(2,:),coil(3,:),'color',color);
        end;
    end;
    axis tight;daspect([1,1,1]);view([-80,20]);rotate3d on;
    title(str);

    [azimuth,elevation]=cart2sph(P1(1,:),P1(2,:),P1(3,:));% 少し離れてる方がch分離しやすい
    k=1;
    elevation=(1-elevation/pi*2).^k;
    clear ChPos;
    ChPos(:,1)=cos(azimuth).*elevation;
    ChPos(:,2)=sin(azimuth).*elevation;
    smp=size(F.ave_0001,1);
    time=F.ave_0001(:,end);
    span=find(time<-5);% 5msより前をbaselineとする
    MEG=F.ave_0001(:,megch)*1e+15;% fTに
    MEG=MEG-ones(smp,1)*mean(MEG(span,:),1);% baseline補正
    xscale=5e-2;
    yscale=2e-4;
    X=ChPos(:,1)*ones(1,smp)+xscale*ones(size(ChPos,1),1)*linspace(-1,1,length(time));
    Y=ChPos(:,2)*ones(1,smp)+yscale*MEG';
    figure('color',[1,1,1]);
    plot(X(axch,:)',Y(axch,:)','color',[0,0,1]);hold on;
    if ~isempty(plch)
        plot(X(plch,:)',Y(plch,:)','color',[0,0.5,0]);
    end;
    axis tight;axis off;
    title(str);

    if nmeg==1
        t=find(time>18,1);% 19msec以上の最初のindex
    else
        t=find(time>100,1);% 100msec以上の最初のindex
    end;
    seg=200;
    xx=linspace(min(ChPos(axch,1)),max(ChPos(axch,1)),seg);
    yy=linspace(min(ChPos(axch,2)),max(ChPos(axch,2)),seg);
    [xx,yy]=meshgrid(xx,yy);
    zz=griddata(ChPos(axch,1),ChPos(axch,2),MEG(t,axch)',xx,yy);
    figure('color',[1,1,1]);
    if nmeg==1;step=10;else step=25;end;
    contour(xx,yy,zz,'LevelList',(fix(min(zz(:))/step)*step):step:-step,'color',[0,0,1]);hold on;
    contour(xx,yy,zz,'LevelList',0,'color',[1,1,1]*0.1);
    contour(xx,yy,zz,'LevelList',step:step:(fix(max(zz(:))/step)*step),'color',[1,0,0]);
    plot(ChPos(:,1),ChPos(:,2),'k.');
    for sns=1:size(ChPos(axch,:),1)
        str=F.ChanName(megch(axch(sns)));
        str=strrep(str,'MEG ','');
        text(ChPos(axch(sns),1),ChPos(axch(sns),2),str);
    end;
    axis tight;
    axis off;
    % title([str,sprintf(' %d fT/step',step)]);
end;