MATLABとEXCELを使ってグラフ付きの動画を作成する方法(改良2)

MATLAB

はじめに

この記事では、時系列の画像の指定した部分にグラフを挿入して動画を作成する方法を紹介します。
このプログラムを作成した動機は、顕微鏡で撮影した蛍光画像の複数のROIを取ってそれぞれをグラフにして、蛍光画像の右下に挿入したいと考えたからです。
そうすることで蛍光変化を画像とグラフで視覚化することができます。
MATLAB R2012aからavifile関数が廃止となったためで、代わりにVideoWriter関数を使っています。

プログラムの内容

このスクリプトは、Excelファイルからデータを読み込んでグラフを作成し、時系列の画像の一部分に挿入してから動画を作成します。
Excelのマクロでも可能ですが、MATLABの方が応用が効くので作りました。
VideoWriter関数を使ったことで動画フォーマットを自由に変更できるようになっています。

もともと顕微鏡で撮影した蛍光画像のROIを取って蛍光変化をグラフにし、蛍光画像の右下に挿入することを目的としているので、時系列画像は暗いことを想定しています。
時系列画像が明るい場合はグラフを暗い色に指定した方が良いです。

なお、Mファイルの中でグラフ画像の背景が黒でない場合にグラフを描画しているので、グラフ画像の背景は黒色にする必要があります。

環境

VideoWriter関数があるR2012a以上なら動作すると思います。

  • Windows10 Pro 64bit
  • MATLAB R2021a
  • Microsoft Excel 2016 / 2019

ファイル構成

Mファイルのファイル名をfig3.mとします。
サンプルの時系列画像(img)は背景が黒色の画像で、4桁の連番のファイル名として10枚を準備しています。
連番の桁数を変更する場合は、Mファイルも修正します。
動画ファイルはMファイルと同じディレクトリに作られます。

・fig3.m MATLABのMファイルです。
・hoge.txt グラフのデータです。
・NumberFile.txt グラフのファイル番号と時系列のファイル番号を対応付けたファイル。
・graph Excelファイルから作成したグラフ図を保存します。
・img グラフを挿入する時系列画像です。
・out 時系列画像(img)にグラフを挿入した画像です。

fig3.m
hoge.txt
NumberFile.txt
hoge.mp4
graph
 ┣ -- 0000.tif
 ┣ -- 0001.tif
 ・・・
 ┣ -- 0010.tif
img
 ┣ -- 0000.tif
 ┣ -- 0001.tif
 ・・・
 ┣ -- 0010.tif
out
 ┣ -- 0000.tif
 ┣ -- 0001.tif
 ・・・
 ┣ -- 0010.tif

サンプル

fig.7z
Mファイルは入っていないので、下記からコピーして下さい。
hoge.txtはグラフのデータです。
テキストファイルなのでExcelで読み込んでxlsx形式で保存してください。

サンプルで作成したグラフ画像

Excelファイルから作成したグラフ画像の一覧です。
指定した時系列画像に挿入して動画を作成します。

サンプルで作成した動画

グラフの描画が見易いようにフレームレートを5fpsにして作成しています。

手順

  • スクリプトと同じディレクトリにout、img、graphという名前のディレクトリを作成する。
  • グラフを挿入する時系列画像(ファイル名は連番)を準備してimgディレクトリに入れる。
  • データを入力したxlsxファイルを準備する。(1列目はX軸、2列目以降はY軸でグラフの数だけ入力する。)
  • グラフのファイル番号と時系列のファイル番号を対応付けたファイルを準備する。
  • Mファイルのパラメータを適当に設定する。
  • Mファイルを実行して動画を作成する。

Mファイルのパラメータ

  • グラフと時系列画像の連番の最初の番号、及び桁数を統一する。
  • 作成したグラフ画像、出力画像を保存するディレクトリを予め作成する。
  • xlsxファイルから作成するグラフ画像はRGB画像なので、時系列画像もRGB画像にする。
  • グラフ画像のサイズは縦横の比率を綺麗にしておくと良い。
  • ラインの太さは結果を見ながら調整する。

グラフと時系列のファイル番号を対応付けたファイル

NumberFile.txtのことです。
グラフ画像のファイル番号と、時系列画像のファイル番号を対応付けたファイルを準備します。
1行1列で表すのでExcelで作ると楽です。

