MATLAB 영상처리 활용

[MATLAB 영상처리 활용] 애니메이션 GIF 만들기 (Animated GIF)

toyprojects 2023. 8. 22. 02:38

 

 

연산중 혹은 연산후 결과 데이터의 시간별 변화/추이를 애니메이션 이미지 파일로써

시각화(visualization) 할 수 있다면 프로그래밍 혹은 디버깅에서 큰 장점을 발휘할 수 있습니다.

 

간단한 예로써, 정규분포(normal distribution) 그래프 모양을 결정짓는 두가지 입력 파라메터 -  

sigma, mu - 중 sigma 값의 변화에 따른 그래프 변화를 관찰하고자 합니다. 가장 간단한 방법으로는

여러 sigma 후보값을 설정하고, 그에 따른 여러 그래프를 만들어 화면 가득히 펼쳐보이는 것입니다.

 

그러나 여러 그래프를 - 그래프간의 적정한 delay 시간을 주어서 - 하나의 애니메이션 이미지로 만들어서

관찰하는편이 효율적일 것입니다.

 

MATLAB 2022a 버전 이상부터는 "exportgraphics( )" 함수를 이용할 수 있고, 그 이하 버전에서는

이미지 파일 생성 명령어인 imwrite( ) 를 통해 애니메이션 GIF 이미지 파일을 만들수 있습니다 (출처1).

위에서 언급한 정규분포(normal distribution) 의 파라메터인 sigma의 범위를 0.5부터 1.0까지 0.1단위로

증가시켜 그래프를 만들고 약 1초의 delay를 갖는 애니메이션 GIF 파일을 만들어 보겠습니다.

단, 정규분포(normal distribution)의 또다른 파라메터 mu = 0, 그래프의 x축은 -5 부터 +5 까지로

고정하였습니다.

 

 

% normal distribution
mu = 0; 
sigma = 0.5:0.1:1.0;            % range of sigma value
imgSet = cell(1,length(sigma)); % imgSet stores all graph

% draw normal distribution graphs
fig = figure;
for iter = 1:1:length(sigma)
    xs = -5:0.1:5;
    ys = normpdf(xs, mu, sigma(iter));
    plot(xs,ys); 
    ylim([0,1.0]);
    title("Normal distribution with sigma: "+num2str(sigma(iter))); 
    drawnow
    frame = getframe(fig);
    imgSet{1,iter} = frame2im(frame);
end
close; 

% make an animated GIF file and save on disk
filename = "Normal distribution graph by different sigma.gif";
for iter = 1:length(sigma)
    [A,map] = rgb2ind(imgSet{iter},256);
    if iter == 1
        imwrite(A,map,filename,"gif","LoopCount",Inf,"DelayTime",1);
    else
        imwrite(A,map,filename,"gif","WriteMode","append","DelayTime",1);
    end
end

 

 

 

 

위의 MATLAB 코드는 https://www.mathworks.com/help/matlab/ref/imwrite.html  (출처2)

"Write Animated GIF" 부분을 참고하여 작성하였습니다.

 

코드는 크게 정규분포(normal distribution) 그래프를 만드는 부분과 GIF 이미지 파일을 생성하는

두 부분으로 나뉘어져 있습니다. 여기에서 눈여겨 볼것은 GIF 이미지로 생성할 데이터 - 여기서는

sigma 값에 따른 6개의 그래프 - 가 하나의 cell 형식 변수에 저장되어 있고, "DelayTime" 파라메터가

그래프간 변환 시간을 조절하는것 입니다.

따라서 다양한 이미지 모음을 하나의 cell 형식 변수에 담아 매우 작은 delay 시간으로 조절함으로써

새로운 애니메이션 GIF 이미지 파일을 생성할 수 있습니다. 

 

 

애니메이션 GIF 이미지 만들기의 또 다른 쉬운 예로써 밑의 GIF 이미지와 같이 물체가 수평방향으로

이동하는 코드를 만들겠습니다.

기본 조건으로써 검은색 바탕의 512-by-512 크기의 이미지가 있고, 30-by-30 크기의 하얀색 정사각형

물체가 일정한 속도로 좌 -> 우 방향으로 50 프레임 동안 이동합니다. 여기서는 정사각형 물체의 좌변이

