framemixer.py - 連番 BMP を合成。

120fps 動画のフレームを合成して 15fps にしてみるテスト。
D
……に使ったプログラムをとりあえず公開。

あまり使い物になるものでもない感じですが、参考までに。

改良版→ framemixer.py ver.1.1.0 - リファクタリングした。 - つちのこ、のこのこ。(はてな番外地)
# -*- coding: utf-8 -*-
u"""framemixer - 連番 BMP を合成。

連番 BMP 動画を合成して残像付きの減フレーム連番 BMP 動画を造る。

Python 2.6.5 + Pygame 1.9.1

'source' フォルダに入れた連番 BMP を処理し
'dest' フォルダに出力する。

ESC で中断、SPACE で一時停止。
"""

from __future__ import division
import os
import os.path
import pygame
from pygame.locals import *

####
####  設定  ####
####
SIMPLECOPY = False  # ミックスせず間引くだけの時は True に

FRAME_MIX_NO = 8  # ミックスするフレームの数。
                  # 例えば 120fps のものに 8 を指定すると 120/8 = 15fps になる

ATTENUATION = 3  # 減衰率。1 で全てのフレームを等分に合成。
                 # 大きな値にするほど過去フレームが減衰する。(整数でなくても可)

SOURCE = u'source'  # ソースになる連番 BMP ファイルを置くフォルダ。
                    # ファイル名を文字列としてソートするので
                    # ファイル名の長さを揃える必要がある。

DEST = u'dest'  # 出来上がる連番 BMP ファイルを入れるフォルダ。
                # 自動では作らないのであらかじめ作っておく必要がある。
                # また出力前に自動で空にしたりはしないのであらかじめ空にしておくこと。

STARTFRAME = 0  # 開始フレーム。ファイル名でソートした時の順番。0 が先頭。
ENDFRAME = -1   # 終了フレーム。-1 ならば最後まで。

####
####

def simplecopy(frames):
    index = 0
    file_no = 0
    image = pygame.image.load(os.path.join(SOURCE, frames[index]))
    pygame.image.save(image, os.path.join(DEST, ('%08d.bmp' % file_no)))
    screen = pygame.display.set_mode(image.get_size())
    pause = False
    while True:
        screen.blit(image, (0, 0))
        pygame.display.flip()
        if not pause:
            index += FRAME_MIX_NO
            if index >= len(frames):
                return
            image = pygame.image.load(os.path.join(SOURCE, frames[index]))
            file_no += 1
            pygame.image.save(image, os.path.join(DEST, ('%08d.bmp' % file_no)))
            pygame.display.set_caption('%s' % frames[index])
        for e in pygame.event.get():
            if e.type == QUIT:
                return
            elif e.type == KEYDOWN and e.key == K_ESCAPE:
                return
            elif e.type == KEYDOWN and e.key == K_SPACE:
                pause = not pause
    
def framemix(frames):
    images = [pygame.image.load(os.path.join(SOURCE, filename))
              for filename in frames]
    images.reverse()
    image = images[0]
    for i, s in enumerate(images[1:]):
        s.set_alpha(255 // ((i + 1) * ATTENUATION + 1))
        image.blit(s, (0, 0))
    return image

def main(frames):
    index = 0
    file_no = 0
    image = pygame.image.load(os.path.join(SOURCE, frames[index]))
    pygame.image.save(image, os.path.join(DEST, ('%08d.bmp' % file_no)))
    screen = pygame.display.set_mode(image.get_size())
    pause = False
    while True:
        screen.blit(image, (0, 0))
        pygame.display.flip()
        if not pause:
            framelist = []
            for i in range(FRAME_MIX_NO):
                index += 1
                if index >= len(frames):
                    return
                framelist.append(frames[index])
            image = framemix(framelist)
            file_no += 1
            pygame.image.save(image, os.path.join(DEST, ('%08d.bmp' % file_no)))
            pygame.display.set_caption('%s' % frames[index])
        for e in pygame.event.get():
            if e.type == QUIT:
                return
            elif e.type == KEYDOWN and e.key == K_ESCAPE:
                return
            elif e.type == KEYDOWN and e.key == K_SPACE:
                pause = not pause
    

if __name__ == '__main__':
    try:
        pygame.init()
        frames = [x for x in os.listdir(SOURCE)
              if os.path.splitext(x)[-1].upper() == '.BMP']
        frames.sort()
        frames = frames[STARTFRAME:ENDFRAME]
        if SIMPLECOPY:
            simplecopy(frames)
        else:
            main(frames)
    finally:
        pygame.quit()

# Public Domain. 好きに流用してください。