蛍光変化はフレームごとに変わるわけでなく、同じ状態が何フレームも続く場合があるので、時系列画像ごとにグラフ画像を指定する必要があります。
1行ずつが時系列画像(img)のファイル番号を表しているとして、グラフ画像のファイル番号(graph)を記述しています。
以下は例となります。

1
2
2
3
4
4
4
5
1行目:時系列画像の0001.tifに対して、グラフ画像の0001.tif
2行目:時系列画像の0002.tifに対して、グラフ画像の0002.tif
3行目:時系列画像の0003.tifに対して、グラフ画像の0002.tif
4行目:時系列画像の0004.tifに対して、グラフ画像の0003.tif
5行目:時系列画像の0005.tifに対して、グラフ画像の0004.tif
6行目:時系列画像の0006.tifに対して、グラフ画像の0004.tif
7行目:時系列画像の0007.tifに対して、グラフ画像の0004.tif
8行目:時系列画像の0008.tifに対して、グラフ画像の0005.tif

グラフ画像の縮小サイズ

挿入するグラフのサイズを希望サイズにする方法ですが、「paperunits」などで指定する方法はサイズ指定がうまくいかなかったので、一度ファイルに保存してから「imresize」で変更しました。

グラフ画像の縮小サイズ(pixel)は、時系列画像に挿入する部分のサイズに合わせてください。
また、グラフを表示するFigureを元にグラフ画像を作成するので、その縦横比に合わせて縮小する必要があります。

im_new = imresize(im, [GraphHeight GraphWidth], GraphMethod);

Mファイル

% 変数の削除
clear;
clear all;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% データファイル名(xlsx形式)
ExcelFile = 'hoge.xlsx';

% 時系列画像とグラフ画像の対応付け
NumberFile = 'NumberFile.txt';

% グラフのラインの太さ
Line_Width = 8;

% 縮小するグラフ画像の縦横サイズ(pixel)
GraphWidth  = 90;
GraphHeight = 60;

% 時系列画像へのグラフ画像の挿入位置(左上)(pxcel)
%InsertX = 390;
InsertX = 150;
InsertY = 140;

% グラフ画像ファイル名の開始番号(ex: 0000.tif)
% 挿入先の時系列画像ファイル名と揃える
GraphStart = 0;

% グラフ画像ファイル名の桁数(ex: 0000.tif)
% 挿入先の時系列画像ファイル名と揃える
GraphDigit = '4';

% 時系列画像ファイルの保存先ディレクトリ
ImgDir = './img/';

% 時系列画像+グラフ画像の保存ディレクトリ
% 完成した動画ファイルはxlsxファイルと同じディレクトリに保存する。
OutDir = './out/';

% グラフ画像ファイルの保存先ディレクトリ
GraphDir = './graph/';

% グラフ画像のフォーマット(拡張子)
% ex: tif/jpg/bmp/eps/ai/png
GraphFormat = 'tif';

% 時系列画像フォーマット(拡張子)
% ex: tif/jpg/bmp/eps/ai/png
ImgFormat = 'tif';

% グラフ画像を縮小する補間方法
% nearest/bilinear/bicubic
GraphMethod = 'bicubic';

% Figure(グラフ表示用)の縦横サイズ(pixel)
FigWidth  = 320;
FigHeight = 240;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Excelファイル(1シート目)の読み込み
% 1行目にはヘッダ(タイトル)を入れておく必要はない。
[xlsdata, xlsheader] = xlsread(ExcelFile,1);

% X軸データとY軸データの分離
x_data = xlsdata(:, 1);
y_data = xlsdata(:, 2:end);

% データ配列の行数
[gyo_max tmp] = size( x_data );

% Figureの表示位置、大きさ指定
set(0, 'defaultFigurePosition', [300 200 FigWidth FigHeight]);

% 軸のバックグランドカラーを黒
% 軸のラインとラベルを白
% Figureのバックグランドカラーをダークグレイ
colordef black;

% 背景色を透明に再設定する
set(0, 'defaultFigureColor', 'none');

% X軸の最大値を固定する
xmin = min( x_data );
xmax = max( x_data );

