2008年5月5日月曜日

番外 縞縞アプコンをきれいにして観よう サンプル

ココの話は文章だけではわけがわからないのでサンプル

実験画像 720 x 486のSDサイズ

この画像を

1:1920 x1080に直接拡大

2:インタレ素材と仮定してフィールドに分けフィールド単位に拡大

3:2で作った縞だらけのアプコン映像をResizeIntrで縞を解消
をやってみたもの

リサイズはBilinear



1:1920 x1080に直接拡大

フレーム単位の拡大処理と同等。拡大によるぼけやリサイズ起因のジャギが見えますが、きれいに拡大されています。
左上の部分がスクイーズアプコン相当の絵です。右および下は劣化を見やすいように拡大したもの。


2:インタレ素材と仮定してフィールドに分けフィールド単位に拡大

見事な縞画質になります。実際これに近い画質で放送されているアニメ(に限らないけれど)も多々あるからこまりもの。
これも左上部分がアプコン相当の部分です。
2の画像は上のSDサイズを拡大したもので1の画像から作成したものではありません。


3:2で作った縞だらけのアプコン映像をResizeIntrで縞を解消

これは2の画像を処理したものです。縞縞状態でも、元絵の縦方向の情報は十分残っていて、それをうまく取り出せばこの程度までは回復できる。多少ぶれもありまだ調整余地もありますが、縞の排除効果は大きいといえる差でしょう。
処理したのは左上の部分であり、右や下の拡大部分は、2の拡大部分を処理した結果ではありません。

番外 縞縞アプコンをきれいにして観よう

この記事の方法は古い手法です。実際に自分でチャレンジする場合は新しいDestripeを使うことを推奨。


ドルアーガで書いた、フィールド単位ズームの縞を軽減してきれいにする話に身近なところから反響があったので、このブログとしては方向性が違うけれど補足。自分で動画エンコードするなんてアホだと思う人には何の役に立たない情報なのでスルーしましょう。

実験段階から処理速度も考えavisynthプラグインの方向で進めているものの時間がかかるので、avisynthの組み込み関数だけで実現する方法で説明します。理想的だと思っているわけでもなく、単純に思いついてわかりやすいアプローチだっただけで無駄も多い状態。

AviSynth 2.5.7で確認。

最終目的は縞縞をなくして綺麗な線に戻すことですが、その鍵となる処理は

 拡大イメージを縦方向のボケが極力少なくなるように元の縦解像度サイズまで縮小すること

です。そのために単純なリサイズでなく奇妙な操作を混ぜつつ縮小します。関数は二つ。

function FCrop(clip clip, int "l", int "t", int "r", int "b") {
w = clip.width()
h = clip.height()
return clip.Crop(l, t, w - l - r, h - t - b)
}

function ResizeIntr(clip clip) {
hh = 360
rt = 6
sft1 = 3
sft2 = 2

w = clip.Width()
h = clip.Height()

c = clip.Separatefields().BicubicResize(w, hh * rt)
ve = c.SelectEven().AddBorders(0, sft1, 0, 0).FCrop(0, 0, 0, sft1)
vo = c.SelectOdd().AddBorders(0, sft2, 0, 0).FCrop(0, 0, 0, sft2)
return Interleave(ve, vo).PointResize(w, hh).Weave()
}


アニメ放送状況を考えると利用可能な映像ソースは1080i限定です。
原理的にフィールド順は関係しませんが、ここではTopFirstで記載します。

ResizeIntr()

とすると横幅はそのまま縦が720になります。この状態ではインターレース維持状態ですので、
この処理の後にインタレ解除、必要なリサイズを行ないます。

何をやってるかは、ソースとしてすべて書いてあるわけですが、一応説明。

FCrop関数はCrop関数を左上右下から何ドット削るかという指定にするだけのもので、
可読性を向上させるためのもの。

ResizeIntr関数が目的のフィールド単位縮小処理です。汎用ではありませんが、インタレ保持リサイズ処理の一種。

