Julia의 애니메이션 작동 방식

소개



애니메이션으로 해보고 싶은 것이 있으므로, 기존의 코드로 어떠한 구현이 이루어지고 있는지를 조사한다.

특히 Plots.jl 1의 animation.jl을 읽습니다.
htps : // 기주 b. 코 m/쥬아아 Pぉts/P㎉ts. jl/bぉb/마s r/src/아니마치온. jl

우선 개요에서



Animation을 사용하는 방법



일반적인 사용법


anim = @animate for i in 1:10
    plot(t->sinpi(t+i/5), range(0, 2, length=100))
end

#gif(anim, "sin.gif", fps=10)



매크로를 확장하면?



의미적으로는 다음의 코드가 된다.
anim = Animation()
for i in 1:10
    plt = plot(t->sinpi(t+i/5), range(0, 2, length=100))
    frame(anim, plt)
end

보다 정확하게는, @animate 의 부분을 @macroexpand 하면 매크로 전개 후의 코드를 얻을 수 있으므로 확인하자.
# gensym を使って変数名を生成した結果、 var"##tag#番号" という変数が得られる?
var"##anim#576" = Animation()
global var"##counter#577" = 1

for i = 1:10
    plot((t->begin
            sinpi(t + i / 5)
        end), range(0, 2, length=100))

    # 例えば 10 フレームごとにプロットする場合は var"##counter#577" を使って条件分岐する
    if true
        frame(var"##anim#576")
    end
    global var"##counter#577" += 1
end

var"##anim#576"
plt 가 명시되어 있지 않지만, 현재 플롯을 보관 유지하는 변수 CURRENT_PLOT 가 암묵적으로 사용된다 ( current() 를 통해서 불려 간다).
htps : // 기주 b. 코 m/쥬아아 Pぉts/P㎉ts. jl/bぉb/까지 r/src/pぉt. jl

처리 흐름


  • 일련 번호 PNG 경로를 기억하기 위해 애니메이션을 만듭니다
  • 프레임 당 plot에서 PNG를 출력합니다
  • ffmpeg를 호출하여 일련 번호 PNG에서 mp4로 변환

  • 주요 함수에 대해 자세히 알아보기



    애니메이션



    일련 PNG 관리

    animation.jl
    "Represents an animation object"
    struct Animation
        dir::String
        frames::Vector{String}
    end
    
    function Animation()
        tmpdir = convert(String, mktempdir())
        Animation(tmpdir, String[])
    end
    
  • 임시 디렉토리 만들기
  • 임시 디렉토리의 경로와 프레임 수를 저장하는 구조체를 반환합니다.
    frames::Vector{String} 하지만 프레임수를 기억하는 것이지만, 왜 이것이 단순한 정수가 아니고, 캐릭터 라인의 배열이 되어 있을까는 궁금해 생각했다. 시도해 보면 (기본적으로 immutable이 되는) 구조체라면 내부 변수를 변경할 수 없지만, 내부 배열이라면 그 요소를 변경할 수 있는 것이다. immutable 의 제한을 회피하기 위해서 이런 방법을 취하고 있는 것일까. dir 는 변경되지 않는 편이 좋은 것은 이해할 수 있고, 뭐 그런 것일까 우선은 납득했다.

    frame



    PNG 1프레임 출력

    animation.jl
    """
        frame(animation[, plot])
    Add a plot (the current plot if not specified) to an existing animation
    """
    function frame(anim::Animation, plt::P=current()) where P<:AbstractPlot
        i = length(anim.frames) + 1
        filename = @sprintf("%06d.png", i)
        png(plt, joinpath(anim.dir, filename))
        push!(anim.frames, filename)
    end
    
  • anim과 plt를 받는다
  • anim에서 현재 프레임 수를 가져옵니다.
  • 현재 프레임 수를 6 자리까지 0으로 채워 PNG 파일 이름으로 만듭니다 (예 : 000001.png)
  • Plots.png에서 plt를 PNG로 출력
  • anim의 프레임 수를 더합니다

  • 여기서 보면 알 수 있듯이 Animation.frames는 연속 PNG 파일 이름의 배열입니다.

    mp4



    일련 PNG에서 mp4 만들기

    엉망이지만 본질적으로이 두 줄로 집계됩니다. fn 는 동영상의 파일 이름입니다.

    animation.jl
    run(`ffmpeg -v 0 -framerate $fps -loop $loop -i $(animdir)/%06d.png -pix_fmt yuv420p -y $fn`)
    
    AnimatedGif(fn)
    

    ffmpeg에 PNG 일련 번호 파일을 전달하여 mp4를 만듭니다.
    그 후로 돌아온다 AnimatedGif 는 후술.

    바세. 처w



    animation.jl
    "Wraps the location of an animated gif so that it can be displayed"
    struct AnimatedGif
        filename::String
    end
    
    # write out html to view the gif... note the rand call which is a hack so the image doesn't get cached
    function Base.show(io::IO, ::MIME"text/html", agif::AnimatedGif)
        ext = file_extension(agif.filename)
        write(io, if ext == "gif"
            "<img src=\"$(relpath(agif.filename))?$(rand())>\" />"
        elseif ext in ("mov", "mp4")
            "<video controls><source src=\"$(relpath(agif.filename))?$(rand())>\" type=\"video/$ext\"></video>"
        else
            error("Cannot show animation with extension $ext: $agif")
        end)
    end
    

    mp4 작성 후에 반환되는 AnimatedGif 는, 작성한 동영상의 패스를 보관 유지하고 있다.

    Jupyter 등으로 객체가 불리면(자), 그 형태에 맞는 Base.show 가 불린다. 위의 코드는 그때의 동작을 설명합니다. 요컨대, mp4 함수가 불려 가면 작성한 동영상을 Jupyter 상에 표시하는 것 같은 HTML 태그를 출력한다.

    Julia/Jupyter에서 이미지 표시 구현
    htps : // 이 m / ㄹ리 my / ms / b5604b41247 8 7 00

    주소에 난수를 부가하고 있는 것은, 캐시에 액세스 시키지 않고, 매회 직접 동영상 파일을 읽어들여 가게 하기 위한 테크닉인 것 같다.



    Copyright (c) 2015: Thomas Breloff. 

    좋은 웹페이지 즐겨찾기