|  | 
 
| 
x
EDA365欢迎您登录!您需要 登录 才可以下载或查看,没有帐号?注册  * D/ _' W! A- e0 i0 b内容摘要:本博文介绍MATLAB图片切换动画效果的制作以及GIF文件保存,并结合具体代码详细解释。介绍了利用MATLAB编程进行几幅图片的轮流切换,切换时实现与幻灯片切换相似的炫酷的图片切换特效。其中涉及一些MATLAB的高级图形设计,具体的函数及命令文中会详细介绍。本文先逐步介绍实现思路及代码细节,最后附上完整代码。/ N7 v3 }5 h3 h8 J$ q. B( q
 " b" y6 r6 s  M0 r2 }1 [' c
 1. 前言
 ' [. a* N" X/ r$ s* F9 \       MATLAB中为数据可视化提供了全面系统的功能函数,据此可以绘制出许多形象生动的精美图像。同时MATLAB的句柄图形为用户提供了强有力的工具,可以很方便地对图形的每个可能方面进行控制,这在需要设计GUI的场合用处很大。刚接触时看到许多用MATLAB制作出炫丽的仿真动画不明觉厉,在大学的MATLAB编程课里也经常有编写一个图片切换动画的作业或课设。这里写一个有几幅图片轮流切换不同效果的程序,为此提供一个参考思路,希望对大家有所启发。
 - u; M( d8 ^! B, B2 y  g! W4 Y+ U" Y8 @
 % l' s* D( x* i9 W2. MATLAB动画制作! L, R  v5 R  f; u
 本节介绍MATLAB中动画制作的常用方式,对原理比较熟悉的读者可直接跳至第3节。众所周知动画其实是很多张图片的以一定时间间隔的逐帧切换,一个简单直接的想法当然就是下面代码中方式了。首先读取一张图片,通过for循环逐步扩大要显示图片的索引范围,达到逐步放大图片的效果。
 ' B  c. i" ]2 x! j8 F# [" `! V% x9 }7 t- _) N: }
 I=imread('image.jpg');%读图; t8 Y- W( e" r
 [x,y,z]=size(I);%尺寸
 ; F9 ^1 s+ L9 H& W9 iwhile 1* q1 M" q7 K: E0 V$ R6 y
 for i=1:1003 y0 z& D' M% F9 R; ]/ F# ^/ q
 imshow(I(1:x/100*i,1:y/100*i,:));%逐渐放大显示
 " H$ [3 M8 r  b' {. Send& L; G- B/ V( y7 H! S, u
 end% u. L' J7 ^  j9 ^7 z
 1 P; x0 F, y, |" H5 I
 
 ![]() ! `7 G7 O4 ~; z" M! Y) |! G: [ 图2.1 显示效果图2 c/ B6 X" Y0 F7 M1 @
 
 . S  {  e9 B" U" V       上述代码有点粗糙,运行过程中随着图片的放大窗口也在不断抖动。这其实跟imshow( )函数有关,每次调用它显示一张图片系统就会自动新创建一个窗口,窗口的大小是根据要显示图片的大小自动缩放的,上面的for循环中不断调用imshow( )而图片的大小是不断变大的,显示窗口自然也跟着变化。
 * m* m; t- B$ g" i3 T
 2 }6 ?( S. p# ^2 G* z% C8 N# V( }       其实MATLAB为动画制作提供了三种实现方式:质点动画、电影动画、程序动画。质点动画是最简单的动画产生方式,产生一个顺着曲线轨迹运动的质点来操作;电影动画首先会保存一系列的图形数据,然后按照一定的顺序像电影一样的播放;程序动画是在图形窗口中按照一定的算法连续擦除和重绘图形对象。下面逐个简单演示一下* x7 n9 O% Z* Z1 s+ ?' L
 
 9 u  ^: F/ D7 {' d2 H+ |2.1 质点动画
 % Q/ I( Q! A' X1 G) w- h       质点动画由comet、comet3函数产生质点动画,分别对应二维和三维坐标下的质点。首先求解出质点完整的运动轨迹坐标x,y(三维时还有z),将x,y作为输入参数使用comet或comet3直接绘制动点。7 d* U2 H8 W# o4 A
 1 M5 A+ A, x4 n  g- r
 
 Y6 @# i" j4 W1 @# a| 调用格式 | 调用说明 |  | comet(y) | 显示质点绕着向量y的动画轨迹运动(二维) |  | comet(x,y) | 显示质点在横轴、纵轴方向的运动随向量x,y的动画轨迹(二维) |  | comet(x,y,p) | 效果与上一个相同,额外定义轨迹尾巴线的长度p*length(y),p介于0,1之间,默认为0.1 | 
 comet3函数的使用方法与comet相似。
 - F/ S- A: C4 |/ h2 \
 + W6 O( _+ ^% w: S; {4 H  d实例代码如下:
 8 O8 k1 h% G( V( l* I4 I* u
 & O( n' E4 j0 E/ {- m0 s* G6 mclf;
 8 g' e5 q, m2 K% M% J7 a: N8 Jclear;
 u' R* y9 S0 C  l: U- R3 p6 N0 dgrid on;
 8 z0 B7 ]1 V5 q/ d3 O
 + c7 s6 `  C# ?% z1 r7 Uvx = 100*cos(1/4*pi);
 ( L0 V/ B0 F5 `4 o9 m. ivy = 100*sin(1/4*pi);
 " |9 M! c' ~( V$ ?" y: }6 jt = 0:0.02:15;
 6 R- n: Q! @- S4 Sdx = vx*t;  ! t9 T3 e' y9 f
 dy = vy*t-9.8*t.^2/2;  4 Z/ V' d0 h1 r9 E0 I9 N8 Q; G
 comet(dx, dy);0 C! r5 ?$ v: k& ~9 \; J+ f, r$ d
 ; l2 z) C' E) o' k- Z; o
 效果如图2.1.1所示% p3 W+ O- [! W; p6 S
 
 4 f& ]% J8 D3 g) v
 ![]() I5 Q3 V% ]$ D3 z; }1 u: c8 j( f* A+ ^/ H
 图2.1.1 质点动画效果图9 X, i9 h. R6 E1 u8 x; \8 Q
 
 ; C* Q: b7 E3 Z! k. `2 o2.2 电影动画
 ! [% _- p( X; @       电影动画和电影的制作有点相似,实际可以看出是一个先“拍”再“播”的过程,即捕捉将要构成动画帧的图像逐个存到一个大矩阵中,然后播放这个大矩阵的数据。
 , i, N+ q9 F" g
 1 O" G$ f! b; v3 {: M4 m1 }基本步骤:
 " |8 K5 M- @8 @# Y3 T  1、调用moviein函数初始化内存,创建一个足够大的矩阵,用于存储构成每一帧图像的数据。
 + z. L0 d2 X+ u/ x  2、利用getframe抓取当前画面(即每帧图像),返回的数据用于构成动画矩阵。
 4 _8 j7 j$ Y6 e6 b% x. R5 o  3、调用movie函数按照指定的速度进行指定次数播放该电影动画。例如:movie(M, n)可以播放由矩阵M所定义的画面n次,默认只播放一次。
 ) m" f: l: c+ S0 ?/ k: y
 ; u# z# ?) x  e, wgetframe与movie函数调用格式见表2.2.1及表2.2.2:getframe函数可以捕捉动画帧,并保存到矩阵中。
 9 \* @! z1 z$ X+ n8 E: t* u+ r
 ' O  E! R6 w. Q- u$ w4 A表2.2.1 getframe函数用法
 调用格式 调用说明 % H; q* e, H  x- ]/ ^- kf=getframe从当前图形框中得到动画帧/ n6 e  b& ~2 W* {, ~
 f = getframe(h)从图形句柄h中得到动画帧* y  R* F4 T0 J- h8 M) h' b6 B
 f=getframe(h,rect)从图形句柄h的指定区域rect中得到动画帧
 / t" L$ r* I7 }  U9 O5 w
 当创建了一系列动画帧后,可利用movie函数播放这些动画帧。该函数的用法有:表2.2.2 movie函数用法 调用格式 调用说明/ r& _9 c) V* | movie(M)将矩阵M中的动画帧播放一次
 : T1 d8 L# L6 c3 w% A) Qmovie(M, n)将矩阵M中的动画帧播放n次
 ; K, V" z1 R& j/ Vmovie(M,n,fps)将矩阵M中的动画帧以每秒fps帧的速度播放n次9 a% M7 H& |# J; d  t( j0 h4 ^
 ( S9 M6 ~' c4 t1 q- U1 W
 5 J* r# Z8 ^  ^9 u8 N
 实例:旋转的山峰动画. z" U8 t8 [" E; p  w3 q
 
 ; K* P3 J1 q' Zclc; clear;
 * C, m2 ~" B  @$ Q   9 a. |+ t- |1 }0 n* C) J& \1 f
 % peaks是一个函数,其中有2个变量。由平移和放缩高斯分布函数获得。
 $ |0 i: [" |" B+ f) R( O2 o% 参数为30,得到的X、Y、Z为30*30的矩阵。  0 d* c; q/ N# }* O9 a* j6 t3 j
 [X, Y, Z] = peaks(30);  * D" I" X% O; d: h1 G7 T
 % suRF绘制三维曲面图
 9 j2 z5 ?6 y! h3 Asurf(X,Y,Z);  2 S* o- R5 q/ L3 W
 
 4 W/ A; P; L% j; F9 y8 Laxis([-3,3,-3,3,-10,10]);
 `3 w# u7 f5 F2 I. q. s% 关闭所用坐标轴上的标记、格栅和单位标记。但保留由text和gtext设置的对象  " k% N3 @) u+ w& R- G
 axis off;
 $ @" a1 G% @0 ^, O" M+ \* ishading interp;
 # f7 H- W7 c. D3 V9 d! z$ fcolormap(hot);
 2 l( x5 C" Z( z( j6 D, m- ]
 , W4 E3 d$ J$ |) o: |M = moviein(20);% 建立一个20列的大矩阵
 8 o* R' N* z3 Y3 ?. Y- \for i = 1:20
 : h/ w. [4 B7 ^$ w" c4 z0 K   view(-37.5+24*(i-1),30);% 改变视点     2 d' s& f2 R+ @
 M(i) = getframe;% 将图形保存到M矩阵   9 V) z3 N% `! C+ U- |5 Y8 _
 end
 o- Y* D7 z. l" i. B+ h9 O& w: ~4 w# t9 }
 movie(M,2);% 播放画面2次/ q: S6 v: |& y
 
 . y7 e1 n6 S. P9 |
 7 z/ ?5 C2 x& X, N. t: @运行结果如图2.2.1所示
 0 Q* {& y7 [8 j- S& h8 W1 t5 r* Y" {; |* ^/ {
 
   / e, q/ e5 o8 u8 W+ g. U" [6 z8 {4 l: x1 O4 O
 图2.2.1 电影动画实例6 R& w6 R" Y0 h3 K
 
 & K. Q) ^" S6 N, b' q2.3 程序动画
 9 }+ F' v5 w" O3 t, A- f- M' P* b- ]! w3 {" m' A* |
 在MATLAB中把用于数据可视和界面制作的基本绘图要素称为句柄图形对象,每个图形对象有相应的属性值,例如线条Line对象就有颜色、位置等属性。可以改变图形对象的属性值,重绘图形对象,从而创建程序动画。其基本思路是:首先新建一个图形窗口,再循环内逐渐改变图形对象的相应属性值,并使用drawnow函数更新当前图形,整个循环就会表现出变化的动画效果。" \$ j  W/ \; p8 W; R+ e0 r
 $ ~  p- Y  G% V7 V) l
 实例代码如下
 ; q/ w6 }/ {* a' `" }# h! }6 S( z  Q; y) r1 u" f
 clear;
 " n1 F0 C# L8 e# Wclc;
 : {" V  a9 O* g8 O% m7 Z, c* b- M%% 新建图形窗口并设置初始属性
 + J1 s4 J" M& L; \/ u/ o  WhFigure=figure('menubar','none','NumberTitle','off','position',...
 6 }/ }- x$ w0 N' v- n     [800 800 360 360],'name','图片切换动画效果');
 # B, ~4 l1 A" p9 i8 { movegui(hFigure,'center');%设置居中
 - n2 }8 V8 P* |% P* m% I( |& q %设置坐标轴属性
 ; N) A) B$ {0 `5 i. X hAxes=axes('Visible','off','units','normalized','position',[0 0 1 1]);
 8 l0 p1 x# D# H
 ! J9 e3 n, n+ z7 L& m%% 在图形窗口中显示图片
 / \2 i# u' A8 T$ ?  P8 _8 `* G Im=imread('image.jpg');$ l) b7 I( Q8 o/ }) V, i
 hIm=imshow(Im);
 : ?' a+ I5 H" d7 \& O- |8 \ [x,y,z]=size(Im);% F8 l) j6 R: x; D& b# {
 %% 修改属性并重绘, W: `, R( v  H3 s% G( k
 for i=1:100
 & ^4 H$ S( l. }! k* |9 p) kI=Im(1:x/100*i,1:y/100*i,:);%逐渐放大显示
 : J6 ?. I! ^7 Y" g, g5 Cset(hIm,'CData',I);
 o% ~9 c# w- s  k$ f4 z, @drawnow;4 R3 H8 F* _1 u( g. u
 end; V4 p% B; x) F: e5 o7 F% @
 代码5-8行新建图形窗口并为窗口中的对象设置初始属性,可以理解为给后面显示图像提供一个自定义的环境。首先第5行figure函数用于Figure图形窗口的创建,括号中参数设置相应属性,其中'menubar','none'表示禁用菜单栏;‘NumberTitle’,‘off’表示图形标题中不显示图形编号;‘Position’,'[800 800 360 360]'表示设置图形窗口的位置与大小,格式为[左 底 宽 高];‘Name’,'图片切换动画效果',表示设置图形窗口的标题。
 ! b) T/ d' O0 O& H" v3 f  M( e. i+ h0 w6 ^: W
 第7行设置图形窗口在居中位置。第9行axes函数用于Axes坐标对象的创建,即在当前图形窗口中新建一个坐标轴,括号里面参数设置上与figure格式相似,即设置坐标轴不可见、计量单位为常规、绘图区域的位置和大小设为[0 0 1 1]([左 底 宽 高])。
 i8 S7 d8 e$ Z( p5 a  D6 p# L/ t
 第12-13行为读入图片,并在上面设置的坐标中显示图片,hIm为image(图形)对象的句柄,可通过句柄对该对象进行操作。. |4 g. f3 z0 G6 k& G8 L
 
 9 [5 ?% O7 F! R      第16-20行是在for循环中修改image(图形)对象的CData属性,并重绘图形从而实现动画的。具体的第18行中,set就是一个用于设置一般对象属性的函数,第一个参数hIm为图形句柄用于指定对哪个对象进行操作,这里就是image对象了;后面‘CData’,‘I’是指当前要显示的图像数据设置为矩阵I。整句就是指对hIm对象设置其要显示的图像这一属性为I。第19行drawnow就是将属性改变了的图形显示出来。' e/ T; g. S% Y3 c
 * n6 f( r0 G! W. j
 实现的效果如图2.3.1所示
 & E* k; r6 A7 |+ [) e
 ! Y) d9 b; }0 L& |3 P5 V" h
   ' f6 N( t; \  s  x1 d' S$ [4 A% i; r& l# S% a- E
 图2.3.1 程序动画效果* A# b; M# R/ B4 l: ]3 Y2 N+ `
 
 3 d4 A: O6 ?: f# K" w' w3. 图片切换效果制作
 2 s, r0 |5 H9 p" m9 n3 O       经过前面动画制作原理的介绍,这节就正式说说开头那个效果的实现了,为了便于编程实现(电影动画方式需先存后播编程稍显繁琐)这里采用程序动画的制作方式。首先需要准备几张长方形图片,为了方便后面处理其大小尺寸应该一致,即几张图像有相同长宽,并与接下来编写的M文件放在同一文件夹下。新建一个M文件命名为imageswitch.m,我们后面的编程都在该文件下进行。
 # {# Y8 [1 i0 c4 y
 I  N, ]' W% i0 v' m3.1 效果一实现& Q& B, G9 Y$ ~! k
 如图3.1.1所示,将长方形图片做一个简单划分(这里我的图片尺寸为1920*1200),以宽度的大小为边长在中间掏出一个正方形,就是图中两条蓝色线条分割的中间部分,那么两条线的位置就是(1920-1200)/2和1920-(1920-1200)/2也就是360,和1560,如此一来这张图片就分成了左右两个长条和中间正方形区域了。+ X& b. e4 x: U/ j* K
 
 5 K' f# C2 p; ]8 k# m+ s/ h( u
  5 m/ u5 D5 l; u ; y& _: b8 Q3 y3 l  H7 \
 图3.1.1 分割图片- ^2 _7 x# |5 z% U- R
 * {0 e0 x4 o& r' s
 现在一步步从头开始编写程序吧,首先实现的是第二幅图片的左边长条由顶部向下移动逐渐覆盖原图片的左边长条,同时右边的长条也逐渐被覆盖不过是由底部向上移动的。在程序中这一过程其实是一个图片矩阵中一部分元素逐渐被另一矩阵中元素所替换的过程。说白了图片的存储和处理都是以矩阵的形式,图3.1那张图片就是一个1920*1200*3的三维矩阵,左边长条的移动就是横坐标范围在1-360,纵坐标范围为1-1200的所有元素逐渐由第二张图数据矩阵的相同范围上的对应元素所赋值的过程了。我们看一段代码理解一下。
 $ a% @! I) i$ h+ a8 T, R' m
 - X# q+ T; p! U! Ufunction imageswitch1()6 g" }9 l" x6 x+ D
 %需显示的图片文件名预存  \/ K* Q. }9 o# d( \# D" \
 S=char('BingWallpaper-2016-09-27.jpg',...% Y( n& h, Y( G. \* {
 'BingWallpaper-2016-10-07.jpg');! G& j# A) N" V5 b& Z
 Imagename=cellstr(S);# y& q/ |4 f7 p, Z/ L' E
 %% 读入图片. |) t0 Q+ k1 \3 n0 i3 R, S' M8 N
 I1=imread(Imagename{1});I2=imread(Imagename{2});
 ! v' \1 b6 R3 R9 S%% 转换存储格式为double
 $ g8 J& o; A- e# S6 DI1=im2double(I1);I2=im2double(I2);
 ) `6 [- ^3 q0 Z, s# {7 i9 y%%
 - O. L% L  p7 Z- d% m[x,y,z]=size(I1);2 a1 |0 _( ?' V7 [
 Im=I1;) l) @9 Q4 z( q
 %% 创建图形窗口并设置图形对象初始属性
 7 ]' |: ^, w- [3 d: HhFigure=figure('menubar','none','NumberTitle','off','position',..., k9 F6 K0 i; H6 n0 E
 [1000 1000 720 450],'name','图片切换动画效果');
 5 _7 w: U' X( k7 b. q6 A1 g3 G  hmovegui(hFigure,'center');
 1 r1 Q  t1 I) e; J6 p) v% ?axes('Visible','off','units','normalized','position',[0 0 1 1]);# C+ ^% g$ i& @9 _6 Q" G  _8 |$ |
 % 显示图片
 + p4 I& Q& {0 M5 |2 c; [. x2 RhIm=imshow(Im);
 2 T* g* }, G8 \( T4 s1 }- ]
 $ ~1 [2 o0 R/ R% ~step=x/100;%渐变步长
 ' G( J* w6 x2 y" }. A4 MLength=(y-x)/2;
 5 y, P; t5 R2 T7 ?; T+ o % 动画效果一
 ; ^: j/ A2 b2 k2 O) p    for i=step:step:x# D8 |# B5 g$ p2 \5 n
 % 改变图像数据
 3 x! G) P/ F4 [6 e! i        Im(1:i,1:Length,:)=I2(x-i+1:x,1:Length,:);' T1 i: V* q. V/ P3 B2 y
 Im(x-i+1:x,y-Length+1:y,:)=I2(1:i,y-Length+1:y,:);
 : I" |6 J) p* D4 k        set(hIm,'CData',Im);%设置image对象CData属性为Im
 % }3 ^) C3 L+ q- L: Y+ z. E) U: h$ c        drawnow ;%重绘当前图形窗口
 $ B% I4 {7 U5 @7 u    end
 + k7 }9 \6 R8 f6 @$ ]- G, V! @0 ]end9 _+ Y" |- q) l' _( k
 代码中第1-19行是新建图形窗口并对图形对象设置属性值为后面动画制作提供一个好的“环境”,在2.3节中已经详细介绍这里就不再赘述了。21-30行就是重点了,21行是设置一个赋值的范围跨度,值越大后面动画进行的速度就越快;22行的Length就是小长条的宽,为了方便后面用到;整个动画的实现在for循环中进行,第26行Im是将要显示的矩阵而后面的I2是下一张图片的数据矩阵,在第12行Im已经被赋值为第一张图片的数据矩阵I1了,现在要做的就是随着for循环的进行i的值逐渐增大长条逐渐被赋值了。22行语句如下
 * q' T2 }! P( N. G& c# c
 ( T7 c5 U/ d2 X4 {! `Im(1:i,1:Length,:)=I2(x-i+1:x,1:Length,:);4 O/ G8 }; O% a' Z
 可以看到第二维和第三维的范围两边矩阵是相同的,都分别是1:Length、:,即第二维索引范围是1到Length(小长条的宽),第三维索引取全部范围(第三维可认为是对色彩的设定,“:”表示取索引全部范围,就可以认为是保留色彩了,实际还得理解下RGB图像的存储方式哦)。第一维范围为1到i,随i的增大越来越多的区域被I2中x-i+1到x范围的I2覆盖。同理第23行也是逐渐覆盖原有图像右边长条,所以这是第二维的范围应该是y-Length+1到y,而右边的长条下一张图片的那一部分是向下逐渐覆盖的,故第一维两个矩阵的索引范围与左边的相反。
 # U! F" Z' Q$ V. f: F. d5 z* V) o1 D6 C1 a, s
 第24行将image对象的CData属性设置为赋值后的Im,即显示的图像是新的Im。第25行用drawnow函数重绘图形窗口,就会显示新的一帧。最终以上代码运行的动画效果如图3.1.2所示
 5 ~* b8 E" y" N) t. e2 U1 {- Y$ h# x- v$ r  t
 
   4 E- t. j; E+ f7 u
 R- A3 ]/ c8 n$ ], @& p+ w                                                                                           图3.1.2 长条移动演示  d9 L2 S) K( D$ {$ z
 % R% ?; \1 D5 X6 T' R, B
 将中间正方形区域平均分成上下两部分,上部分区域由下一幅图片相应部分向右移动覆盖,下部分由下一幅图片相应部分向左移动覆盖,效果一的完整效果就是图3.1.3所示的
 ; i5 G# G+ [+ c  S
 % Y- Q2 P+ r* t, Q/ \, `7 B- s
   ( L" f( g  K5 H! I8 ?  j  S1 v$ K$ P
 图3.1.3 效果一的完整效果
 9 I& G5 ]# h7 J" I; ^- e% }. W, ~% L& f
 这只要在上面实现长条的代码23-24间加上以下两行代码其原理与长条移动相同,只不过这时的索引范围需要有所改变上层范围为1到x/2,即上半部分,第二维Im是从Length+1(正方形开始的地方)至Length+i(随i的增大逐渐覆盖这个正方形宽度范围)被I2中最右边部分开始的像素部分取代,而第二行正好覆盖的方向相反。
 & o5 y7 j. Z; x9 \8 H- U- S
 0 f* h; G$ @/ Z% rIm(1:x/2,Length+1:Length+i,:)=I2(1:x/2,y-Length-i+1:y-Length,:);0 b* O) M: @9 E% m
 Im(x/2+1:x,y-Length-i+1:y-Length,:)=I2(x/2+1:x,Length+1:Length+i,:);% C4 V8 c% p7 |  A$ N
 效果一的完整代码如下,新建到imageswitch2.m文件中即可实现图3.1.3的效果
 3 A* y& H; Q% S& g3 u7 ~$ z' Q9 B! H# h7 X* l
 function imageswitch2()& k6 F$ Z' O! y, G/ u; l: j
 %需显示的图片文件名预存
 - ^" X7 d8 o8 t  H# i3 D% WS=char('BingWallpaper-2016-09-27.jpg',.... f% n: i! V- @. }# c# [
 'BingWallpaper-2016-10-07.jpg');
 4 t' Y4 X1 |) e. o! SImagename=cellstr(S);
 6 b# P/ H6 p9 ?4 `%% 读入图片
 - E- z; v# n+ {# q! i4 MI1=imread(Imagename{1});I2=imread(Imagename{2});3 Z9 e: |! N& H
 %% 转换存储格式为double( X+ T6 p. R, a; l7 w3 K
 I1=im2double(I1);I2=im2double(I2);" o( ]* O& e8 [( |7 z  m$ \# {& M
 %%
 0 ^; M, _+ P5 G$ m[x,y,z]=size(I1);' K# X4 j/ f$ ^
 Im=I1;8 v! n$ c  X+ k9 y
 %% 创建图形窗口并设置图形对象初始属性
 7 E) }) G) Q+ C3 ~( ]2 t' ^hFigure=figure('menubar','none','NumberTitle','off','position',...
 + ^7 r2 j6 n" @% i  a5 d  {0 c3 _    [1000 1000 720 450],'name','图片切换动画效果');
 + c& u+ D; X9 s2 s' Ymovegui(hFigure,'center');# g4 j- M9 u$ f$ T$ U& ?
 axes('Visible','off','units','normalized','position',[0 0 1 1]);
 2 R5 \. E: `- K4 ^5 ?% _+ u% 显示图片
 8 b+ B2 ~. g2 r# G+ |* @2 n& whIm=imshow(Im);; P1 R* k$ X. l% o3 F9 {
 m/ G# D7 b& \! {
 step=x/10;%渐变步长# e3 l. l$ m1 Y$ S( O6 O, ?2 O
 Length=(y-x)/2;
 # R, @4 O! V$ r % 动画效果一/ W  V5 G) O" I5 j* ?$ n
 ! {! H" `! V0 ]9 ^4 m: _" E7 q
 for i=step:step:x
 8 R: u% M; L9 n5 q        % 改变图像数据0 `' ]$ ^% `0 b8 z6 d
 Im(1:i,1:Length,:)=I2(x-i+1:x,1:Length,:);
 ) p8 c( Y0 F# M, @# e% k        Im(x-i+1:x,y-Length+1:y,:)=I2(1:i,y-Length+1:y,:);
 ! A7 Z$ c5 D- T        Im(1:x/2,Length+1:Length+i,:)=I2(1:x/2,y-Length-i+1:y-Length,:);4 }* O1 h. b8 H: U" z7 o
 Im(x/2+1:x,y-Length-i+1:y-Length,:)=I2(x/2+1:x,Length+1:Length+i,:);0 d0 y5 d6 a7 g$ A7 X0 o
 set(hIm,'CData',Im);%设置image对象CData属性为Im2 D4 G8 n) c- T, E
 drawnow ;%重绘当前图形窗口
 5 [& ^! G) @. t5 b: m" h* E    end
 * ^2 E" P* k" u! k: c( X6 K    Im=I1;: T) [- r+ U) {! h
 ! ?* A. B: R* r% ^% L
 end7 `& k+ H1 h, ^5 o7 ~, k3 X+ i
 同样可以将覆盖的方向改变,如取与上面相反的覆盖方向就可以实现又一种切换效果了。* ?" S' I& c1 x' ?5 r7 Z& j. L
 7 L- w! q( K$ @. I2 x
 3.2 缩放效果制作
 & q& \) T/ ~9 Y* s" E4 x0 P/ |7 B       对于中间的正方形区域可以设计一个缩放的效果,如图3.2.1所示。4 F- s. g! X- _1 i& @" w* J
 $ P1 u9 n& K# L& _0 E  i% F
 
 ![]() ( Q' A. x* n" `( @" i0 n$ P( }" I- M$ X0 O5 x- L4 q7 W
 图3.2.1 缩放演示效果$ G" [% p+ X! g2 w1 [8 \
 
 ) p! V, d: z( g8 e* O( X" R 3 q- ~$ J& C) n3 V" x5 u, y3 {+ K
 
 9 P7 R) Y$ r( n$ h这时对正方形赋值的代码如下     6 `# b& R6 q7 |
 
 , V& P& E! ?- {, `7 K6 n% i: \Im(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:)=I2(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:);
 0 M! X: c1 H" C4 X1 q2 H3 O       要实现缩小就需要正方形区域的索引范围以一定速度缩小,最终由下一张图片完全覆盖,实现图3.2.1效果的完整代码如下
 : Z# e/ ?; u( y  [9 O' z5 j5 u: P* ^, m, O0 x( I& L" d! {
 function imageswitch3()
 " O+ a) {. e# e% m%需显示的图片文件名预存, `5 s- H5 ~6 j
 S=char('BingWallpaper-2016-10-13.jpg',...& m* I' ?# ?6 H
 'BingWallpaper-2016-10-14.jpg');' P2 b. i  ?4 X2 V1 I
 Imagename=cellstr(S);9 |! M# y/ ?% S- @' a4 e
 %% 读入图片
 ; W( t6 i& @. s+ e5 `0 X% D7 @I1=imread(Imagename{1});I2=imread(Imagename{2});) T  {: j( U& ~3 f& C
 %% 转换存储格式为double0 C9 K) O+ b1 Z/ O
 I1=im2double(I1);I2=im2double(I2);7 v% |( r* v& A- G) k- k
 %% 9 ?* y3 A" i) n& V& }- v
 [x,y,z]=size(I1);
 4 i5 ?+ \; e- F; O7 \Im=I1;
 ' d3 l7 g/ x* J7 X& k%% 创建图形窗口并设置图形对象初始属性
 / t( k9 z3 g+ Y! j9 RhFigure=figure('menubar','none','NumberTitle','off','position',...
 : m% K' @) u: M( d% ?+ u. `9 \    [1000 1000 720 450],'name','图片切换动画效果');% _# W1 q2 |$ G. z0 L. L
 movegui(hFigure,'center');0 ^- v/ a' f; j
 axes('Visible','off','units','normalized','position',[0 0 1 1]);8 C# Q6 |% c* C' U4 l+ m- _
 % 显示图片
 9 m8 C$ U: V/ c! }" J. O: XhIm=imshow(Im);0 r' n; e: t  U3 x. ^
 8 ?$ S' X: z9 l" Q% `8 c7 `& a
 step=x/10;%渐变步长
 \. G: {/ K1 m+ D% gLength=(y-x)/2;
 0 U& s* E) E6 l& D+ H2 a* Q1 j % 动画效果一
 . W* r: T9 |/ z$ t' q; x/ ^while 1
 : K- `1 l5 G* u, @
 ( y5 Z& i- G  B6 o/ {    for i=x:-step:step  , S# Y' U7 o. c# E  k8 N6 W+ J
 Im=I1;
 ' X5 k) O' n2 f        Im(x-i+1:x,1:Length,:)=I2(1:i,1:Length,:);
 - L, d2 N" I- Q! Y        Im(1:i,y-Length+1:y,:)=I2(x-i+1:x,y-Length+1:y,:);
 - [9 B0 S$ ~% h. F
 5 `8 P! \' ~4 q        Im(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:)=I2(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:);6 y9 O0 ~2 R. B: c
 set(hIm,'CData',Im);%设置image对象CData属性为Im
 % b1 v. ~' N% j  h# T6 l        drawnow ;%重绘当前图形窗口% G! A3 r9 w9 k% J
 end+ a! f. W3 x* @* ?
 Im=I1;
 + }& @1 F  [1 H6 h- _end. b/ j% C% E& K* u
 end3 k% B3 C* v/ l( B" s9 {7 B8 a  d& Q1 P
 同样可以设计一个从中间放大而覆盖图像的效果,这只需要改变上面代码中for中的赋值部分就可以了,至于具体如何修改大家只要参考第4节中的完整代码就行了,限于篇幅这里就不多说了。$ S1 n  j; {/ h: a; I( K* W3 T
 5 N' m4 A$ Z" w+ t
 3.3 将动画保存为GIF. y6 [0 u( b( N
 MATLAB 制作gif动态图的基本思想就是,将一张张的静态图组合成一张能动的gif图片,并保存到相应的位置。 那么,要想制作一张动态图,首先要有若干个静态图,并且他们的索引值是连续的。将我们制作的动画保存下来其完整代码如下( z; z9 f3 ^, b! |' w( k
 T" h* t4 L% i- Z# U  V) I
 function imageswitch4()
 7 I9 |! x& y" G& W$ }%需显示的图片文件名预存
 2 L  j6 L5 `7 ?3 r; tS=char('BingWallpaper-2016-10-13.jpg',...
 9 V8 N5 Y$ t, N( i3 j$ j; ?) ~2 A& \9 Y    'BingWallpaper-2016-10-14.jpg');
 ! ]. d! v* E1 m& rImagename=cellstr(S);! O* q. a0 x+ H" ]* W! U8 x
 %% 读入图片
 * C9 \1 X) Y+ D4 Z9 j' {. W! V' LI1=imread(Imagename{1});I2=imread(Imagename{2});4 J" W+ R  |. e+ E
 %% 转换存储格式为double
 + n) t! C, [- G- SI1=im2double(I1);I2=im2double(I2);1 z% R5 w4 g8 t1 l1 V" j$ a
 %%
 ; G" N, r( x( p! U8 e" U& u[x,y,z]=size(I1);
 1 Y/ s/ W8 }' E* ~+ Z8 \Im=I1;8 S! O+ q0 M- d
 %% 创建图形窗口并设置图形对象初始属性
 3 F  ?4 F- n2 S  |hFigure=figure('menubar','none','NumberTitle','off','position',...
 $ a' y- }' d$ Z; B5 L3 V3 ^    [1000 1000 720 450],'name','图片切换动画效果');
 , f8 ^3 c: _) A/ h* N! E4 {$ Tmovegui(hFigure,'center');
 4 T1 }5 J% }2 X( A" Uaxes('Visible','off','units','normalized','position',[0 0 1 1]);" ~& n9 J4 K' P: t' U3 B; U
 % 显示图片
 . o7 U7 s' {' u8 R! p0 o2 G+ I/ `hIm=imshow(Im);
 0 ^! v1 T+ m% V2 a! }6 N
 8 c- Q$ B0 O: w& N# H3 k- lstep=x/10;%渐变步长2 E- e4 F9 Y5 a6 Q) r7 v4 r. |
 Length=(y-x)/2;8 ~4 ]; X& p; T1 q8 H
 % 动画效果一, ?3 m, q8 V) r, ]: V# t" Y/ X1 O* ^
 . M1 Y* D9 [( l' K# B8 x9 O2 U+ _& w
 jo=0;
 6 l. a4 l' N+ [    for i=x:-step:step  8 A+ X; ]' L- z! S8 N
 Im=I1;' U0 d& _$ N- ]- L8 o4 E2 N  A
 Im(x-i+1:x,1:Length,:)=I2(1:i,1:Length,:);
 3 Q7 k- I* o+ I4 B        Im(1:i,y-Length+1:y,:)=I2(x-i+1:x,y-Length+1:y,:);
 z* i% Z) _& n6 s; |
 " O" ?1 {& b7 G2 i- Y        Im(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:)=I2(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:);6 n: {( g. d- \1 L
 set(hIm,'CData',Im);%设置image对象CData属性为Im' N  \' Y' X- ]% o, N0 v% }" q5 B. ~8 O
 drawnow ;%重绘当前图形窗口
 * u) Q# ^$ s0 n" }- c
 9 ^5 o% [) W: W. A7 j. Y        jo=jo+1;/ x8 n- o5 ?: I( r+ e$ Z
 %依次输出图片
 / o& h0 C& G7 I; w) d! `        print(hFigure,'-dbmp',sprintf('%d',jo))   $ F6 V- W! F! {' x; a
 end% p5 q) {+ Q+ `9 }. e, E
 $ V0 }# ]3 A* F' f' P- W* y1 l8 E6 G
 %% 依次读取生成的所有图片
 ) I9 _$ a. J) K+ e" S& M' ifor j=1:jo
 # b" E, K' O) u    %获取当前图片) Y) x1 J7 v9 {! ^/ y$ D9 C
 A=imread(sprintf('%d.bmp',j));
 8 D5 a. h1 C% |6 D( t' A9 {" l    [I,map]=rgb2ind(A,256);6 U" f8 T. S$ E* `
 %生成gif,并保存
 " H: Q# X6 i" b1 o0 K! D    if(j==1)8 H" t- {, i7 o! k1 x
 imwrite(I,map,'movefig.gif','DelayTime',0.1,'LoopCount',Inf)
 4 U# H" S4 m% E1 r$ |; {5 L! S    else
 4 |& U# M0 C9 i( H0 C+ \; `- l        imwrite(I,map,'movefig.gif','WriteMode','append','DelayTime',0.1)
 1 A$ L) U0 f' M" C! L    end/ n( m7 U5 o! J' e7 a
 end3 c* l. l  F: ^- \5 Z
 
 + d; e$ [- w. m9 `3 a# Kend0 H7 H6 n8 m3 ~% M5 J
 
 + A8 D( F' K) X; ?, _* F9 s) N: D0 o' F  }& _
 前面第1-33行代码与之前的代码一致,在第37行print(hFigure,'-dbmp',sprintf('%d',jo)) 中hFigure是当前图形窗口的图形句柄,指定输出当前窗口的图像,'-dbmp'指定输出文件格式,sprintf('%d',jo)是对bmp文件命名为jo。后面第41-51行的for循环是遍历刚刚生成的图片将其组合成gif文件,生成的gif文件如图3.2.2所示。
 : Q6 v* L8 w8 n9 q$ U# |: L( W% L2 o# u1 f# V9 L& \" r' y* s0 Z
 
 ![]() ! t/ c' _$ K+ n, W1 R* c7 R4 l 
 " q+ j9 @5 a2 j                                                                                              图3.2.2 movefig.gif9 K, F5 c5 K' f* O9 K4 L- ~: ^
 [7 K" b% l/ n! }
 4. 完整代码1 N- R  F; S+ o- l) f, T8 r1 F: b" X7 h
 实现文中开头图中的效果的完整代码如下所示,在MATLAB中新建一个m文件,命名为imageswitch.m运行即可。注意将相应图片放在m文件相同文件夹下,图片的命名需与代码中一致(也可修改代码中文件名)免得出错。下面是本文所用到的图片文件,需要的可以选中复制下来用于测试程序,只是需要改下文件名与这里一致。
 ) v& c' R9 A( ~9 f
 0 O; n) B9 H0 {9 c6 D%作品:图片切换动画效果6 V3 p% x- Z8 I7 u# m* ]
 %作者:吴限 7 U6 C! F7 W, {8 s% |% x
 %2018.3.14
 % P# S- E( T: Y8 ?+ Jfunction imageswitch()
 5 U  ^4 m  P' a* @) V( o%需显示的图片文件名预存
 s* J3 C: x. d0 G  }S=char('BingWallpaper-2016-09-27.jpg',...
 * T2 b, A( ~+ R, e- \2 Z    'BingWallpaper-2016-10-07.jpg',...( G- W1 j3 V9 H1 p3 C
 'BingWallpaper-2016-10-13.jpg',...
 , d# z9 G* c; V    'BingWallpaper-2016-10-14.jpg',.../ h/ ^3 l8 a+ ^8 l: A- P
 'BingWallpaper-2016-12-26.jpg');
 . E5 h2 [5 A/ KImagename=cellstr(S);0 `1 N" F6 R+ L3 S' b& u
 %% 读入图片5 h% C2 y' ]. b6 R* h! a
 I1=imread(Imagename{1});I2=imread(Imagename{2});
 1 _# `1 _' \  d/ M0 h+ }: Q' h3 YI3=imread(Imagename{3});I4=imread(Imagename{4});
 6 u0 Y2 F1 K5 p& o0 y7 M5 ~I5=imread(Imagename{5});
 * U) m+ \5 N6 t, W3 Y0 X- M. X# L& ^6 I%% 转换存储格式为double: i4 Y7 i9 [1 F, o3 k( }8 u
 I1=im2double(I1);I2=im2double(I2);I3=im2double(I3);/ L- S# p5 l! S2 I" X! l
 I4=im2double(I4);I5=im2double(I5);4 s* G8 q6 B3 W4 x1 `
 %%
 0 p. s% N! H7 Q- M9 v[x,y,z]=size(I1);
 + J* P- |1 S! p8 uIm=I1;
 / e8 x, r( n0 l  U9 T' T* Y%% 创建图形窗口并设置图形对象初始属性/ I+ d; T, e9 I4 y: J3 ^6 N& Q
 hFigure=figure('menubar','none','NumberTitle','off','position',...# j8 Y& y) D0 \9 X$ U3 C4 l9 P
 [1000 1000 720 450],'name','图片切换动画效果');
 % L2 K5 F  L' d, I# f) Q' b5 omovegui(hFigure,'center');0 b8 i) ^( U. p4 K2 i* t
 axes('Visible','off','units','normalized','position',[0 0 1 1]);
 3 u  D5 Z' M  \  i# |% 显示图片6 u4 y2 d  F! y% V  T; J' X
 hIm=imshow(Im);+ j5 c' b$ H# J7 V2 x/ t
 
 0 u$ l+ Y! w/ A  Dstep=x/10;%渐变步长
 ( `6 u% v% o3 u, PLength=(y-x)/2;5 M; `' c, z0 k0 c
 
 * i# D* h3 n. [. n+ twhile 1' A2 V% G) G( L+ `* m* J7 `
 % 动画效果一4 o* Z+ t' C4 A+ D0 \# A- d  R
 for i=step:step:x
 % B* S; y% `4 g  K1 n3 f        % 改变图像数据! w7 a" s1 H5 o5 w
 Im(1:i,1:Length,:)=I2(x-i+1:x,1:Length,:);8 |9 I1 o- j" ?! k. F2 M0 V
 Im(x-i+1:x,y-Length+1:y,:)=I2(1:i,y-Length+1:y,:);+ ~; b& e# g: z0 x
 7 R. |7 J; e  r! y. S" g; l
 Im(1:x/2,Length+1:Length+i,:)=I2(1:x/2,y-Length-i+1:y-Length,:);
 4 X  |# b) r, w3 ]% ?        Im(x/2+1:x,y-Length-i+1:y-Length,:)=I2(x/2+1:x,Length+1:Length+i,:);1 C3 p6 r3 j# y, e: i
 * E2 z3 z: Q$ _9 b* a5 q$ Y6 ~
 set(hIm,'CData',Im);%设置image对象CData属性为Im
 5 H5 J) o3 f) C% Y* x        drawnow ;%重绘当前图形窗口
 ) U+ K3 O8 X* u& R  ~" L/ @    end; B) d6 r3 \% C1 Y7 g2 h
 
 ) X1 s9 v( B* @; Q9 `$ u     % 动画效果二
 5 H" q$ ]" }3 m' I    Im=I2;
 7 z: k5 l: H4 Y8 F    for i=x:-step:step9 ~  U) F3 `* [% ^% I, e
 Im=I4;6 \/ L+ O; o" u" C  r' z, }
 Im(x-i+1:x,1:Length,:)=I3(1:i,1:Length,:);
 1 `$ e" Q, L* B0 N& i; H2 @        Im(1:i,y-Length+1:y,:)=I3(x-i+1:x,y-Length+1:y,:);/ J# R8 j) a. s) x
 
 8 s0 y  K, @7 }- ^        Im(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:)=I3(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:);/ N) q) Y! j+ W
 set(hIm,'CData',Im);%设置image对象CData属性为Im
 7 l* l) K; S0 u- F! c/ _1 L7 N        drawnow ;%重绘当前图形窗口# @! R) m, f/ y) _
 end0 V7 J2 T6 _( q
 
 1 t6 i& R$ O/ M$ @) J    3 u/ T8 v' f' P$ Z; J* N3 K# I
 %动画效果三
 5 a# D1 p1 Q5 M! t    Im=I3;
 ' @; Z1 m: N& I; E# k+ @    for i=x:-step:step
 " A5 a2 U7 n8 [" {        Im=I1;
 9 J! i9 B) r8 f  P8 u        Im(1:i,1:Length,:)=I4(x-i+1:x,1:Length,:);
 ) S7 k  K9 }; H- p4 [9 E        Im(x-i+1:x,y-Length+1:y,:)=I4(1:i,y-Length+1:y,:);
 1 U/ y4 {- \% A5 g. x# ^
 & C' R4 e! H; y$ b) T' N: w$ U9 K        Im(1:x/2,Length+1:Length+i,:)=I4(1:x/2,y-Length-i+1:y-Length,:);
 & Q* {1 p) Y1 Y  T  B        Im(x/2+1:x,y-Length-i+1:y-Length,:)=I4(x/2+1:x,Length+1:Length+i,:);3 I+ _! T' x2 O! L! u
 # G0 E( b* f( Q( X* U& K
 set(hIm,'CData',Im);%设置image对象CData属性为Im3 G0 i! ^3 _+ y7 @, C
 drawnow  ;%重绘当前图形窗口
 3 n. o. f& u: M; P3 c# f7 _- R2 }    end, W" D; e3 \8 u) y6 }: }0 f
 
 ) T- M* g. |. O  p8 r. W5 k8 n    %动画效果四
 : Q6 `  Q; h; T3 P+ z3 l& P    Im=I4;
 $ t6 a: A+ [2 `, ^- M0 }    for i=1:step:x
 % ]( p" T( V( g5 S        Im(x-i+1:x,1:Length,:)=I5(1:i,1:Length,:);
 6 V0 X' O* k. g        Im(1:i,y-Length+1:y,:)=I5(x-i+1:x,y-Length+1:y,:);3 U! @. `  l3 S4 u7 ~: t: Q
 
 1 ]4 }) j9 w7 n( s        Im(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:)=I5(x/2-i/2+1:x/2+i/2,y/2-i/2+1:y/2+i/2,:);
 5 m0 f  m: s% |% l6 ~" ]        8 Q8 C5 r/ H' ^
 set(hIm,'CData',Im);%设置image对象CData属性为Im
 9 g2 k7 h% ]3 ]" V9 F5 J3 y8 A+ g        drawnow ;%重绘当前图形窗口
 4 t4 p4 r" W/ f  e    end- l' X. ^4 O+ |
 
 8 Z& ^; q0 s9 O. C) F: ~2 c    Im=I5;
 ) c3 ?8 J+ c* T( e4 M8 K  S) vend1 a$ A+ I# F2 I  J! y* M
 end+ A0 u3 A, }7 G  Z; L( ^; {" J
 
 ) K, F3 F6 a1 ?& G( O
 , e  d1 U$ `( ^" _$ v1 X0 d! R4 S" [0 R, g( P$ K2 r( C2 s4 M
 
 | 
 |