最初の4行は処理のための設定値です。hhは縮小するフィールドの縦解像度です。720pサイズで制作されたアニメの場合720/2 で360です。正しい値が必須なので、アニメの元解像度がわかっている必要があります。その辺の情報は、こことか参考になるかも。他の数字は後述。
hh = 360
rt = 6
sft1 = 3
sft2 = 2

変数wにクリップの横幅をいれておきます。
w = clip.Width()

SeparateFields関数でフィールド単位にした上で、横幅は維持しつつ縦をhh × rt、上記設定の状態で360 × 6 = 2160まで拡大します。
c = clip.SeparateFields().BicubicResize(w, hh * rt)

SelectEven()でTopフィールドだけを取り出し、上で指定したsft1の値だけ上にborderを付け、
その分下を削りveとする
同様にBottomフィールドについてsft2の値を使って同じことをします。それがvo。

ve = c.SelectEven().AddBorders(0, sft1, 0, 0).FCrop(0, 0, 0, sft1)
vo = c.SelectOdd().AddBorders(0, sft2, 0, 0).FCrop(0, 0, 0, sft2)

ve,voをInterleaveでtop+bottomに組みなおし、PointResizeで縦360にリサイズします。
この状態ではまだフィールド単位ですので、720の半分で360。
リサイズが終わったらweave関数でインターレース状態に戻します。

return Interleave(ve, vo).PointResize(w, hh).Weave()

ここで重要なのが、最後のリサイズがPointResizeである点。

原理はともかく、TSもしくはD3キャプチャのドルアーガのソースがある人は、

#もしロゴ削除等やる場合は先にやること
ResizeIntr()
#インタレ解除する処理を入れるならここ
Lanczos4Resize(1920, 1080)

を(もちろん上の関数部分も)付け加えたavsファイルをVirtualDubとかAviUtlで開いてどう変わるかやってみよう。運がよければ、縞が解消された状態になるはず。
もし縞は消えたもののジャギがくっきり見えるようであれば、上の
sft1 = 3
sft2 = 2
の数字をそれぞれ0~5の間で変更して変化をみてください。sft1,sft2の最大値は、rtで指定した値-1です。
HV1280アニメで常時縞が見える状態のものであれば他のアニメでも効果があります。
(実はtrue tearsも縦720は確定なので、同様に縞軽減できてしまうのですが、それも前提としての1280+評価なのでまだ変える気分になってません)

さらにSDからのアプコン(16:9の場合はスクイーズに限られるが)の場合は

hh = 243
rt = 4
sft1 = 1
sft2 = 0

あたりに変えると同様に縞の解消が可能なアニメがあります。この場合もsft1とsft2を変化させてり最も状態のよい数値を決めて下さい。ここではrtを4としているので0~3が指定可能な値。SDの場合480以上が普通と思われるので、画面の上が改善して下がおかしいとう場合はhhの値を少し増減すると改善する可能性があるかもしれません。いくつか調べてみたところ、hhは243のものがほとんどのようなので、アプコン4:3あるいはスクイーズの場合は縦486ドットを1080に拡大して放送しているようです。ただし例外もちらほらあるので、注意が必要。

rtの値でも変化が出やすいみたいなのでrtは2~10程度の範囲で替えてみるのもいいかも
その場合sft1, sft2は 0 ~ (rt - 1)になるようにあわせて調整すること。
ただしrtを大きくすると遅くなるので出力に差なければ小さい方がよい。

副作用や注意点として

・線が周辺の色によって侵食されるため薄くなる
・1080iでのテロップ、編集等が激しく劣化する
・ボケが酷くなるだけで単に縦にぼかしたのと同じ効果しかないこともある
・無駄が多いのでとても重い処理
・縞があったりなかったりする場合も縞のない部分では劣化する
・つまり縞のない映像に対してはマイナス効果しかない(誤って使うと泣くことになる)
・1080化後に縦方向にぼかしをかけたアニメの場合は線の揺らぎは軽減してもボケは改善しない
・もっとエレガントで高度な処理を作った人がいて、この記事自体無意味かも

などなど、他にも気付いてない欠点も多々あるかと。
もちろん完全な元データを得ることもできませんので、過度な期待はしないように!

設定値とか関数の引き数にして使い勝手向上とかできますが、もし有用に思えたら好きに改造しましょう。