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 で中断。
"""

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

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

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

STARTFRAME = 0  # 開始フレーム。ファイル名でソートした時の順番。(0 から数える)
ENDFRAME = 0   # 終了フレーム。0 ならば最後まで。
               # (1 から数える。つまり 0 から数えるなら 1 足した数)

DISPLAY = True  # 作業中に表示しないなら False
IS_SAVE = True  # 画像を保存しないなら False

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

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

__author__ = 'kadotanimitsuru'
__credits__ = ''
__date__ = '2010-07-18'
__version__ = '1.1.0'

def _imageload(filename, path=SOURCE):
    return pygame.image.load(os.path.join(path, filename))

def _imagesave(image, filename, path=DEST):
    if IS_SAVE:
        pygame.image.save(image, os.path.join(path, filename))

def framemix(frames, attenuation):
    frames.reverse()
    keyimage = _imageload(frames[0])
    for index, frame in enumerate(frames[1:], start=1):
        image = _imageload(frame)
        image.set_alpha(255 // (index * attenuation + 1))
        keyimage.blit(image, (0, 0))
    return keyimage

def _escape():
    for e in pygame.event.get():
        if e.type == QUIT:
            return True
        elif e.type == KEYDOWN and e.key == K_ESCAPE:
            return True
    return False
    
def framemixer(frames, frame_mix_no, attenuation):
    index = 0
    file_no = 0
    image = _imageload(frames[index])
    _imagesave(image, '%08d.bmp' % file_no)
    if DISPLAY:
        screen = pygame.display.set_mode(image.get_size())
    while not _escape():
        if DISPLAY:
            screen.blit(image, (0, 0))
            pygame.display.set_caption('%s' % frames[index])
            pygame.display.flip()
        framelist = []
        try:
            for i in range(frame_mix_no):
                index += 1
                framelist.append(frames[index])
        except IndexError:
            return
        image = framemix(framelist, attenuation)
        file_no += 1
        _imagesave(image, '%08d.bmp' % file_no)


if __name__ == '__main__':
    try:
        pygame.init()
        frames = [x for x in os.listdir(SOURCE)
              if os.path.splitext(x)[-1].upper() == '.BMP']
        frames.sort()
        endframe = ENDFRAME if ENDFRAME else len(frames)
        frames = frames[STARTFRAME:endframe]
        print '%d frames mix, ATTENUATION=%d, "%s" - "%s"' % (
            FRAME_MIX_NO, ATTENUATION, frames[0], frames[-1])
        framemixer(frames, FRAME_MIX_NO, ATTENUATION)
    finally:
        pygame.quit()

# Public Domain. 好きに流用してください。
  • FRAME_MIX_NO の初期値を 8 から 4 へ(元が 120fps なら結果は 15fps から 30fps へ)
  • 無加工でのコピーの機能を削除
  • PAUSE 機能を無くした

そして

  • 非表示のモードを追加
  • セーブしないモードを追加

が機能上の変更点。泥縄スクリプトのテスト用の部分を削って、一般的な実用や(自分のより)速い環境での使い勝手に配慮してみました。

まあ本当に実用にするなら AviUtl なりのプラグインにするべきなんでしょうが。自分用途では連番BMP加工する方が他に追加の処理入れるに便利だし、そもそもプラグインなんて作り方知らないし、なので、こんなところでかんべんを。