이미지상의 이동하는 기준이 됩니다. 즉, 1번째 x축 픽셀에서 482 픽셀(= 512-30)까지가 물체 이동의

범위 입니다. 이때 y축은 512-by-512 크기의 배경 이미지의 중간을 고정값으로 설정 하였습니다.

 

 

 

 

코드에서 물체의 이동속도는 직접 설정하지 않았고, 배경이미지상 물체의 이동범위를 프레임수로 나누어 

균일하게 각 프레임별 위치지정을 하여 애니메이션 효과를 만들었습니다.

 

 

img_size = [512,512];           % image size, changeable
movObj_size = [30,30];          % rectangle, both of shape & size are changeable
x_pos_end = img_size(2)-movObj_size(2); 
x_pos_range = linspace(1,x_pos_end,num_frame);

 

 

그리고 검정색 배경이미지에서 물체의 이동을 생성하는 하위 함수 "gen_imageset( )" 를 보면, 

각 프레임별 물체의 위치 지정 변수 "x_pos_range" 가 하위 함수 "gen_imageset( )" 의 입력 파마메터

로써 지정된 x축 위치에 정사각형 물체의 크기만큼 픽셀값 "1"을 삽입하고 이것을 결과 이미지로써

"im_set" 변수에 저장을 하는것이 주요 내용 입니다.

 

이렇게 생성된 "im_set" 데이터는 최종적으로 "gen_animated_GIF( )" 함수에서 애니메이션 GIF 이미지

파일로 생성되고 디스크에 저장됩니다. 애니메이션 GIF 이미지는, 여러 이미지 데이터가 누적(append)된

상태에서 일정시간마다 하나씩 이미지를 보이는 것이므로 imwrite( ) 의 입력 파라메터에  ( "WriteMode" ,

"append" ) 를 설정해 줍니다.

 

 

% variables initalization
img_size = [512,512];           % image size, changeable
fix_t = 0.1;                    % fixed time as 0.1s between frames
movObj_size = [30,30];          % rectangle, both of shape & size are changeable
num_frame = 50;                 % number of frame in total

% position of a moving object in y-axis is fixed 
% on center of an image, thus, an object moves horizontal 
center = [img_size(1)/2, movObj_size(1)/2];   % center pixel of an image & an object each in y-axis
y_pos = round(center(1)-center(2)):round(center(1)+center(2));

% an object moves with fixed speed  
x_pos_end = img_size(2)-movObj_size(2); 
x_pos_range = linspace(1,x_pos_end,num_frame); 
im_set = gen_imageset(num_frame,x_pos_range,y_pos,img_size,movObj_size);
filename = "moving_rect_Animated.gif"; 
gen_animated_GIF(filename,num_frame,im_set)

function [im_set] = gen_imageset(num_frame,x_pos_range,y_pos,img_size,movObj_size)
    background = zeros(img_size);   % all black colored background
    tar_img = background;           % target image where a moving object is playing
    im_set = cell(1,num_frame);     % image-set is stored 
      
    for f_iter = 1:1:num_frame
        % position of a lefe-sided on an object in x-axis 
        x_pos_left = round(x_pos_range(f_iter));
        x_pos = x_pos_left:x_pos_left+movObj_size(2);
        
        tar_img(y_pos, x_pos) = 1;
        im_set{f_iter} = tar_img;
        tar_img = background;
    end
end

function gen_animated_GIF(filename,num_frame,im_set)
    for f_iter = 1:num_frame
        [A,map] = gray2ind(im_set{f_iter},256);
        if f_iter == 1
            imwrite(A,map,filename,"gif","LoopCount",Inf,"DelayTime",.1);
        else
            imwrite(A,map,filename,"gif","WriteMode","append","DelayTime",.1);
        end
    end
end

 

 

 

이번에는 본 글의 첫부분에서 다룬 정규분포(normal distribution) 그래프의 모양을 닮은 물체이동

속도의 변화를 구현해 보겠습니다. 물체는 천천히 이동을 시작하여 급격하게 가속한후 빠르게 감속하다가

천천히 이동을 멈출것입니다.

 

