お知らせ: にーやんのブログ 2 に移転しました。こちらのブログは、2009 年末までに削除します。

スポンサード リンク

mojimoji 関数: テキストの移動と拡大縮小

SubtitleとAnimateを組み合わせてテキストを移動させるでは、Subtitle と Animate を組み合わせて、テキストを移動させたり、拡大縮小させたりする方法を紹介しました。

その方法では引数の指定が面倒だったので、今回、これを関数化してみました。名付けて、mojimoji 関数。

ダウンロード

mojimoji 関数のダウンロード(2008-04-08 更新)

シンタックス

mojimoji
mojimoji(clip input, string moji, int start, int end, int x1, int y1, int "x2", int "y2", int "size",
\ string "font", int "text_color", int "halo_color", int "size2", int "lsp")
mojimojiOvr
mojimojiOvr(clip input, string moji, string "fx", string "fy", string "fopacity", string "fzoom",
\ int "size", string "font", int "text_color", int "halo_color", int "x", int "y", float "opacity")

パラメータ

共通
  • input: ビデオクリップ。
  • moji: 表示するテキスト。
  • size: テキストの標準サイズ。
  • font: 使用するフォント。
  • text_color: テキストの色。
  • halo_color: テキストの縁の色。
mojimoji のみ
  • start: 開始フレーム。
  • end: 終了フレーム。
  • x1: start 時点におけるテキストの x 座標。
  • y1: start 時点におけるテキストの y 座標。
  • x2: end 時点におけるテキストの x 座標。
  • y2: end 時点におけるテキストの y 座標。
  • size2: end 時点におけるテキストのサイズ。拡大縮小時のみ指定。
  • lsp: 行間の高さ。Subtitle の同名パラメータに相当。
mojimojiOvr のみ
  • fx: Overlay フィルタの ol_x_offset を定義した ConditionalReader 形式のファイル。
  • fy: Overlay フィルタの ol_y_offset を定義した ConditionalReader 形式のファイル。
  • fopacity: Overlay フィルタの ol_opacity_offset を定義した ConditionalReader 形式のファイル。
  • fzoom: 文字サイズを定義した ConditionalReader 形式のファイル。
  • x: テキストの x 座標。fx を使わず、位置を固定したい場合に指定。
  • y: テキストの y 座標。fy を使わず、位置を固定したい場合に指定。
  • opacity: 不透明度。fopacity を使わず、不透明度を固定したい場合に指定。

mojimoji.avs 内で設定可能な global 変数

mojimoji.avs の中で、以下の global 変数の値を設定/変更することができます。

# デフォルト値の指定
global mojimoji_text_color = $000000 # フォントの色
global mojimoji_halo_color = $EFEFEF # 文字の縁、および、文字を重ねるクリップの背景色
global mojimoji_font = "MS Pゴシック" # フォント
global mojimoji_size = 18 # フォントの大きさ
global mojimoji_lsp = 0 # 複数行表示時の行間の大きさ(-1 なら常に 1 行として扱う)
global mojimoji_mask_tolerance = 10 # マスクの許容度(大きくするほど、より多くの色を透過色とみなす)

font_color, halo_color, font, size, lsp を省略した場合には、上の 4 つの値がそれぞれ適用されます。最後の mojimoji_mask_tolerance は同じ色とみなす許容範囲を指定する。ColorKeyMask の tolerance パラメータに相当。

使用例

mojimoji
# 関数定義ファイルのインポート
Import("mojimoji.avs")
# ソースクリップ
BlankClip(100, 320, 240, fps=15, color=mojimoji_halo_color)
# 右から左へ
mojimoji("               ∧∧\n   ┃   ┏━┃     (,,゚∀゚)     ┃┃\n ━┏┛ ┏━┃ ━━/ つ━━┛ .┃┃\n ━┏┛ ┛  ┃   ~( ,ノつ      ┛┛\n   ┛       ┛     (/        .┛┛", 0,24, 320,80, -320,80)
# 下から上へ
mojimoji("               ∧∧\n   ┃   ┏━┃     (,,゚∀゚)     ┃┃\n ━┏┛ ┏━┃ ━━/ つ━━┛ .┃┃\n ━┏┛ ┛  ┃   ~( ,ノつ      ┛┛\n   ┛       ┛     (/        .┛┛", 25,49, 0,240, 0,-240)
# 拡大
mojimoji("               ∧∧\n   ┃   ┏━┃     (,,゚∀゚)     ┃┃\n ━┏┛ ┏━┃ ━━/ つ━━┛ .┃┃\n ━┏┛ ┛  ┃   ~( ,ノつ      ┛┛\n   ┛       ┛     (/        .┛┛", 50,98, 150,120, -400,-40, 1, size2=64)

