Deinterlacing videa z Pentaxu K3 - dokončenie
V mojom minulom blogu o deinterlacingu som skončil pri použití filtra QTGMC. Dnešný blog bude obsahovať trochu teórie a výrazný tuning QTGMC.
Ako vyzerá prekladané video
Prekladané video sa na rozdiel od progresívneho videa skladá z polsnímkv s dvojnásobnou frekvenciou snímkov. Polsnímky obsahujú striedavo párne a nepárne riadky. Pri deinterlacingu sa buď skladajú 2 polsnímky do kompletného snímku a vznikne video s polovičnou frekvenciou snímkov (60i - 30p), alebo sa pre každý polsnímok dorátajú striedavo párne a nepárne riadky a vtedy vznikne video s plnou frekvenciou (60i - 60p).
Na nasledujúcej animácii sú 2 po sebe idúce snímky.
V prekladanom videu by boli 2 po sebe idúce snímky spojené do jedného snímku kde párne riadky by patrili jednému snímku a nepárne druhému. Statické časti obrazu zostávajú rovnaké ako pri progresívnom videu, ale pohyblivé časti sa rozštiepia na párne a nepárne riadky.
Pentax však zaznamenáva vždy len párne (alebo nepárne) snímky, takže reálne vertikálne rozlíšenie je polovičné.
Algoritmus
Našťastie existuje celá rodina algoritmov, ktoré pracujú len so snímkom s polovičným rozlíšením. Tieto algoritmy môžu mať pri korektne prekladanom obraze roztrasený obraz pretože pre každý polsnímok sa dopočítava obrázok z trochu rozdielnych dát. Z pentaxu by však statické časti mali byť pre oba polsnímky rovnaké a pri správne použitom algoritme by mal byť obraz stabilnejší.
Nasledujúci kód rieši väčšinu problémov s deinterlacingom z pentaxu.
import havsfunc as haf import numpy as np import os import vapoursynth as vs def modify_frame(n, f): frame = f.copy() for plane in range(frame.format.num_planes): write_array = np.array(frame.get_write_array(plane), copy=False) # Fix extra line on every second frame if n % 2 == 1: write_array[:] = np.roll(write_array, 1, axis=0) write_array[0,:] = write_array[1,:] # Double height frame copy = write_array.copy()[0:write_array.shape[0]//2,:] # Copy top half of frame write_array[0::2,:] = copy # Assign to odd and even lines write_array[1::2,:] = copy return frame core = vs.get_core() core.max_cache_size = 1024 clip = core.ffms2.Source(source=os.environ['VIDEO_SOURCE']) clip = core.std.SelectEvery(clip=clip, cycle=2, offsets=0) clip = core.std.AddBorders(clip=clip, bottom=clip.height) clip = core.std.SeparateFields(clip) clip = core.std.ModifyFrame(clip, clip, modify_frame) #clip = core.nnedi3.nnedi3(clip=clip, field=0, nsize=0, nns=2, qual=2, etype=1) clip = haf.QTGMC( Input=clip, Preset="Placebo", TFF=True, FPSDivisor=2, Lossless=0 ) clip.set_output()
Kód deinterlace.vpy
sa používa nasledovne:
export VIDEO_SOURCE=video.mkv vspipe ./deinterlace.vpy - --y4m|\ ffmpeg -i - -c:v libx264 -preset slow -crf 15 -r 60000/1001 -pix_fmt yuvj420p out.mkv
Analýza kódu
Prejdime si jednotlivé časti kódu. Na začiatku sa nastavujú parametre backendu.
core = vs.get_core() core.max_cache_size = 1024
Klip sa načítava pomocou ffmpeg backendu.
clip = core.ffms2.Source(source=os.environ['VIDEO_SOURCE'])
Na mojom systéme ffmpeg načítava prekladané snímky ako 2 po sebe idúce rovnaké snímky. Tu si nie som istý, či to je korektné správanie, alebo bug. Nasledujúci riadok odstráni polovicu snímkov, takže vznikne video bez duplicitných snímkov.
clip = core.std.SelectEvery(clip=clip, cycle=2, offsets=0)
V ďalšom kroku sa zväčší klip na dvojnásobnú výšku (pridá sa čierny pás dole).
clip = core.std.AddBorders(clip=clip, bottom=clip.height)
Nasleduje rozdelenie snímku na dva samostatné polsnímky.
clip = core.std.SeparateFields(clip)
Každý druhý snímok je vertikálne posunutý o jeden pixel. Kvôli tomu obraz poskakuje hore a dole. Funkcia modify_frame
pomocou numpy opravuje poskakovanie a zväčší snímok na plnú výšku (tu sa využije predtým pridaný čierny pás).
clip = core.std.ModifyFrame(clip, clip, modify_frame)
Funkcia sa volá pre každý snímok s parametrami n
(číslo snímku) a f
(inštancia snímku). Začíname kopírovaním snímku (nie je dobré modifikovať snímok zo vstupného prúpdu).
def modify_frame(n, f): frame = f.copy()
Kód sa bude spúšťať nad každou zložkou obrazu (jas a farba sú uložené samostatne).
for plane in range(frame.format.num_planes):
Časť snímku sa konvertuje do numpy poľa.
write_array = np.array(frame.get_write_array(plane), copy=False)
Pre nepárne snímky sa obraz posunie o jeden pixel smerom nadol. Prvý riadok nepárnych snímkov sa skopíruje z druhého riadka.
if n % 2 == 1: write_array[:] = np.roll(write_array, 1, axis=0) write_array[0,:] = write_array[1,:]
Snímok sa roztiahne na plnú výšku. Nasledujúci kód priradí kópiu polovice snímku (copy
) párnym a nepárnym riadkom. Výsledný snímok má dvojnásobné rozlíšenie.
copy = write_array.copy()[0:write_array.shape[0]//2,:] write_array[0::2,:] = copy write_array[1::2,:] = copy
Výsledné video sa preženie deinterlacerom. Ja používam QTGMC pretože si vie celkom slušne poradiť s chybami pri kompresii. Prekladaný obraz sa totiž môže komprimovať samostatne pre párne a nepárne riadky a čistý nnedi3 filter pre takýto vstup vyprodukuje obraz v ktorom sa bude veľmi rýchlo meniť odtieň na väčších plochách. QTGMC tieto problémy čiastočne rieši.
Interne QTGMC používa neurónové siete (nnedi3 filter) a množstvo ďalších filtrov. Popis parametrov je v dokumentácii. Ja som pri testoch použil nasledujúce parametre:
- Preset
- Predvolené nastavenia parametrov (maximálna kvalita).
- TFF
- Príznak pre používanie párnych, alebo nepárnych riadkov. Keďže vychádzame z polovičného rozlíšenia je hodnota tohto parametra irelevantná.
- FPSDivisor
- Tento filter štandardne generuje pre každý vstupný snímok dva výstupné snímky. Parametrom FPSDivisor nastavujem aby pre každý snímok bol generovaný jeden výstupný snímok.
- Lossless
- Pri nastavení tohto parametra by sa do generovania výstupnej snímky zarátali oba polsnímky a výstupné rozlíšenie by kvôli tomu bolo polovičné (keďže vstup je polovičný).
Výsledky
Teraz sa pozrime na výsledky z predchádzajúceho blogu a porovnajme s vylepšenou verziou.
Po oprave zostávajú statické časti stabilné.
Pohyblivé časti vyzerajú tak isto v poriadku.
Pre pridávanie komentárov sa musíte prihlásiť.
Kámo pekné čítanie.
Ale prečo si nepožil 1080p/30fps a nepoužil naurálnu sieť na dopočítanie na 1080p/60fps? Určite by to bolo bez tejto zbytočnej práce. Mno hlavne keď prekladané video nie je prekladané. To by som im vrátil ako šmejd ktorý si môžu dovoliť čínske šmejdy kde sa to očakáva a nie značkový produkt.
Pentax u mňa klesol na dno.
Ja som potreboval nahrať reálne video aby som videl, skutočné fps.
Obrázok 13: Oprava QTGMC - vsimni si vlavo dole to biele