먼저 mu = 0, sigma = 0.3 의 입력 파라메터로써 정규분포(normal distribution)를 생성합니다.

하지만 생성된 정규분포의 값은 실제로 물체의 이동속도를 나타내지 못하고 어떠한 offset을 곱하여

up scaling을 해주어야 합니다. 이 offset을 구하는 방법은 다양하지만, 여기서는 매우 간단한 방법을

취하겠습니다.

 

물체의 이동범위인 482 픽셀(= 512-30)을 정규분포값의 총합으로 나누는 것입니다. 이렇게 구한 offset을

정규분포에 element-wise 곱셈을 수행하여 프레임별 물체의 이동속도 벡터를 생성합니다.

그리고 이동속도 벡터를 이용하여 각 프레임별 물체의 위치 지정을 알려주는 "x_pos_range" 를 만듭니다.

(이러한 일련의 연산과정은 normal_distribution_speed( ) 라는 하위함수로 작성되어 있습니다.)

 

 

% normal_distribution_speed( )
mu = 0; 
sigma = 0.3;
xs = [-1:(2/num_frame):1];
ys = normpdf(xs, mu, sigma);    % ys is not scaled yet
up_scale_offset = (img_size(2)-movObj_size(2)) / sum(ys(1:num_frame));
spd_hor = ys .* up_scale_offset;
x_pos_range = [];
    
x_pos = 1;
for f_iter = 1:1:num_frame
    x_pos = x_pos + spd_hor(f_iter);
    x_pos_range = [x_pos_range, x_pos];         % accumulate x_pos in an array
end

 

 

물체의 이동에 따른 프레임을 생성하는 "gen_imageset( )" 는 앞서서 수행한것과 동일하지만

한가지 추가된것이 있습니다. 입력 파라메터로써 ( '+speed', spd_hor ) 을 추가하면 물체의 이동속도를

묘사한 그래프가 함께 그려집니다. 

물체의 이동속도 그래프를 그리기 위한 배경이미지의 (y축) 특정 구역을 설정하고, 그리기 범위에 맞게끔

이동속도를 rescaling 합니다. 최종적으로 배경이미지상에 물체의 이동과 함께 이동속도 그래프를 그 위에

그려집니다. (이러한 일련의 연산과정은 gen_imageset( ) 하위함수에 작성되어 있습니다.)

 

 

% im_set = gen_imageset(num_frame,x_pos_range,y_pos,img_size,movObj_size,'+speed',spd_hor);

if strcmpi(varargin{1},'+speed')
   spd_graph_flag = 1;
   spd_hor = varargin{2};
   blank_height = 10;
   spd_graph_top = blank_height+1;
   spd_graph_bottom = img_size(1)/2 - movObj_size(2) - blank_height;
   spd_rescaled = (spd_hor - min(spd_hor)) ./ (max(spd_hor) - min(spd_hor)); % 0 - 1.0; min-max scaling
   spd_graph_pos = round((1 - spd_rescaled) * spd_graph_bottom + spd_graph_top);
end

for f_iter = 1:1:num_frame
    % position of a lefe-sided on an object in x-axis 
    x_pos_left = round(x_pos_range(f_iter));
    x_pos = x_pos_left:x_pos_left+movObj_size(2);
        
    tar_img(y_pos, x_pos) = 1;
    if spd_graph_flag == 1
       tar_img(spd_graph_pos(f_iter),x_pos) = 1;    % draw a speed graph
    end
    im_set{f_iter} = tar_img;
    tar_img = background;
end

 

 

 

 

MATLAB의 GUI 환경에서 정규분포(normal distribution)의 모양을 사용자가 원하는 대로

간단하게 변환을 할 수 있다면 skewed 그래프를 생성하여 그에 상응하는 물체의 이동속도를

조절하는 기능을 가질수 있습니다.

 

아니면 Adobe Premiere Pro 혹은 After Effect에서 제공하는 Ease In, Ease Out 기능을

MATLAB에서 묘사할 수도 있습니다. 이것은 물체가 배경이미지상에서 서서히 이동하다가

급격히 가속, 또는 그 반대의 움직임을 생성하는 기능입니다.  

 

 

 

