MATLAB(Simulink)でアニメーションGIF
前回の記事、折り返し雑音とは?~なぜ車のホイールは逆回転するのか~デジタルはアナログの近似ではない で、ホイールのアニメーションとグラフのアニメーションを合体させて、一つのアニメーションGIFファイルとして表示しました。
今回は、その作り方を説明したいと思います。
基本は、フレーム毎の静止画を作って、それを連続的に書き込むだけです。
全体的なモデルはこちら。
まずはSimulinkモデルの設定。
モデル上で、右クリック->「モデル コンフィギュレーション パラメータ」(または Ctrl+E)として、以下のように設定します。
ステップサイズは、もっと遅い1/10とかでも良いかもしれません。
Digital Clock のサンプル時間も同じに設定します。
これで、Digital Clock の出力は 1/30sごとに進むことになります。
その後の乗数は、見た目がちょうど良いスピードになるように適当に決めます。
一応、加速してるっぽくするため二乗してみました。
Compositing ブロックは、2つの画像を重ね合わせます。
ここでは以下のように設定し、Image1 の 一部を Image2 で置き換えます。
Image1(wheel3.png)の入力はこれです。
MATLABは、イメージ系はuint8、それ以外はshort/doubleとかなので、後段のために読み込みブロックでdoubleにしておきます。
これに、回転するスポークを重ね合わせます。
スポークの元画像はこれ。
上のモデルでは以下の行列で指定しています。
repmat([ones(5,124) ; repmat([ones(1,60) 0 0 0 0 ones(1,60)], 64-5,1) ; ones(62,124)],1,1,3)
4本版も追加してみました。
repmat([ones(5,124) ; repmat([ones(1,60) 0 0 0 0 ones(1,60)], 62-7,1); repmat([1 1 1 1 1 zeros(1,124-10) 1 1 1 1 1], 4, 1); repmat([ones(1,60) 0 0 0 0 ones(1,60)], 62-7,1); ones(5,124)],1,1,3)
画像で用意した方が早いかもしれませんね。(¬_¬)
これを、Rotate ブロックで回転させます。
デフォルト設定のまま回転させると以下のようになってしまうため、
出力サイズ:入力イメージと同じ
背景の塗りつぶしの値:1
とします。
Angle 入力はラジアンです。MATLAB関数の rotate はなぜか「角度」なので注意しましょう。時計回りに回転させたかったため、-piを掛けて入力しています。
Mask は、回転したホイール画像そのものから、黒部分だけを使います。
元画像は白と黒のみですが、回転させると中間階調が発生するため、≦ 0.5 とします。
saveGIF (MATLAB Function) ブロックの中身はこちら。
function G = saveGIF(u,t,speed,WE)
persistent filename h we % 永続変数として保持
% コード生成に対応していない関数を外部関数として定義
coder.extrinsic('imwrite', 'rgb2ind', 'getframe', 'frame2im', 'animatedline', 'addpoints')
coder.extrinsic('disableDefaultInteractivity', 'setfield', 'colororder')
u = im2uint8(u); % double -> uint8 変換
G = [u u u]; % 変数サイズ定義
COrder = zeros(7,3); % カラーオーダー用サイズ定義
if isempty(filename) % 初回設定
we = WE;
filename = 'wheel_out.gif';
figure(1);
clf
COrder = colororder; % デフォルトカラー取得
h = animatedline('Linewidth',2,'Color',COrder(1,:)); % アニメーションライン作成
% メニュー表示・割り込み停止
ax = gca;
ax = setfield(ax,'Toolbar','Visible','off');
disableDefaultInteractivity(ax)
axis([0,10,0,200])
ylabel('Speed','FontSize',30)
xlabel('Time','FontSize',30)
% アニメーションラインにポイント追加&表示
addpoints(h,t,0);
drawnow
frame = getframe(gcf); % フレーム画取得
gr = frame2im(frame); % RGB画像へ変換
grs = imresize(gr,[124 248]); % ホイール画像とのサイズ合わせ
G = [u grs]; % ホイール画像と連結
[A,map] = rgb2ind(G,256); % 書き込み用インデックスイメージに変換
if we; imwrite(A,map,filename,'gif','LoopCount',Inf,'DelayTime',1/30); end % 30フレーム/s
else % 初回以外
addpoints(h,t,speed);
drawnow
frame = getframe(gcf);
gr = frame2im(frame);
grs = imresize(gr,[124 248]);
G = [u grs];
[A,map] = rgb2ind(G,256);
if we; imwrite(A,map,filename,'gif','WriteMode','append','DelayTime',1/30); end
end
最初の部分は、MATLAB Functionブロックを使おう!~ Running σ、Simulinkで高速bar描画 でも解説しましたが、初回設定とそれ以外を別けるために永続変数を用い、コード生成に対応していない関数を外部関数として定義します。
上にも書きましたが、イメージ系はuint8(0~255)、それ以外はdouble(0~1)なので、スケーリングや丸めを行って変換する必要があります。それ用の関数( im2uint8 )が用意されているのでそれを使っています。
G = ~ の部分は、MATLAB Functionのようなコード生成部分では配列の大きさを明示する必要があるのでそのためです。
また、マウスをグラフの上に持って行ったときにツールバーが表示されてそれも書き込まれてしまうため、グラフの座標軸ツールバー表示を消します。
ax = gca;
ax = setfield(ax,'Toolbar','Visible','off');
コード生成に対応していない関数の構造体指定は MATLAB Function 内ではできないので setfield を使います。(通常であれば、ax.Toolbar.Visible = 'off'; が使えます)
GIF書き込みでエラーが出ないよう、割り込みも禁止にします。
disableDefaultInteractivity(ax)
imwrite の最後のパラメータ、'DelayTime' は、各フレームの表示時間です。これで、30フレーム/s の アニメーションGIFファイルになります。
4本版の出力結果はこちら。
このように、素材さえ用意すればあとは他のツールは不要で、MATLABで完結します。
プレゼンやWeb素材を作るのにも便利ですので、使ってみてはいかがでしょうか?
では。
追補
・Wheel View ウィンドウが小さいためにマウスでクリックしても移動できません。
MATLABアイコンを右クリック -> 移動 -> カーソルキー
で、マウス移動ができるようになります。
・書き込み実行中は、getframe する Figure 1 を操作しないでください。
書き込みエラーになることがあります。