ひさびさにvsavsreaderを更新しました。
Gthubページ
バイナリ
VapourSynthもプラグインがそこそこ増えたりavisynth2.6用プラグインも使えるようになったりと色々変化してるので、今では誰が使ってるのかよくわかりませんが、今回から64bit版のdllも配布することにしました。
avisynth_c.hもavisynth+の最新のやつにしようと思ったんですが、ちょっと一部が壊れてるんですな、これが。
で、替わりにavisynth.hを使うことにしたので、C++で書き直しました。
2016年5月11日水曜日
2016年5月7日土曜日
VapourSource その2
2013年7月31日水曜日
VapourSource
Avisytnh2.6用プラグインの新作を公開。
VapourSource-20130731.zip
https://github.com/chikuzen/VapourSource/
VapourSourceはVSScriptの使い方を調べるために書いてみたAvisynthプラグインです。
VapourSynth script(r19以降)をAvisynthで読み込みます。
書き始めたのはr19のtest1のリリース直後でしたが、r19の正式公開までに二度のAPIブレークがありました。
やっぱこういうのって、実際に色々使ってみないと足りないところとかわからないものだよね。
VapourSource-20130731.zip
https://github.com/chikuzen/VapourSource/
VapourSourceはVSScriptの使い方を調べるために書いてみたAvisynthプラグインです。
VapourSynth script(r19以降)をAvisynthで読み込みます。
書き始めたのはr19のtest1のリリース直後でしたが、r19の正式公開までに二度のAPIブレークがありました。
やっぱこういうのって、実際に色々使ってみないと足りないところとかわからないものだよね。
vsrawsource その4
更新
vsrawsource-0.3.2.zip
https://github.com/chikuzen/vsrawsource/
* アルファ付ソース読み込み時のメモリリーク修正
* ファイル名に非ASCII文字が含まれる場合の対応
vsrawsource-0.3.2.zip
https://github.com/chikuzen/vsrawsource/
* アルファ付ソース読み込み時のメモリリーク修正
* ファイル名に非ASCII文字が含まれる場合の対応
vsimagereader その2
VS r19にあわせて更新
vsimagereader 0.2.1.zip
https://github.com/chikuzen/vsimagereader/
* アルファ読み込み時のメモリリーク修正
* ファイル名に非ASCII文字を含む場合の対応
* libpngを1.6.2に更新
vsimagereader 0.2.1.zip
https://github.com/chikuzen/vsimagereader/
* アルファ読み込み時のメモリリーク修正
* ファイル名に非ASCII文字を含む場合の対応
* libpngを1.6.2に更新
2013年7月14日日曜日
LutとLut2を改良した
VapourSynthのissueリストにこんなのがありました。
http://code.google.com/p/vapoursynth/issues/detail?id=52
「LutとLut2なんだけど、いちいちlook up tableを作ってから渡すのってメンドイから、生成用関数渡したらフィルタ側で作るようにしたいんだけど、誰かやらんかね?」
たしかにあれはめんどくさい。
特にLut2の場合は二重ループ必須だから、余計に書くのがめんどくさい。
というわけで、パッチ書いて送ったのが昨日mergeされました。
例:クリップのすべてのYの値を50下げたい場合。
例:二つのクリップを平均化して新しいクリップを作りたい。
functionの引数名はmasktools2のmt_lut/mt_lutxyに合わせて、xとyで固定です。
今までのようにlutをPython側で作って渡すこともできますが、lutとfunctionの両方を渡したらfunctionは無視されます。
次のリリース(多分r19test5)から使えるようになります。
http://code.google.com/p/vapoursynth/issues/detail?id=52
「LutとLut2なんだけど、いちいちlook up tableを作ってから渡すのってメンドイから、生成用関数渡したらフィルタ側で作るようにしたいんだけど、誰かやらんかね?」
たしかにあれはめんどくさい。
特にLut2の場合は二重ループ必須だから、余計に書くのがめんどくさい。
というわけで、パッチ書いて送ったのが昨日mergeされました。
core.std.Lut(clip:clip;planes:int[];lut:int[]:opt;function:func:opt;) core.std.Lut2(clips:clip[];planes:int[];lut:int[]:opt;function:func:opt;bits:int:opt;)
例:クリップのすべてのYの値を50下げたい場合。
clip = something #これまでの書き方 lut = [max(x - 50, 0) for x in range(2 ** clip.format.bits_per_sample)] clip = core.std.Lut(clip, lut=lut, planes=0) #改良後 def func(x): return max(x - 50, 0) clip = core.std.Lut(clip, function=func, planes=0) #または clip = core.std.Lut(clip, function=lambda x: max(x - 50, 0), planes=0)
例:二つのクリップを平均化して新しいクリップを作りたい。
clipx = something clipy = something #これまでの書き方 lut = [] for y in range(2 ** clipy.format.bits_per_sample): for x in range(2 ** clipx.format.bits_per_sample): lut.append((x + y) // 2) clip = core.std.Lut2([clipx, clipy], lut=lut, planes=[0, 1, 2]) #改良後 clip = core.std.Lut2([clipx, clipy], planes=[0, 1, 2], function=lambda x, y: (x + y) // 2)
functionの引数名はmasktools2のmt_lut/mt_lutxyに合わせて、xとyで固定です。
今までのようにlutをPython側で作って渡すこともできますが、lutとfunctionの両方を渡したらfunctionは無視されます。
次のリリース(多分r19test5)から使えるようになります。
2013年7月13日土曜日
MultiByteToWideCharToMultiByte その4
5.VapourSynthと文字エンコード
VapourSynthはr19でいろいろと大きな変更が入ることが見えたきたが、そのなかでも特に大きなものはVSScriptの導入である。r18までのVSはvapoursynth.dll(Linuxならlibvapoursynth.so)と、vapoursynth.pydの二つに分かれていた。
vapoursynth.dllはメイン処理を担当するC++で書かれたライブラリであり、vapoursynth.pydはvapoursynth.dllをPythonで操作するためのcython製モジュールである。
この構成は全てをPython上で実行するだけなら特に問題はなかったが、いざPython以外の外部プログラムと連携させようとするとどうやったらいいものなのかが非常にわかりにくかった。
それこそコードを全部読みこんでMyrsloik氏自身と同程度の理解を要求されるレベルなのだ。
Windowsならばvsvfwとvsfsがあるのでまだ良いが、LinuxやMacではどうしたらいいものかさっぱりだった(筆者自身、半年ほど前にavs2yuvのようなツールを作ってみようとして、半日で挫折している)。
VSScriptはこのような状況を改善し外部プログラムとの連携を容易にするために作られたAPIである。
現段階ではまだ未完成ではあるが、既にAvsPmod開発者のvdcrim氏などは非常に強い関心を寄せている。
LinuxやMacでもAvsPmodを使ってVS用スクリプトを編集できるようになる日もそう遠くはないかもしれない。
さて、VSScriptのコードを追っかけてみると、vapoursynth.pydを構成するコード(vapoursynth.pyx)に次のような記述が追加されていた。
cdef public api int vpy_evaluateScript(VPYScriptExport *se, const char *script, const char *errorFilename) nogil: ... comp = compile(script.decode('utf-8'), errorFilename.decode('utf-8'), 'exec') exec(comp) in evaldict ...このコードから察するに、VSScriptを使ってメモリ上に展開されたスクリプトのバイト列をPythonに渡すと、Pythonはバイト列をUTF-8でデコードしてユニコード文字列に変換し、しかる後にバイトコードにコンパイルすることになる。
これは事実上、r19以降はスクリプトの記述はUTF-8(BOMなし)で統一されるというこどだ。
そもそもPythonでは、スクリプトはASCII文字のみで書くか、それがダメならばUTF-8にすることとPEP8で決められている。
これは絶対の掟ではないが、今時Shift_jisやEUC-JPでPython書きたがるのはただの馬鹿だ(過去に書かれた負債のため嫌々書くならば理解できる)。
また、Avisynthでも前回で述べたようなファイル名の問題等を解決するため、スクリプトはUTF-8で書くように出来ないかとか、ユニコード対応ソフトウェアに改修しようといった議論が行われたことがあった(結局互換性重視の方針により実現はしなかったが)。
この変更は、大いに歓迎すべきものだろう。
VapourSynthはまだまだ若いプロジェクトであり、プラグイン作者もそれほど多くはない。
しかもほぼ全員が#darkhold(IRCチャンネル)に常駐しているため、舵取りも容易な状態である。
あきらかに自分のコードの書き方が悪かっただけなのに、プラグインの互換性を損ねたと駄々をこねる困った子も今のところはいない。
変更を追いかけるのはキツイかもしれないが、あと1年くらいは色々と変わるのではないかと思う。
2013年6月30日日曜日
VapourSynth r19(テスト版)
VapourSynthがr19で色々変わるようです。
とりあえずテスト版でのr18までとの違いを簡単にまとめておきます。
*この記事は現時点でのテスト版を元に書かれており、今後の展開によっては特に断りなく修正されます*
具体的には、いままでこう書いていたのが
これにより、いままではユーザー定義のクラスや関数に引数でCoreオブジェクトを渡す必要がありましたが、今後はなくなりました(必要なところでget_core()すれば、どこで呼んでもプロセス内で共通のものが使われます)。
そしてx264等に入力するにはvspipe(インストールすればおまけでついてきます)を使います。
get_plugins()は、list_functions()では文字列として取得できた情報がdictとして取得出来ます。
get_functions()は、もう少しマシです。
状況によってlist_functions()と使い分けましょう。
スクリプトはPythonに渡された後、UTF-8決め打ちで一旦Pythonのバイトコードにコンパイルされます。
このためUTF-8以外で日本語用文字等を使っている場合は、内部で文字化けを起こしてデコードエラーになります。
これまでは日本語ファイル名等を扱う場合はCP932で保存したほうがよかったですが、今後はUTF-8で保存するようにしましょう(もちろんBOMなしで)。
そしてもうひとつ大事なことですが、Windowsの場合、日本語ファイル名が扱えるかどうかはプラグインの実装次第となりました。
とりあえず見た感じ、avisource.dllはちゃんと対応してたけどd2vsourceはどうも現状ではダメっぽいですな。
ちなみに拙作のソースフィルタ群も軒並みダメです。ああ、直すのメンドイorz
とりあえずテスト版でのr18までとの違いを簡単にまとめておきます。
*この記事は現時点でのテスト版を元に書かれており、今後の展開によっては特に断りなく修正されます*
変更1:Coreクラスがsingletonになり、Core()がget_core()になった。
singletonは、デザインパターン(オブジェクト指向プログラミングのノウハウ)の一種ですが説明はメンドイので省きます。具体的には、いままでこう書いていたのが
import vapoursynth as vs core = vs.Core(threads=2, add_cache=False, accept_lowercase=True) c0 = core.std.BlankClip(format=vs.YUV420P8) c1 = core.std.BlankClip(format=vs.YUV422P9) c2 = core.std.BlankClip(format=vs.YUV444P16)こうなります。
import vapoursynth as vs core = vs.get_core(threads=2, add_cache=False, accept_lowercase=True) c0 = core.std.BlankClip(format=vs.YUV420P8) c1 = core.std.BlankClip(format=vs.YUV422P9) c2 = core.std.BlankClip(format=vs.YUV444P16)もっと言えばこう書いてもいいです(あまりオススメはしませんが)。
import vapoursynth as vs c0 = vs.get_core(threads=2, add_cache=False, accept_lowercase=True).std.BlankClip(format=vs.YUV420P8) c1 = vs.get_core().std.BlankClip(format=vs.YUV422P9) c2 = vs.get_core().std.BlankClip(format=vs.YUV444P16)これまでは
Note that the loaded plugins are per core instance and not global so if you do create several processing cores at once (not recommended) you will have to load the plugins you want for each core.と、Coreオブジェクトは何個でも作れましたが、r19以降は1プロセスに付き1個しか作れなくなりました(最初にget_core()した時に作られます)。
これにより、いままではユーザー定義のクラスや関数に引数でCoreオブジェクトを渡す必要がありましたが、今後はなくなりました(必要なところでget_core()すれば、どこで呼んでもプロセス内で共通のものが使われます)。
変更2:output()がなくなり、set_output()とvspipe.exeが出来た。またlastというクリップ名に特別な意味がなくなった。
これまでは#sample_pre.vpy import vapoursynth as vs core = vs.Core() last = core.std.BlankClip(format=vs.YUV420P8) if __name__ == '__main__': last.output(sys.stdout, y4m=True)このスクリプトをコマンドラインで使う場合は
$ python sample_pre.vpy | x264 - --demuxer y4m -o out.264という感じで実行し、VirtualDub等で読みこめばlastクリップがプレビュー出来ましたが今後はこうなります。
#sample_r19.vpy import vapoursynth as vs core = vs.get_core() clip = core.std.BlankClip(format=vs.YUV420P8) clip.set_output()このスクリプトをVirtualDub等VfWを利用するソフトウェアで読みこめば、set_output()したクリップ(変数名はなんでもよい)がプレビューできます。
そしてx264等に入力するにはvspipe(インストールすればおまけでついてきます)を使います。
$ vspipe sample_r19.vpy - -y4m | x264 - --demuxer y4m -o out.264output()がなくなってしまったので、これまでのようにスクリプト内でsubprocessつかってコマンド実行が困難(出来ないことはないけど)になりましたが、かわりに外部プログラムから使うためのAPIがかなり整備されたので、そのうちx264等にAVS入力同様、VS入力が実装されるでしょう。
変更3:get_plugins()とget_functions()が追加された。
r18までは、core.list_functions()で現在使えるすべてのフィルタ及びその引数の一覧を文字列として取得出来ましたが、r19でさらにcore.get_plugins()とcore.***.get_functions()が追加されました。get_plugins()は、list_functions()では文字列として取得できた情報がdictとして取得出来ます。
>>> import vapoursynth as vs >>> vs.get_core().get_plugins() {'com.vapoursynth.resize': {'namespace': 'resize', 'identifier': 'com.vapoursynt h.resize', 'functions': {'Bicubic': 'clip:clip;width:int:opt;height:int:opt;form at:int:opt;yuvrange:int:opt;', 'Sinc': 'clip:clip;width:int:opt;height:int:opt;f ormat:int:opt;yuvrange:int:opt;', 'Bilinear': 'clip:clip;width:int:opt;height:in t:opt;format:int:opt;yuvrange:int:opt;', 'Spline': 'clip:clip;width:int:opt;heig ht:int:opt;format:int:opt;yuvrange:int:opt;', 'Gauss': 'clip:clip;width:int:opt; height:int:opt;format:int:opt;yuvrange:int:opt;', 'Lanczos': 'clip:clip;width:in t:opt;height:int:opt;format:int:opt;yuvrange:int:opt;', 'Point': 'clip:clip;widt h:int:opt;height:int:opt;format:int:opt;yuvrange:int:opt;'}, 'name': 'VapourSynt h Resize'}, 'com.vapoursynth.avisynth': {'namespace': 'avs', 'identifier': 'com. vapoursynth.avisynth', 'functions': {'LoadPlugin': 'path:data;'}, 'name': 'Vapou rSynth Avisynth Compatibility'}, 'com.vapoursynth.std': {'namespace': 'std', 'id entifier': 'com.vapoursynth.std', 'functions': {'Loop': 'clip:clip;times:int:opt ;', 'PropToClip': 'clip:clip;prop:data:opt;', 'StackVertical': 'clips:clip[];', 'Transpose': 'clip:clip;', 'FlipVertical': 'clip:clip;', 'PEMVerifier': 'clip:cl ip;upper:int[]:opt;lower:int[]:opt;', 'Splice': 'clips:clip[];mismatch:int:opt;' , 'ClipToProp': 'clip:clip;mclip:clip;prop:data:opt;', 'PlaneDifference': 'clips :clip[];plane:int;prop:data:opt;', 'Lut2': 'clips:clip[];lut:int[];planes:int[]; bits:int:opt;', 'AssumeFPS': 'clip:clip;src:clip:opt;fpsnum:int:opt;fpsden:int:o pt;', 'Cache': 'clip:clip;size:int:opt;fixed:int:opt;', 'FlipHorizontal': 'clip: clip;', 'Expr': 'clips:clip[];expr:data[];format:int:opt;', 'LoadPlugin': 'path: data;forcens:data:opt;', 'MaskedMerge': 'clips:clip[];mask:clip;planes:int[]:opt ;first_plane:int:opt;', 'ShufflePlanes': 'clips:clip[];planes:int[];format:int;' , 'CropRel': 'clip:clip;left:int:opt;right:int:opt;top:int:opt;bottom:int:opt;', 'SeparateFields': 'clip:clip;tff:int;', 'Reverse': 'clip:clip;', 'StackHorizont al': 'clips:clip[];', 'Trim': 'clip:clip;first:int:opt;last:int:opt;length:int:o pt;', 'SelectEvery': 'clip:clip;cycle:int;offsets:int[];', 'Interleave': 'clips: clip[];extend:int:opt;mismatch:int:opt;', 'ModifyFrame': 'clips:clip[];selector: func;', 'Turn180': 'clip:clip;', 'Lut': 'clip:clip;lut:int[];planes:int[];', 'Pl aneAverage': 'clip:clip;plane:int;prop:data:opt;', 'BlankClip': 'clip:clip:opt;w idth:int:opt;height:int:opt;format:int:opt;length:int:opt;fpsnum:int:opt;fpsden: int:opt;color:float[]:opt;', 'DoubleWeave': 'clip:clip;tff:int;', 'CropAbs': 'cl ip:clip;width:int;height:int;x:int:opt;y:int:opt;', 'SelectClip': 'clips:clip[]; src:clip[];selector:func;', 'AddBorders': 'clip:clip;left:int:opt;right:int:opt; top:int:opt;bottom:int:opt;color:float[]:opt;', 'Merge': 'clips:clip[];weight:fl oat[]:opt;'}, 'name': 'VapourSynth Core Functions'}}はい、dictのなかにさらにdictがたくさんあって、なにがなにやらさっぱりわかりません……。
get_functions()は、もう少しマシです。
>>> vs.get_core().std.get_functions() {'Loop': 'clip:clip;times:int:opt;', 'PropToClip': 'clip:clip;prop:data:opt;', ' StackVertical': 'clips:clip[];', 'Transpose': 'clip:clip;', 'FlipVertical': 'cli p:clip;', 'PEMVerifier': 'clip:clip;upper:int[]:opt;lower:int[]:opt;', 'Splice': 'clips:clip[];mismatch:int:opt;', 'ClipToProp': 'clip:clip;mclip:clip;prop:data :opt;', 'PlaneDifference': 'clips:clip[];plane:int;prop:data:opt;', 'Lut2': 'cli ps:clip[];lut:int[];planes:int[];bits:int:opt;', 'AssumeFPS': 'clip:clip;src:cli p:opt;fpsnum:int:opt;fpsden:int:opt;', 'Cache': 'clip:clip;size:int:opt;fixed:in t:opt;', 'FlipHorizontal': 'clip:clip;', 'Expr': 'clips:clip[];expr:data[];forma t:int:opt;', 'LoadPlugin': 'path:data;forcens:data:opt;', 'MaskedMerge': 'clips: clip[];mask:clip;planes:int[]:opt;first_plane:int:opt;', 'ShufflePlanes': 'clips :clip[];planes:int[];format:int;', 'CropRel': 'clip:clip;left:int:opt;right:int: opt;top:int:opt;bottom:int:opt;', 'SeparateFields': 'clip:clip;tff:int;', 'Rever se': 'clip:clip;', 'StackHorizontal': 'clips:clip[];', 'Trim': 'clip:clip;first: int:opt;last:int:opt;length:int:opt;', 'SelectEvery': 'clip:clip;cycle:int;offse ts:int[];', 'Interleave': 'clips:clip[];extend:int:opt;mismatch:int:opt;', 'Modi fyFrame': 'clips:clip[];selector:func;', 'Turn180': 'clip:clip;', 'Lut': 'clip:c lip;lut:int[];planes:int[];', 'PlaneAverage': 'clip:clip;plane:int;prop:data:opt ;', 'BlankClip': 'clip:clip:opt;width:int:opt;height:int:opt;format:int:opt;leng th:int:opt;fpsnum:int:opt;fpsden:int:opt;color:float[]:opt;', 'DoubleWeave': 'cl ip:clip;tff:int;', 'CropAbs': 'clip:clip;width:int;height:int;x:int:opt;y:int:op t;', 'SelectClip': 'clips:clip[];src:clip[];selector:func;', 'AddBorders': 'clip :clip;left:int:opt;right:int:opt;top:int:opt;bottom:int:opt;color:float[]:opt;', 'Merge': 'clips:clip[];weight:float[]:opt;'}たとえば「Lut2の引数ってどんな感じだったっけ?」と思ったら、
>>> core.std.get_functions()['Lut2'] 'clips:clip[];lut:int[];planes:int[];bits:int:opt;'このようにcore.name.get_functions()['フィルタ名']とタイプすれば確認できます。
状況によってlist_functions()と使い分けましょう。
変更4:スクリプトに使用する文字セットをUTF-8で統一
VSScript(外部アプリケーションとの連携用API)の導入に伴い、スクリプトはUTF-8で書くように統一されたようです。スクリプトはPythonに渡された後、UTF-8決め打ちで一旦Pythonのバイトコードにコンパイルされます。
このためUTF-8以外で日本語用文字等を使っている場合は、内部で文字化けを起こしてデコードエラーになります。
これまでは日本語ファイル名等を扱う場合はCP932で保存したほうがよかったですが、今後はUTF-8で保存するようにしましょう(もちろんBOMなしで)。
そしてもうひとつ大事なことですが、Windowsの場合、日本語ファイル名が扱えるかどうかはプラグインの実装次第となりました。
とりあえず見た感じ、avisource.dllはちゃんと対応してたけどd2vsourceはどうも現状ではダメっぽいですな。
ちなみに拙作のソースフィルタ群も軒並みダメです。ああ、直すのメンドイorz
2013年2月22日金曜日
EasyVFR for VapourSynth
なんかEasyVFRのVS版を欲しがってる人たちがいるようなので、やってみました。
easyvfr.py
使い方
まずeasyvfr.pyをPython3/Lib/site-packagesに置きます。
あとはこんな感じ
timecodeのフォーマットはv2のみです。
chapterファイルのエンコーディングはUTF-8になります。
追記(20160402):最近のVapourSynthにあわせてちょっと書き直しました。
easyvfr.py
使い方
まずeasyvfr.pyをPython3/Lib/site-packagesに置きます。
あとはこんな感じ
#!/usr/bin/env python3 import vapoursynth as vs import easyvfr vs.get_core().std.LoadPlugin('/path/to/d2vsource.dll') d2vsrc = vs.get_core().d2v.Source def ivtc(clip, offset, cycle=10, tff=True): sf = clip.std.SeparateFields(tff) dw = sf.std.DoubleWeave(tff) return dw.std.SelectEvery(cycle, offset) def bob(clip, tff=True): sf = clip.std.SeparateFields(tff) return sf.resize.Bicubic(height=clip.height) src = d2vsrc('/path/to/the/source.d2v') # 適当にTrimして、デインタレ/IVTC/Bobしたりする av0 = ivtc(src[100: 2000], [0, 3, 5, 8]) # 24fps op0 = ivtc(src[2000: 3456], [0, 2, 5, 7]) # 24fps op1 = bob(src[3456: 3501]) # 60fps op2 = ivtc(src[3501: 6541], [1, 4, 6, 9]) # 24fps a00 = ivtc(src[8000: 12000], [1, 3, 6, 8]) #24fps a01 = src[12000: 12151] # 30fps a02 = ivtc(src[12151: 20000], [0, 2, 5, 7]) #24fps # av0, op0, a00の先頭フレームにチャプタを打ち、IDRフレームにしたい av0 = easyvfr.ChapterClip(av0, 'アバンA') op0 = easyvfr.ChapterClip(op0, 'OP') a00 = easyvfr.ChapterClip(a00, 'Aパート') # 各クリップをひとつのリストにまとめる clips = [av0, op0, op1, op2, a00, a01, a02] ''' 22行目以降はこういう書き方もあり cc = easyvfr.ChapterClip clips = [ cc(ivtc(src[100: 2000], [0, 3, 5, 8]), 'アバンA'), cc(ivtc(src[2000: 3456], [0, 2, 5, 7]), 'OP'), bob(src[3456: 3501]), ivtc(src[3501: 6541], [1, 4, 6, 9]), cc(ivtc(src[8000: 12000], [1, 3, 6, 8]), 'Aパート'), src[12000: 12151], ivtc(src[12151: 20000], [0, 2, 5, 7]), ] ''' # timecodeファイル、chapterファイル、x264用QPファイルの出力、及び各クリップを結合 vfr = easyvfr.EasyVFR(clips) vfr.write_tcq('/path/to/the/files') #vfr.write_timecode('/path/to/the/timecode.txt') #vfr.write_chapter('/path/to/the/chapter.txt') #vfr.write_qpfile('/path/to/the/qpfile.txt') vfr.splice_clips().set_output()
timecodeのフォーマットはv2のみです。
chapterファイルのエンコーディングはUTF-8になります。
追記(20160402):最近のVapourSynthにあわせてちょっと書き直しました。
2013年1月17日木曜日
GenericFilters その5
更新しました。
GenericFilters-0.4.1.7z
https://github.com/chikuzen/GenericFilters
* Convolution/ConvolutionHV/Blur:フレームが16bitだった場合の処理を修正
uint16_tとint16_tの乗算が出来る組み込み命令がないのって面倒すぎると思うんですが…。
GenericFilters-0.4.1.7z
https://github.com/chikuzen/GenericFilters
* Convolution/ConvolutionHV/Blur:フレームが16bitだった場合の処理を修正
uint16_tとint16_tの乗算が出来る組み込み命令がないのって面倒すぎると思うんですが…。
2013年1月13日日曜日
GenericFilters その4
更新しました。
GenericFilters-0.4.0.7z
https://github.com/chikuzen/GenericFilters
* 新関数'Binarize2'を追加
Binarize2はBinarizeと同じくクリップを二値化するフィルタですが、閾値によって分けるのではなく、Sierra-2-4Aという誤差拡散法(いわゆるディザリングで使われるアルゴリズム)の一種によって処理します。
具体的には、これが
こうなります。
簡単に出来そうだったからやってみただけですので、実用性とかは気にしてはいけません。
GenericFilters-0.4.0.7z
https://github.com/chikuzen/GenericFilters
* 新関数'Binarize2'を追加
Binarize2はBinarizeと同じくクリップを二値化するフィルタですが、閾値によって分けるのではなく、Sierra-2-4Aという誤差拡散法(いわゆるディザリングで使われるアルゴリズム)の一種によって処理します。
具体的には、これが
こうなります。
簡単に出来そうだったからやってみただけですので、実用性とかは気にしてはいけません。
2013年1月12日土曜日
vsimagereader その2
更新しました。
vsimagereader-0.2.0.7z
https://github.com/chikuzen/vsimagereader
* VapourSynthのAPIバージョンを3に更新
* アルファチャンネルの読み込みに対応/'alpha'オプションの追加
* 幅/高さ/出力フォーマットがバラバラな画像の読み込みに対応
以前も書きましたが、VapourSynthはavisynth等と違って各フレームの解像度やフォーマットが異なっていても扱えるようになっています。
今回の更新で、「解像度や保存形式がバラバラなものを一度に読み込んで、解像度を揃えて出力」なんてことが出来るようになりました。
一度にプラグイン4つも更新したので、大変疲れました。
vsimagereader-0.2.0.7z
https://github.com/chikuzen/vsimagereader
* VapourSynthのAPIバージョンを3に更新
* アルファチャンネルの読み込みに対応/'alpha'オプションの追加
* 幅/高さ/出力フォーマットがバラバラな画像の読み込みに対応
以前も書きましたが、VapourSynthはavisynth等と違って各フレームの解像度やフォーマットが異なっていても扱えるようになっています。
今回の更新で、「解像度や保存形式がバラバラなものを一度に読み込んで、解像度を揃えて出力」なんてことが出来るようになりました。
一度にプラグイン4つも更新したので、大変疲れました。
GenericFilters その3
更新しました。
GenericFilters-0.3.0.7z
https://github.com/chikuzen/GenericFilters
* 関数に'Blur'を追加。
普通の3x3のぼかしフィルタです。
単なるConvolution()のaliasですな。
GenericFilters-0.3.0.7z
https://github.com/chikuzen/GenericFilters
* 関数に'Blur'を追加。
普通の3x3のぼかしフィルタです。
単なるConvolution()のaliasですな。
vsavsreader その7
更新しました。
vsavsreader-0.1.0.7z
https://github.com/chikuzen/VS_AvsReader
* アルファチャンネルのサポート方法を変更
* 'alpha'オプションを追加
今回からこれにもバージョン番号を付けることにしました。
vsavsreader-0.1.0.7z
https://github.com/chikuzen/VS_AvsReader
* アルファチャンネルのサポート方法を変更
* 'alpha'オプションを追加
今回からこれにもバージョン番号を付けることにしました。
2013年1月6日日曜日
GenericFilters その2
更新しました。
GenericFilters-0.2.2.7x
https://github.com/chikuzen/GenericFilters
* Sobel/Prewitt: rshiftオプションを追加
* いろいろバグフィックス
ちょっとだけ内部処理の説明を書いておきます。
画像処理を行う場合、各ピクセルの値は、そのフォーマットによって扱える範囲が限定されます。
8bitフォーマットであればピクセルは0~255、16bitフォーマットであれば0~65535の範囲内に存在する値をとらなければなりません。
しかし、いろいろな計算を行った場合、出力値はしばしばこの下限/上限を超えることがあります。
この対処法として、最終的に出力する前に次のようなことが行われます。
1. 単純に、下限値/上限値で切り捨て。
2. 絶対値で扱う(値が下限値の0を超えた場合の対策)
3. 値を割ることで範囲内に収める(値が上限を超えた場合の対策)
GenericFiltersのConvolution(HV)はsaturateオプションをFalseにすれば処理2(絶対値化)が行われます。
divisorオプションによって処理3が行われます。
最後に処理1(値の切り捨て)を行うことで、出力値は範囲内に収められます。
さて、SobelとPrewittも多少工程が複雑であることを除けば単なる畳み込み演算なのですが、これらはエッジ検出に特化したアルゴリズムなので、自由度はConvolutionよりも低くなっています。
まず、処理2の絶対値化は必ず行われます。
そして、処理3の割る数は(主に処理速度の都合により)2の整数乗(rshiftで指定)に限定されています。
では今回はこのへんで
GenericFilters-0.2.2.7x
https://github.com/chikuzen/GenericFilters
* Sobel/Prewitt: rshiftオプションを追加
* いろいろバグフィックス
ちょっとだけ内部処理の説明を書いておきます。
画像処理を行う場合、各ピクセルの値は、そのフォーマットによって扱える範囲が限定されます。
8bitフォーマットであればピクセルは0~255、16bitフォーマットであれば0~65535の範囲内に存在する値をとらなければなりません。
しかし、いろいろな計算を行った場合、出力値はしばしばこの下限/上限を超えることがあります。
この対処法として、最終的に出力する前に次のようなことが行われます。
1. 単純に、下限値/上限値で切り捨て。
2. 絶対値で扱う(値が下限値の0を超えた場合の対策)
3. 値を割ることで範囲内に収める(値が上限を超えた場合の対策)
GenericFiltersのConvolution(HV)はsaturateオプションをFalseにすれば処理2(絶対値化)が行われます。
divisorオプションによって処理3が行われます。
最後に処理1(値の切り捨て)を行うことで、出力値は範囲内に収められます。
さて、SobelとPrewittも多少工程が複雑であることを除けば単なる畳み込み演算なのですが、これらはエッジ検出に特化したアルゴリズムなので、自由度はConvolutionよりも低くなっています。
まず、処理2の絶対値化は必ず行われます。
そして、処理3の割る数は(主に処理速度の都合により)2の整数乗(rshiftで指定)に限定されています。
では今回はこのへんで
2013年1月5日土曜日
GenericFilters
VapourSynth用のプラグインを書きました
GenericFilters-0.2.0.7z
https://github.com/chikuzen/GenericFilters
名前のとおり、デジタル画像処理で一般的によく知られているフィルタ(畳み込み演算、二値化、膨張、収縮、メディアン等)の詰め合わせです。
avisynthで言えばmasktoolsにあたります。
VapourSynthは標準フィルタとして画像合成用のLut/Lut2/Expr/MaskedMergeを装備しているので、これらと組み合わせれば大幅に出来ることが増えます。
これを書くためにまず試作品としてConvo2Dを書き、ついでこれを書いたわけですが、最初はC言語オンリーだったので遅いったらありません。
とりあえず色々チュ-ニングしては見ましたが、やはり根本的な解決には至らず、今回とうとうSIMD(SSE2)に手を出してしまいました。
intrinsicなので多少はラクなのでしょうが、いや疲れたのなんのって、頭の中がバイト列の組み合わせパズルで占領されてしまいました。
UtVideoの梅澤さんに「よくアセンブラとか書けますね」って聞いてみたら、intrinsicよりMASMやNASMのほうが彼にはとってはラクなのだとか…怖いなぁ。
GenericFilters-0.2.0.7z
https://github.com/chikuzen/GenericFilters
名前のとおり、デジタル画像処理で一般的によく知られているフィルタ(畳み込み演算、二値化、膨張、収縮、メディアン等)の詰め合わせです。
avisynthで言えばmasktoolsにあたります。
VapourSynthは標準フィルタとして画像合成用のLut/Lut2/Expr/MaskedMergeを装備しているので、これらと組み合わせれば大幅に出来ることが増えます。
これを書くためにまず試作品としてConvo2Dを書き、ついでこれを書いたわけですが、最初はC言語オンリーだったので遅いったらありません。
とりあえず色々チュ-ニングしては見ましたが、やはり根本的な解決には至らず、今回とうとうSIMD(SSE2)に手を出してしまいました。
intrinsicなので多少はラクなのでしょうが、いや疲れたのなんのって、頭の中がバイト列の組み合わせパズルで占領されてしまいました。
UtVideoの梅澤さんに「よくアセンブラとか書けますね」って聞いてみたら、intrinsicよりMASMやNASMのほうが彼にはとってはラクなのだとか…怖いなぁ。
2012年12月14日金曜日
convo2d その3
更新しました。
convo2d-0.1.3.7z
https://github.com/chikuzen/convo2d
* 5x5マトリクスが効かなくなっていたのを修正
* ちょっと高速化
追記:
本フィルタはGenericFiltersに統合されましたので、開発/配布を終了しました。
今後はGenericFiltersをお使い下さい。
convo2d-0.1.3.7z
https://github.com/chikuzen/convo2d
* 5x5マトリクスが効かなくなっていたのを修正
* ちょっと高速化
追記:
本フィルタはGenericFiltersに統合されましたので、開発/配布を終了しました。
今後はGenericFiltersをお使い下さい。
2012年12月13日木曜日
convo2d その2
更新しました。
convo2d-0.1.2-2.7z
https://github.com/chikuzen/convo2d
・入力クリップの各フレームのフォーマットが一定でない場合、及びフレームのサンプルがfloatの場合の処理を追加。
VapourSynthはAviSynthと違って、クリップの各フレームの解像度や色空間がすべて同じであるとは限りません。フレーム0は720x480のYUV420P8なのに、フレーム1は1920x1080のRGBということもありえます。
そこらへんを忘れていたので処理を追加しました。
さて、せっかく書いたプラグインなので、なにかやってみようと思います。
とりあえず簡単そうなやつで、アンシャープマスクでもいってみましょうか。
一般にアンシャープマスクと呼ばれる処理は次のようなことを行います。
1. 元画像と、それをぼかした画像を用意する。
2. 元画像とぼかした画像の差分をとる。
3. 差分をそのまま、もしくは何かしら手を加えてから元画像にかぶせる。
この結果として、なんかコントラストがきつくなったような画像が得られます。
まずは元画像を読み込みます。
差分のクリップに閾値をつけたり、値に倍率を掛けたり、あるいはぼかし方を変えてみたりしてみるのも面白いかもしれません。
しかし、これではconvo2dの使い方というよりLut2の使い方みたいですな…。
追記:
0.1.2では修正が不十分だったので、0.1.2-2に更新しました。
convo2d-0.1.2-2.7z
https://github.com/chikuzen/convo2d
・入力クリップの各フレームのフォーマットが一定でない場合、及びフレームのサンプルがfloatの場合の処理を追加。
VapourSynthはAviSynthと違って、クリップの各フレームの解像度や色空間がすべて同じであるとは限りません。フレーム0は720x480のYUV420P8なのに、フレーム1は1920x1080のRGBということもありえます。
そこらへんを忘れていたので処理を追加しました。
さて、せっかく書いたプラグインなので、なにかやってみようと思います。
とりあえず簡単そうなやつで、アンシャープマスクでもいってみましょうか。
一般にアンシャープマスクと呼ばれる処理は次のようなことを行います。
1. 元画像と、それをぼかした画像を用意する。
2. 元画像とぼかした画像の差分をとる。
3. 差分をそのまま、もしくは何かしら手を加えてから元画像にかぶせる。
この結果として、なんかコントラストがきつくなったような画像が得られます。
まずは元画像を読み込みます。
import vapoursynth core = vapoursynth.Core() core.std.LoadPlugin('/path/to/vsimagereader.dll') clip = core.imgr.Read('/path/to/lena.bmp')次にぼかした画像を用意する。
core.std.LoadPlugin('/path/to/convo2d.dll') blur = core.convo2d.Convolution(clip, [1, 1, 1, 1, 1, 1, 1, 1, 1])二つの画像の差分をとります。
def clamp(value): if value < 1: return 0 if value > 254: return 255 return value lut_diff = [] for y in range(256): for x in range(256): lut_diff.append(clamp(x - y + 128)) diff = core.std.Lut2([clip, blur], lut_diff, [0, 1, 2])とりあえず今回はそのままかぶせます。
lut = [] for y in range(256): for x in range(256): lut.append(clamp(x + y - 128)) result = core.std.Lut2([clip, diff], lut, [0, 1, 2])では元画像と結果を並べてみましょう。
last = core.std.StackHorizontal([clip, result])
差分のクリップに閾値をつけたり、値に倍率を掛けたり、あるいはぼかし方を変えてみたりしてみるのも面白いかもしれません。
しかし、これではconvo2dの使い方というよりLut2の使い方みたいですな…。
追記:
0.1.2では修正が不十分だったので、0.1.2-2に更新しました。
2012年12月10日月曜日
convo2d
VapourSynth用に空間convolutionフィルタを書きました。
convo2d-0.1.1.7z
https://github.com/chikuzen/convo2d
マトリクスを変更することによって様々な処理が行えることから、カスタムフィルタと呼ばれたりするものです。
単純な平滑化/先鋭化からエッジ検出、エンボス、ピクセルシフトといろいろ出来るので、ググってマトリクスを探してみるのもいいかもしれません。
追記:
メモリリーク、及び画像端の処理が間違ってたのを直しました。
convo2d-0.1.1.7z
https://github.com/chikuzen/convo2d
マトリクスを変更することによって様々な処理が行えることから、カスタムフィルタと呼ばれたりするものです。
単純な平滑化/先鋭化からエッジ検出、エンボス、ピクセルシフトといろいろ出来るので、ググってマトリクスを探してみるのもいいかもしれません。
追記:
メモリリーク、及び画像端の処理が間違ってたのを直しました。
登録:
投稿 (Atom)