마지막으로 본 글에 쓰인 모든 MATLAB 코드는 밑에 표시해 두었습니다.

 

% variables initalization
img_size = [512,512];           % image size, changeable
fix_t = 0.1;                    % fixed time as 0.1s between frames
movObj_size = [30,30];          % rectangle, both of shape & size are changeable
num_frame = 50;                 % number of frame in total

% position of a moving object in y-axis is fixed 
% on center of an image, thus, an object moves horizontal 
center = [img_size(1)/2, movObj_size(1)/2];   % center pixel of an image & an object each in y-axis
y_pos = round(center(1)-center(2)):round(center(1)+center(2));

% speed generated by normal distribution with sigma modification
% mu = 0 fixed, sigma is changeable
sigma = 0.3; 
[x_pos_range,spd_hor] = normal_distribution_speed(num_frame,sigma,img_size,movObj_size);
im_set = gen_imageset(num_frame,x_pos_range,y_pos,img_size,movObj_size,'+speed',spd_hor);
filename = "moving_rect_by_normal_distribution_sigma0dot3+spd_graph.gif"; 
gen_animated_GIF(filename,num_frame,im_set)

function [x_pos_range,spd_hor] = normal_distribution_speed(num_frame,sigma,img_size,movObj_size)
    % normal distribution
    mu = 0; 
    xs = [-1:(2/num_frame):1];
    ys = normpdf(xs, mu, sigma);    % ys is not scaled yet
    up_scale_offset = (img_size(2)-movObj_size(2)) / sum(ys(1:num_frame));
    spd_hor = ys .* up_scale_offset;
    x_pos_range = [];
    
    x_pos = 1;
    for f_iter = 1:1:num_frame
        x_pos = x_pos + spd_hor(f_iter);
        x_pos_range = [x_pos_range, x_pos];         % accumulate x_pos in an array
    end
end

function [im_set] = gen_imageset(num_frame,x_pos_range,y_pos,img_size,movObj_size,varargin)
    background = zeros(img_size);   % all black colored background
    tar_img = background;           % target image where a moving object is playing
    im_set = cell(1,num_frame);     % image-set is stored 
    spd_graph_flag = 0; 
    
    if length(varargin)==2
       if strcmpi(varargin{1},'+speed')
          spd_graph_flag = 1;
          spd_hor = varargin{2};
          blank_height = 10;
          spd_graph_top = blank_height+1;
          spd_graph_bottom = img_size(1)/2 - movObj_size(2) - blank_height;
          spd_rescaled = (spd_hor - min(spd_hor)) ./ (max(spd_hor) - min(spd_hor)); % 0 - 1.0; min-max scaling
          spd_graph_pos = round((1 - spd_rescaled) * spd_graph_bottom + spd_graph_top);
       end
    end
    
    for f_iter = 1:1:num_frame
        % position of a lefe-sided on an object in x-axis 
        x_pos_left = round(x_pos_range(f_iter));
        x_pos = x_pos_left:x_pos_left+movObj_size(2);
        
        tar_img(y_pos, x_pos) = 1;
        if spd_graph_flag == 1
           tar_img(spd_graph_pos(f_iter),x_pos) = 1;    % draw a speed graph
        end
        im_set{f_iter} = tar_img;
        tar_img = background;
    end
end

function gen_animated_GIF(filename,num_frame,im_set)
    for f_iter = 1:num_frame
        [A,map] = gray2ind(im_set{f_iter},256);
        if f_iter == 1
            imwrite(A,map,filename,"gif","LoopCount",Inf,"DelayTime",.1);
        else
            imwrite(A,map,filename,"gif","WriteMode","append","DelayTime",.1);
        end
    end
end

 

 

 

 

출처1: https://www.mathworks.com/matlabcentral/answers/94495-how-can-i-create-animated-gif-images-in-matlab

 

How can I create animated GIF images in MATLAB?

I would like to know if there is MATLAB functionality to create an animated GIF in MATLAB.

www.mathworks.com

 

 

 

출처2: https://www.mathworks.com/help/matlab/ref/imwrite.html

 

Write image to graphics file - MATLAB imwrite

When writing indexed PNG files, you must specify a colormap with enough entries to interpret the image correctly.

www.mathworks.com