zoome動画: mojimoji.avs サンプル動画 1: mojimoji - niiyan X zoome

mojimojiOvr
# 関数定義ファイルのインポート
Import("mojimoji.avs")
# ソースクリップ
BlankClip(100, 320, 240, fps=15, color=mojimoji_halo_color)
# mojimojiOvr 関数
mojimojiOvr("キタ━(゚∀゚)━!", "xoffset.txt", "yoffset.txt", "opacity.txt", "zoom.txt")

※注: "xoffset.txt", "yoffset.txt", "opacity.txt", "zoom.txt" は、サンプルとして mojimoji.avs に同梱されています。

zoome動画: mojimoji.avs サンプル動画 2: mojimojiOvr - niiyan X zoome

関連記事

更新履歴

  • 2005-10-22: 初版。
  • 2007-10-23: ZIP 形式で圧縮したものに差し替え。内容は変更なし。

SubtitleとAnimateを組み合わせてテキストを移動させる

2008-04-08 追記: この方法には問題があります。詳しくは、Subtitle フィルタにとって -1 は魔法の値を参照してください。

にーやんのブログ :: Project N: 024 - 文字雨(Animateフィルタを使う)のように、Subtitle フィルタと Animate フィルタを組み合わせれば、テキストを移動させたり、拡大縮小したりすることができます。簡単な例をあげながら、これをもう少し説明したいと思います。

# ソースクリップ
BlankClip(200, 320, 240, fps=29.97)

BlankClip フィルタを使って空のクリップ(200frames, 320x240, 29.97fps)を作成します。今回は、これをソースクリップとして使用します。

font = "MS Pゴシック" # フォント
size = 36
text_color = color_lightpink # フォントの色($FFB6C1)
halo_color = color_hotpink # 文字の縁の色($FF69B4)

フォントの色や大きさなどを指定しておきます。フィルタの中で直接指定してもOKです。その場合は、下記のスクリプトの font, size, text_color, halo_color となっているところを変更します。

color_lightpink と color_hotpink は、AviSynthで使用出来る色のプリセットです。プリセット値は、AviSynth と一緒にインストールされる colors_rgb.avsi(plugins フォルダの中にあります)の中で設定されています。colors_rgb.avsi は、オートローディング機能で、自動的に読み込まれます。10 進数か 16 進数を使って指定してもかまいません。

# 右から左へ移動
Animate(0, 29, "Subtitle",
\ "キタ━(゚∀゚)━!",320,100,0,29,font,
\ "キタ━(゚∀゚)━!",-200,100,0,29,font)

Subtitle フィルタの引数のうち、x だけが変化していることに注意してください。0 フレームのときは x = 320、29 フレームのときは x = -200 の位置に配置されます。日本語(2バイト文字)なので、それに対応しているフォントを指定する必要があります。

# 下から上へ移動
Animate(30, 59, "Subtitle",
\ "萌えーっ!!",70,240,30,59,font,size,text_color,halo_color,
\ "萌えーっ!!",70,-50,30,59,font,size,text_color,halo_color)

今度は y の値だけが変化しています。

# 拡大
Animate(60, 89, "Subtitle",
\ "萌えーっ!!",160,120,60,89,font,1,text_color,halo_color,
\ "萌えーっ!!",-40,80,60,89,font,96,text_color,halo_color)

size を変化させることで、テキストの拡大や縮小も可能です。ただし x と y を固定にすると、左上から右斜め下へと拡大(縮小の場合は、その逆)するように見えてしまいます。これを防ぐために、x と y の値も変化させています(適当)。