% Y軸の最大値を固定する(複数列に対応のため2回行う)
ymin = min( y_data );
ymin = min( ymin );
ymax = max( y_data );
ymax = max( ymax );

% 臨時 2010.05.13
%ymax = 330;

% 時間系列毎に分割してグラフ画像を作成する
for i = 1:gyo_max
    % 配列の分割
    x = x_data(1:i, 1:end);
    y = y_data(1:i, 1:end);

    % グラフ描画(線の太さを指定)
    plot(x, y, 'LineWidth', Line_Width);
    axis([xmin xmax ymin ymax]);

    % 保存時にFigureの背景色を保持する。
    set(gcf, 'InvertHardCopy', 'off');

    % グラフ枠の消去
    % ラベル、目盛、Backgroundの消去
    box off;
    axis off;

    % ファイル名
    digit = strcat('%0', GraphDigit, 'd');
    fname = sprintf(digit, i - 1 + GraphStart);
    fname = strcat(GraphDir, fname);

    % グラフのサイズ指定・保存(拡張子は省略)
    saveas(gcf, fname, GraphFormat);

    % 作成したグラフ画像を任意のサイズ(pixel)に縮小する
    fname  = strcat(fname, '.', GraphFormat);
    im     = imread( fname );
    im_new = imresize(im, [GraphHeight GraphWidth], GraphMethod);

    % グラフ画像保存(上書き)
    imwrite(im_new, fname, GraphFormat);

    % Figureの削除
    %close;
end

% Figureの削除
close;

% 時系列画像にグラフ画像を挿入する場所
imgname = strcat(ImgDir, '*.', ImgFormat);
imglist = dir( imgname );
w       = InsertX + GraphWidth - 1;
h       = InsertY + GraphHeight - 1;

% グラフ(RGB画像)挿入位置の検査
fname_image           = strcat(ImgDir, imglist(1).name);
im_image              = imread( fname_image );
[tmp_x, tmp_y, tmp_z] = size( im_image );
if ( tmp_x < h | tmp_y < w )
    tmp_string = sprintf('%s', 'Size is over. GraphWidth GraphHeight InsertX InsertY');
    return;
end

% Excelファイルのパスからファイル名を取り出す。
[pathstr, moviename, ext] = fileparts( ExcelFile );

% MP4
mp4name = strcat(moviename, '.mp4');
v = VideoWriter(mp4name, 'MPEG-4');

% AVI(無圧縮)
%aviname = strcat(moviename, '.avi');
%v = VideoWriter(aviname, 'Uncompressed AVI');

% 動画のオプションを指定する
v.Quality   = 100;
v.FrameRate = 5;

% VideoWriterオブジェクトを開く
open(v)

% 時系列画像とグラフ画像の対応付けファイルの読み込み
%nf = dlmread( NumberFile );
nf = readmatrix( NumberFile );

% 時系列画像にグラフ画像を挿入する・動画作成
for i = 1:size( imglist )
    % 時系列画像を読み込む
    fname_image = strcat(ImgDir, imglist(i).name);
    im_image    = imread( fname_image );

    % グラフ画像を読み込む
    digit       = strcat('%0', GraphDigit, 'd');
    fname_graph = sprintf(digit, nf(i, 1));
    fname_graph = strcat(GraphDir, fname_graph, '.', GraphFormat);
    im_graph    = imread( fname_graph );

    % 時系列画像の該当部をグラフ画像のライン部と入れ替える
    [graph_x, graph_y, graph_rgb] = size( im_graph );
    for x = 1:graph_x
        for y = 1:graph_y
            for rgb = 1:graph_rgb
                flg_change = 0;
                if im_graph(x, y, rgb) > 0
                    flg_change = 1;
                    break;
                end
            end
            % グラフ画像のpixelがゼロ(黒)以外の場合に入れ替える
            if flg_change == 1
                im_image(InsertY + x, InsertX + y, 1:end) = im_graph(x, y, 1:end);
            end
        end
    end

    % 時系列画像の保存(上書き)
    fname_new = strcat(OutDir, imglist(i).name);
    imwrite(im_image, fname_new, ImgFormat);

    % VideoWriterオブジェクトに書き込む
    writeVideo(v, im_image);
end

% VideoWriterオブジェクトを閉じる
close(v)

Comments