MATLABとEXCELを使って矢印付きの動画を作成する方法(改良1)

MATLAB

はじめに

この記事では、画像の指定した部分にグラフを挿入した時系列の画像に、さらに矢印を挿入して動画を作成する方法を紹介します。
このプログラムを作成した動機は、顕微鏡で撮影した蛍光画像の複数のROIを取ってそれぞれをグラフにして、蛍光画像の右下に挿入したいと考えたからです。
そうすることで蛍光変化を画像とグラフで視覚化することができます。
ただ、各グラフが指す蛍光画像の位置が分からないと意味がないので矢印を挿入するスクリプトを修正しました。

プログラムの内容

この記事でグラフを挿入した一時ファイルを使って矢印を挿入するスクリプトです。
MathWorksで公開されている矢印を作る関数には色々オプションがありますが、矢印は横向きに固定します。
斜めの矢印が必要な場合は、arrow関数のパラメータで指定します。
矢印の形は描画するfigureの縦横サイズ、縦横比にも依存するので、お好みの矢印を作るには試行錯誤が必要かもしれません。

環境

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

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

ファイル構成

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

・arror2.m MATLABのMファイルです。
・position.txt 矢印を描く座標データです。
・NumberFile.txt 時系列のファイル番号と矢印を対応付けたファイル。
・img-arrow 矢印の図を保存します。
・img 矢印を挿入する時系列画像です。
・out 時系列画像(img)に矢印を挿入した画像です。

arror2.m
hoge.txt
NumberFile.txt
position.txt
img-arrow
 ┣ -- 0000.tif
 ┣ -- 0001.tif
 ┣ -- 0002.tif
 ┣ -- 0003.tif
img
 ┣ -- 0000.tif
 ┣ -- 0001.tif
 ・・・
 ┣ -- 0010.tif
out
 ┣ -- 0000.tif
 ┣ -- 0001.tif
 ・・・
 ┣ -- 0010.tif

arrow.mのインストール

MATLABで「arrow.m」を置いたディレクトリにパスを通しても良いのですが、日常的に使うので以下のディレクトリにコピーしました。
「R2021a」はインストールしたMATLABのバージョンによって適宜、読み替えてください

arrow
Draw a line with an arrowhead.

サンプル

figarrow
Mファイルは入っていないので、下記からコピーして下さい。
NumberFile.txtとposition.txtはテキストファイルなのでExcelで読み込んでxlsx形式で保存してください。

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

手順

  • グラフを挿入した時系列画像(img)を準備する。(ファイル名は連番)
  • 矢印とグラフの色を合わせたい場合は、予めグラフの色を調べておく。
  • 矢印のファイルと時系列のファイル番号を対応付けたxlsxファイルを準備する。
  • 矢印を挿入する左上の座標と色を記述したxlsxファイルを準備する。
  • Mファイルのパラメータを適当に設定する。
  • Mファイルを実行して動画を作成する。

Mファイルのパラメータ

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

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

NumberFile.txtのことです。
1行ずつが時系列画像のファイル番号を表しています。
以下は1枚の画像に4個ずつの矢印を挿入する場合です。
矢印を挿入しない場合は空行にします。

0 1 2 3
0 1 2 3
0 1 2 3
0 1 2 3

矢印の左上の座標と色を記述したxlsxファイル

position.txtのことで、矢印のパラメータを記述します。
1行目から順番に矢印番号「0」、「1」・・・を表し、左の列から「左上のX座標」、「左上のY座標」、「色」を表します。
また、MATLABで決められている表記に従って各グラフの色と合わせます。

110	13	y
13	151	m
50	54	r
80	90	c
301 Moved Permanently

スクリプト

% 変数の削除
clear;
clear all;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 時系列画像と矢印画像の対応付け
% 「PositionFile」と「ArrowStart」変数に揃える。
% 1番目の行が時系列画像の1番目に対応する。
% 表示する矢印番号を列挙する。
% 矢印を挿入しない場合は、その行には何も記入しない。
NumberFile = 'NumberFile.xlsx';

% 縮小する矢印画像の縦横サイズ(pixel)
ArrowWidth  = 40;
ArrowHeight = 30;

% 矢印画像の挿入位置(左上)(pxcel)
% X座標
% Y座標
% 矢印の色(y/m/c/r/g/b/w/k)
PositionFile = 'position.xlsx';

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

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

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