Subtitle フィルタには、テキストの配置に関する align という引数もあります。今回は align は使用しませんでしたが、これを組み合わせてみてもいいかもしれません。

# 左上から右下へ移動
Animate(90, 159, "Subtitle",
\ "/⌒ヽ  ちょっと通りますね",-200,0,90,159,font,16,$FFFFFF,$000000,
\ "/⌒ヽ  ちょっと通りますね",350,150,90,159,font,16,$FFFFFF,$000000)
Animate(90, 159, "Subtitle",
\ "/ ´_ゝ`)",-210,18,90,159,font,16,$FFFFFF,$000000,
\ "/ ´_ゝ`)",340,168,90,159,font,16,$FFFFFF,$000000)
Animate(90, 159, "Subtitle",
\ "|    /",-210,36,90,159,font,16,$FFFFFF,$000000,
\ "|    /",340,186,90,159,font,16,$FFFFFF,$000000)
Animate(90, 159, "Subtitle",
\ "| /| |",-208,54,90,159,font,16,$FFFFFF,$000000,
\ "| /| |",342,204,90,159,font,16,$FFFFFF,$000000)
Animate(90, 159, "Subtitle",
\ "// | |",-216,72,90,159,font,16,$FFFFFF,$000000,
\ "// | |",334,222,90,159,font,16,$FFFFFF,$000000)
Animate(90, 159, "Subtitle",
\ "U  .U",-222,90,90,159,font,16,$FFFFFF,$000000,
\ "U  .U",328,240,90,159,font,16,$FFFFFF,$000000)

x と y の両方を変化させることで、斜め方向への移動も可能です。

# 上から下へ移動
Animate(160, 199, "Subtitle",
\ "萎え . . . orz",70,-50,160,199,font,36,color_olivedrab,
\ "萎え . . . orz",70,240,160,199,font,36,color_olivedrab)

これは下から上への移動の逆です。

FadeOut(30)

最後はフェードアウト。

これを 1 つの AVSファイルにまとめて、MPEG-1 形式の動画に変換してみました。

サンプル動画(MPEG-1, 424KB)

あまりなめらかとは言えないかもしれませんが、一応、動いています。

Project N: 024 - 文字雨(Animateフィルタを使う)

関数化することによって、1 行で文字の縦書き(みたいなの)が可能になりました。

しかし、これだけでは不十分です。なぜなら、縦書き文字列を動かす(動いているように見せる)ためには、以下のように一部のパラメータの値を変化させたコマンドを、いちいち書かなければならないからです。

# Import("tategaki.avs") # インポートする場合
bg = BlankClip(width=320, height=240)
bg.tategaki(4, 0, 0, 0, "A", "B", "C", "D", "E")
tategaki(4, 10, 1, 1, "A", "B", "C", "D", "E")
tategaki(4, 20, 2, 2, "A", "B", "C", "D", "E")
return last

上のスクリプトをよく見ると、使っている関数は自作の tategaki 関数だけで、y, start, end の 3 つの値だけが一定の割合で変化していることがわかります。

AviSynth には、このようにパラメータが連続的に変化する同一のフィルタ(関数)を評価するメタフィルタがあります。それは、Animate フィルタです。

Animate フィルタの書式と引数

Animate(clip clip, int start_frame, int end_frame, string filtername, start_args, end_args)
  • start_frame: 開始フレーム。
  • end_frame: 終了フレーム。
  • filtername: 評価するフィルタ(または関数)。
  • start_args: start_frame 時の filtername の引数。
  • end_args: end_frame 時の filtername の引数。

start_frame と end_frame の中間のフレームでは、引数は補間されます。

Animate フィルタを使ってみる

イメージ

start_frame と end_frame のときに、それぞれ上の図の位置に文字が来るようにしてみます。このとき、start_frame では y = 0、end_frame では y = 170 です。

# Import("tategaki.avs") # インポートする場合
bg = BlankClip(width=320, height=240)
Animate(bg, 0, 29, "tategaki", 4,0,0,0,"A","B","C","D","E", 4,170,29,29,"A","B","C","D","E")
return last

start_frame = 0, end_frame = 29 とすると、スクリプトは上記のようになります。

Animate(0, 29, "tategaki", bg,4,0,0,0,"A","B","C","D","E", bg,4,170,29,29,"A","B","C","D","E")

Animate フィルタは、このように記述することもできます。

サンプル動画

サンプル動画(MPEG-1, 84KB)

上記のスクリプトを、MPEG-1 形式の動画に変換してみました(30 フレーム以降をカットし、音声なしにしてあります)。

文字が上から下へと移動していると思います。ようやく形が見えてきました。

CopyFieldCond関数(2): フィルタ適用前のフィールドと置換可能に

CopyFieldCond 関数を少しいじってみました。

前回のスクリプトは CopyField 関数の拡張版ということで、あるフィールドを別のフィールドに置き換えるものでした。今回の修正で、フィルタ適用後のあるフィールドをフィルタ適用前の別のフィールドに置き換えることができるようになりました・・・たぶん。

そもそも前のバージョンがちゃんと動くかどうかもわからない状態なので、今回のも意図したとおりに動作しない可能性があります。よって動作無保証です。

CopyFieldCond関数(改)

function CopyFieldCond(clip clip, string logfile, bool "debug", bool "show_result", string "filter")
{
# デフォルト値などの指定
debug = default(debug, false) # debugのデフォルトはfalse
show_result = default(show_result, false) # debugのデフォルトはfalse
filter = default(filter, "") # filterのデフォルトは""
global FreezeThis = -1 # これは変更しないこと
global video = clip.SeparateFields() # フィールド分離

# filter指定があればfilterを適用してフィールド分離、さもなければvideo
filtered = (filter != "") ? Eval("clip." + filter).SeparateFields() : video

# debug==true&show_result==falseの場合: フレーム番号の表示
video = ((debug == true) && (show_result == false)) ?
\ video.ScriptClip("Subtitle(String(current_frame))") : video

# ScriptClip: フレームごとに評価
copy = video.ScriptClip("FreezeFrame(current_frame, current_frame, FreezeThis)")

# ConditionalFilter/ConditonalReader: FreezeThisが-1より大きいならcopyから取る
ConditionalFilter(video, copy, filtered, "FreezeThis", ">", "-1")
ConditionalReader(logfile, "FreezeThis", false)

# debug==true&show_result==falseの場合: フレーム番号の表示
((debug == true) && (show_result == true)) ?
\ ScriptClip("Subtitle(String(current_frame))") : last

# debug==trueの場合: 各フィールドを選択
even = ((debug == true) && (show_result == false)) ? video.SelectEven() :
\ ((debug == true) && (show_result == true)) ? SelectEven() : NOP
odd = ((debug == true) && (show_result == false)) ? video.SelectOdd() :
\ ((debug == true) && (show_result == true)) ? SelectOdd() : NOP

# debug==trueなら2画面分割表示、さもなければWeave
(debug == true) ? StackVertical(even, odd) : Weave()
}

使用例1: 通常モード(フィルタなし)

CopyFieldCond("CopyFields.txt")

前回と同じ。

!通常モード(フィルタなし)
大きい画像

使用例2: デバッグモード(フィルタなし/フィールド入れ替え前)

CopyFieldCond("CopyFields.txt", true) # または debug=true

前回と同じ。

!デバッグモード(フィルタなし/フィールド入れ替え前)
大きい画像

使用例3: デバッグモード(フィルタなし/フィールド入れ替え後)

CopyFieldCond("CopyFields.txt", true, true)

!デバッグモード(フィルタなし/フィールド入れ替え後)
大きい画像

「show_result=true」なら入れ替え後の状態を表示(debug=true 時のみ)。

使用例4: 通常モード(フィルタあり)

CopyFieldCond("CopyFields.txt", filter="GreyScale()") # filter=でフィルタ指定

!通常モード(フィルタあり)
大きい画像

使用例5: デバッグモード(フィルタあり/フィールド入れ替え前/フィルタ適用前)

CopyFieldCond("CopyFields.txt", true, filter="GreyScale()")

使用例2と同じ。