% 時系列画像+矢印画像の保存ディレクトリ
% 動画ファイルはXLSxファイルと同じディレクトリに保存する。
OutDir = './out/';

% 矢印画像ファイルの保存先ディレクトリ
ArrowDir = './img-arrow/';

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

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

% 矢印画像を縮小する補間方法
% nearest/bilinear/bicubic
ArrowMethod = 'bicubic';

% Figure(矢印表示用)の縦横サイズ(pixel)
% 矢印の画像の形に影響する。
FigWidth  = 400;
FigHeight = 300;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

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

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

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

% 矢印ファイルの読み込み
% 「MATLAB R2012a」では「xlsheader」で引数は不要。
[xlsdata, xlsheader] = xlsread(PositionFile,1);
arrow_x = xlsdata(:, 1);
arrow_y = xlsdata(:, 2);
arrow_c = xlsheader;
%arrow_c = xlsheader(:, 3);

% 行数
arrow_num = size( arrow_x );

% 矢印画像を作成する
for i = 1:arrow_num(1)
    % 矢印の色
    c = arrow_c(i, 1);
    if strcmp(c, 'y') c = [1 1 0]; end
    if strcmp(c, 'm') c = [1 0 1]; end
    if strcmp(c, 'c') c = [0 1 1]; end
    if strcmp(c, 'r') c = [1 0 0]; end
    if strcmp(c, 'g') c = [0 1 0]; end
    if strcmp(c, 'b') c = [0 0 1]; end
    if strcmp(c, 'w') c = [1 1 1]; end
    if strcmp(c, 'k') c = [0 0 0]; end

    % 保存時にFigureの背景色を保持する。
    % 矢印枠の消去
    % ラベル、目盛、Backgroundの消去
    set(gcf, 'InvertHardCopy', 'off');
    box off;
    axis off;

    % ファイル名
    digit = strcat('%0', ArrowDigit, 'd');
    fname = sprintf(digit, i - 1 + ArrowStart);
    fname = strcat(ArrowDir, fname);

    % 矢印の描画
    a = arrow([100,50], [0,50], 'end', 'start');
    arrow(a, 'FaceColor', c, 'EdgeColor', c, 'tipangle', 20, 'LineWidth', 16, 'Width', 16, 'Length', 180);

    % 矢印のサイズ指定・保存(拡張子は省略)
    saveas(gcf, fname, ArrowFormat);

    % 作成した矢印画像を任意のサイズ(pixel)に縮小する
    fname  = strcat(fname, '.', ArrowFormat);
    im     = imread( fname );
    im_new = imresize(im, [ArrowHeight ArrowWidth], ArrowMethod);

    % 矢印画像保存(上書き)
    imwrite(im_new, fname, ArrowFormat);

    % Figureの削除
    close;
end

% 時系列画像と矢印画像の対応付けファイルの読み込み
[xlsdata, xlsheader] = xlsread(NumberFile,1);

% 時系列画像ファイルのリスト
imgname = strcat(ImgDir, '*.', ImgFormat);
imglist = dir( imgname );

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

% 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)

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

    arrowlist = xlsdata(i, 1:end);
    arrowsize = size( arrowlist ); % (1, 2)が列数

    % 空行の場合は矢印を挿入しない)
    if isnan( arrowlist(1, 1) ) == 0
        for j = 1:arrowsize(1, 2)
            % 矢印画像を読み込む
            digit       = strcat('%0', ArrowDigit, 'd');
            fname_arrow = sprintf(digit, arrowlist(1, j));
            fname_arrow = strcat(ArrowDir, fname_arrow, '.', ArrowFormat);
            im_arrow    = imread( fname_arrow );

            % 時系列画像の該当部を矢印画像と入れ替える
            [ar_x, ar_y, ar_rgb] = size( im_arrow );
            for x = 1:ar_x
                for y = 1:ar_y
                    for rgb = 1:ar_rgb
                        flg_change = 0;
                        if im_arrow(x, y, rgb) > 0
                            flg_change = 1;
                            break;
                        end
                    end
 
                    % 矢印画像のBackgroundがゼロ(黒)以外の場合に入れ替える
                    if flg_change == 1
                        im_image(arrow_y(j, 1) + x, arrow_x(j, 1) + y, 1:end) = im_arrow(x, y, 1:end);
                    end
                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