使用例6: デバッグモード(フィルタあり/フィールド入れ替え後/フィルタ適用後)

CopyFieldCond("CopyFields.txt", true, true, "GreyScale()")

!デバッグモード(フィルタあり/フィルタ適用後)
大きい画像


(追記)フィルタ(filter)はフィールド分離前のクリップに適用されます。

CopyFieldCond関数: ConditionalFilterを利用したCopyField拡張版

昨日あたりから、2ちゃんねる DTV 板の Avisynth スレッドで、「あるフィールドを別のコピーしたフィールドに入れ替える方法」に関する話題が出ていたので、HowToApplyFilterToManySingleFrames を使って CopyField 関数の拡張版を作ってみました。

HowToApplyFilterToManySingleFrames は、ConditionalFilterConditionalReader を使って、たくさんの単一フレームにフィルタをかけるテクニックです(AviSynth 開発者の sh0dan 氏が提案したもの)。にーやんのブログ :: HowToApplyFilterToManySingleFrames でも紹介していますので、参考にしてください。

CopyFieldCond関数

function CopyFieldCond(clip clip, string logfile, bool "debug")
{
# デフォルト値などの指定
global video = clip.SeparateFields() # フィールド分離
global FreezeThis = -1 # これは変更しないこと
debug = default(debug, false) # debugのデフォルトはfalse

# debug==trueの場合: フレーム番号の表示と各フィールドの選択
video = (debug == true) ? video.ScriptClip("Subtitle(String(current_frame))") : video
even = (debug == true) ? video.SelectEven() : NOP
odd = (debug == true) ? video.SelectOdd() : NOP

# ScriptClip: フレームごとに評価
copy = video.ScriptClip("FreezeFrame(current_frame, current_frame, FreezeThis)")

# ConditionalFilter/ConditonalReader: FreezeThisが-1より大きいならcopyから取る
ConditionalFilter(video, copy, video, "FreezeThis", ">", "-1")
ConditionalReader(logfile, "FreezeThis", false)

# debug==trueなら2画面分割表示、さもなければWeave
(debug == true) ? StackVertical(even, odd) : Weave()
}

使い方

1.ログファイル(例: CopyFields.txt)を作成。ログファイル内には、以下の 2 行を記述。

type int
default -1

2.CopyFieldCond を debug モードで使用。debug モードでは、フィールド別に上下 2 画面表示になり、各フィールドの番号が表示されます。

# 例
AviSource("foo.avi") # ソース
AssumeFrameBased().ComplementParity() # フィールドオーダーの指定
CopyFieldCond("CopyFields.txt", true) # debug=trueならdebugモード

3.「コピー先フィールドNo コピー元フィールドNo」の形式で、ログファイルに追記していく。

# 例
type int
default -1
2 4
4 6

4.debug モードを解除。

AviSource("foo.avi") # ソース
AssumeFrameBased().ComplementParity() # フィールドオーダーの指定
CopyFieldCond("CopyFields.txt") # または CopyFieldCond("CopyFields.txt", false)

使用例

  • オリジナル:

オリジナル

オリジナルの大きい画像

  • debug モード:

debug

  • フィルタ適用後:

!フィルタ適用後

フィルタ適用後の大きい画像

補足

簡単な動作テストはしていますが、いつものように動作無保証です。ログファイルを作成する手間などを考えると、あまり実用的とは言えないかもしれません。

範囲指定も可能です(正常に動作するかどうかは別にして)。範囲指定したい場合は、ログファイルに次のように記述します。

#  1~2フィールドを3フィールド目に置き換える
R 1 2 3

# 4~5フィールドを5~6フィールドに置き換える
I 4 5 5 6

オリジナルの CopyField 関数は、アーカイブ: 旧AviSynthのぺーじにあります。徐々にではありますが、このページにあるユーザー定義関数についても、AviSynth Wiki へ移行させていきたいと考えています。


(追記)にーやんのブログ :: CopyFieldCond関数(2): フィルタ適用前のフィールドと置換可能にに新しいバージョンを掲載しています。

Page 8 of 28: « 4 5 6 7 8 9 10 11 12 »