Возможно, очень смешно видеть попытки написания мануала по чему-либо, связанному с IVTC, от человека, не имеющего ни одного рипа с интерлейсов. Так что если вы привыкли полагаться только на советы заматеревших от проблемы кодеров, либо сами скушали средний приют для бездомных собак на этой теме - дальше лучше не читать.
Всем остальным я постараюсь описать некоторые элементы методики лечения сложных паттернов телесина на примере пары минут видео из True Tears.
Исследование ни в коем случае не претендует на полноценный мануал по этой проблеме. Я даже не буду пытаться объяснить весь прекрасный процесс ivtc с нуля, возможно потому, что и сам его плохо понимаю
(я до сих пор так и не смог запомнить, что же такое VIDEO, а что такое FILM). Просто немного пройдусь по теории и выскажу пару мыслей лечения моментов, которые обычный tfm().tdecimate()
не берет.
И, да, для полного понимания всего, что написано ниже, опять требуется знание masktools
. Благо, мануал лежит неподалёку. ;)
В качестве показательного видео я выбрал True Tears не случайно - это замечательнейший пример криворукости японцев, который, к тому же, довольно просто лечится.
Изначально имеем видео в 29.970 (далее просто 30) fps, обладающее расслоением, двумя разными паттернами телесина в опенинге и прогрессивными нерасслоенными титрами поверх расслоенного зателесиненного задника в эндинге. По условию задачи некредитовок у нас нет (на самом деле, они есть, но с ними проблема особого внимания не стоит).
Расслоение будем лечить строкой destripe(360,6,4,3).evenly720(2,10)
, которая может и не совсем оптимальна для этого случая, но достаточно убивает титры, чтобы их пришлось лечить. Конечный вид
видео должен представлять собой 23.976 (далее просто 24) fps в опенинге и 30 в эндинге (титры уникальны на каждом кадре, поэтому использовать на них decimate
я не буду).
Далее, другая проблема - разные паттерны внутри одного кадра. Приведенные ниже скриншоты - два последовательных кадра. Видно, что окошко внутри и задник идут на разных паттернах. Но оба с 24 реальных fps.
Большая часть видеоряда шагает на своём паттерне и довольно просто детелесинится, поэтому первоначально составленный скрипт будет выглядить так:
AVCSource("E:\!tt\source\src.dga") #грузим сорц trim(0,2697) #тримим опенинг framecache(4) #используем для ускорения работы скрипта assumetff() #определяем правильный порядок полей destripe(360,6,4,3).evenly720(2,10) #лечим расслоение deint2 = tfm(display=false,pp=1,mode=5,slow=2) #складываем поля с отключенным ppОтключать pp очень важно, иначе можно просто не увидеть, где скрипт дает сбои. И вроде бы всё ничего и скрипт прекрасно отрабатывает на большей части видео, но хронически сбоит на местах с разными паттернами.
Вам решать, что делать с задниками - можно прописать кадры в OVR (их не так много), а можно откропать всю картинку так, чтобы области с другими паттернами не попадали (или попадали немного) на конечную картинку,
затем пройтись по видеоряду в лосслесс с нулевым выходом (или просто посмотреть в плеере), предварительно указав в tfm
параметр output, а затем загрузив его
в скрипт уже через параметр input (придется подправить сумму CRC в файле на ту, которую покажет скрипт в сообщении с ошибкой).
То же самое, в принципе, касается и tdecimate, который опять будет неправильно отрабатывать на местах с разными паттернами.
Теперь займемся окошками. Для того, чтобы правильно провести ivtc внутри них, необходимо откропать с картинки всё, кроме них самих. Работа это довольно долгая и унылая, но ничего, и не такое проходили. Финальный вид будет примерно таким:
op_p1 = crop(158,144,-1224,-144).tfm(pp=0).tdecimate() #1215,1251 op_p2 = crop(160,180,-800,-180).tfm(pp=0).tdecimate() #1252,1407 op_p3 = crop(850,300,-208,-60).tfm(pp=0).tdecimate() #1408,1437 op_p4 = crop(1224,216,-156,-70).tfm(pp=0,mode=5,slow=2).crop(0,2,0,0).tdecimate() #1438,1529 op_p5 = crop(600,74,-600,-72).tfm(pp=0,mode=1,slow=2).tdecimate() #1530,1583
Лучше сразу записать номера кадров, в которых появляются и исчезают окошки, чтобы потом не надо было искать их еще раз.
Главное, не забудьте проверять всё на глаз, ибо (первая картинка с кропом 214 сверху, вторая - 216. Всё из op_p4): Итак, имеем правильно обработанные в 24 фпс задники, и так же правильно обработанные в 24фпс окошки. Сложить их очень просто.ov1 = overlay(op_p1,158,144) ov2 = overlay(op_p2,160,180) ov3 = overlay(op_p3,850,300) ov4 = overlay(op_p4,1224,218) ov5 = overlay(op_p5,600,74) last.trim(0,1214)+ov1.trim(1215,1251)+ov2.trim(1252,1407)+ov3.trim(1408,1437)+ov4.trim(1438,1529)+ov5.trim(1530,1583)+last.trim(1584,0)
Ну вот и замечательно, теперь у нас есть прогрессивное видео в 24 фпс и разрешением 1920х720 и... убитыми титрами.
Для того, чтобы разобраться с титрами, первым делом их надо отловить. Сделать это можно многими разными способами, но учтите, что титры не появляются мгновенно, а своего рода фэйдятся, и любая маска никак не отловит крайние кадры, где титры еле видны, но уже убиты. Поэтому, как мне кажется, проще всего заранее заготовить картинки с масками, а затем тащить их в скрипт примерно такой функцией:function sv(clip clp, clip deint, int "frame") { clp msk = imagesource("E:\!tt\masks\"+string(frame)+".png").converttoyv12().mt_binarize(20).spline144resize(1920,720).mt_expand(mode=mt_rectangle(2,4)) #грузим картинку, переводим её в YV12, бинаризируем (скорее всего, изначально она будет в 16-235 вместо необходимого 0-255), приводим к конечному разрешению и немного расширяем. Расширять по горизонтали #нужно больше, чем по вертикали, ибо лечение расслоения затрагивает именно вертикальное относительное положение полей. mt_merge(clp,deint,msk,luma=true) #накладываем клип deint на clp по полученной маске. }Внутри скрипта это используется примерно так ;).
edeint = tfm(display=true,pp=0,mode=5,slow=2,scthresh =0,input="E:\!tt\tftout.txt").spline144resize(1920,720) # складываем исходную картинку по метрике tfm-а, полученной после лечения расслоения trim(0,190).sv(edeint,0)+trim(191,271)+trim(272,365).sv(edeint.trim(272,365),272)+trim(366,409)+sv(edeint,410).trim(410,534) + trim(535,550)+sv(edeint,551).trim(551,642)+trim(643,957)+sv(edeint,958).trim(958,1081)+ \ trim(1082,1278)+sv(edeint,1279).trim(1279,1408)+trim(1409,1455)+sv(edeint,1456).trim(1456,1589)+trim(1590,1601)+sv(edeint,1602).trim(1602,1748)+trim(1749,1769)+sv(edeint,1770).trim(1770,1900)+trim(1901,1983)+ \ sv(edeint,1984).trim(1984,2090)+trim(2091,2148)+sv(edeint,2149).trim(2149,2277)+trim(2278,2294)+sv(edeint,2295).trim(2295,2405)+trim(2406,2410)+sv(edeint,2411).trim(2411,2510)+trim(2511,2555)+sv(edeint,2556).trim(2556,0)Накладывать титры обязательно после
tfm
. В противном случае, если маска не идеальна (а без некредитовок она не идеальна), получится немного не то, что должно было быть.
Пожалуй, на этом с опенингом можно закончить.
Оригинальный кадр - кадр после tfm()
tfm(pp=0)
и... не увидим изменений. Плагин просто неспособен правильно найти совпадающие поля при такой большой области, занимаемой титрами.
Поэтому применяем наш старый добрый вариант - откропываем всё, кроме титров, а уже затем пробуем убрать телесин, попутно выводя информацию о совпадающих полях в файл.
AVCSource("E:\!tt\source\src.dga") framecache(5) trim(39860,42557) assumetff() crop(1160,0,0,0) destripe(360,6,4,3).evenly720(2,10) tfm(pp=0,output="E:\!tt\tfm_edd.txt")И всё бы ничего, но после применения на всё видео...
Как должно быть в идеале - убитые destipe титры - убитые tfm титры
tfm
.
mask = crop(100,0,-500,0).mt("""mt_luts(last,last,"range",mt_square(2),"y").mt_binarize(160).mt_expand(mode=mt_rectangle(10,10))""",4,10).addborders(100,0,500,0).mt_binarize(20,chroma="128") #Кропаем места, где нет титров (чтобы не надетектить лишнего, да и для скорости), берем разницу минимальных и маскимальных значений в области 5х5 (детектить можно кучей разных способов, в принципе), #бинаризируем маску, значительно расширяем её, затем добавляем границы обратно и еще раз бинаризируем (границы добавляются со значением 16, что не хорошо).На выходе получаем что-то вроде Замечательно. А что теперь делать с этой маской? Как мы поняли еще по опенингу, накладывать оригинальное изображение нельзя - получится кака в местах, где маска "прихватила лишнего". Конечно, можно интерполировать картинку перед этим, тогда видимых артефактов на задниках не будет, но и титры будут далеко не идеальны (особенно в движении). Был бы скролл и интерлейсные титры - было бы не жалко, всё равно ведь не получишь конфетку, а прогрессивные титры лучше не трогать лишний раз.
cs = blackmanresize(1920,720,taps=8).blackmanresize(1280,720,4,4,1906,711,taps=8) #двойной ресайз это не круто, но мне просто лень расчитывать кроп для другого разрешения. cn = nnedi3(0,pscrn=false).blackmanresize(1920,720,taps=8).blackmanresize(1280,720,4,4,1906,711,taps=8) destripe(360,6,4,3).evenly720(2,10) tfm(pp=0,mode=5,input="E:\!tt\tfm_edd.txt") blackmanresize(1280,720,4,4,1906,711,taps=8) mask = crop(100,0,-500,0).mt("""mt_luts(last,last,"range",mt_square(2),"y").mt_binarize(160).mt_expand(mode=mt_rectangle(10,10))""",4,10).addborders(100,0,500,0).mt_binarize(20,chroma="128")
Кадр без деинта после ресайза - наложение оригинала по маске - наложение nnedi3 по маске.
function VinverseD(clip clp, float "sstr", int "amnt", int "uv") { uv = default(uv,3) sstr = default(sstr,2.7) amnt = default(amnt,255) uv2 = (uv==2) ? 1 : uv STR = string(sstr) AMN = string(amnt) vblur = clp.mt_convolution("1","50 99 50",U=uv,V=uv) vblurD = mt_makediff(clp,vblur,U=uv2,V=uv2) Vshrp = mt_lutxy(vblur,vblur.mt_convolution("1","1 4 6 4 1",U=uv2,V=uv2),expr="x x y - "+STR+" * +",U=uv2,V=uv2) VshrpD = mt_makediff(Vshrp,vblur,U=uv2,V=uv2) VlimD = mt_lutxy(VshrpD,VblurD,expr="x 128 - y 128 - * 0 < x 128 - abs y 128 - abs < x y ? 128 - 0.25 * 128 + x 128 - abs y 128 - abs < x y ? ?",U=uv2,V=uv2) mt_adddiff(Vblur,VlimD,U=uv,V=uv) (amnt>254) ? last : (amnt==0) ? clp : mt_lutxy(clp,last,expr="x "+AMN+" + y < x "+AMN+" + x "+AMN+" - y > x "+AMN+" - y ? ?",U=uv,V=uv) return(last) }В принципе, можно вытащить маску из самого скрипта, а можно поступить проще - взять разницу между оригинальным кадром и кадром после того, как скрипт сделает свои темные дела.
Оригинальный кадр - VinverseD() - mt_lutxy(last,VinverseD,"x y - abs 16 *").grayscale()
removegrain(4)
, немного расширить, чтобы исключить случайные ошибки, бинаризировать и вообще. Факт в том, что на выходе мы имеем все
области, которые при наложении оригинальной картинки на выходную после tfm
давали "некрасивости". А значит всё, что нам осталось сделать - наложить интерполированный nnedi3 кадр на исходный по этим областям,
затем наложить исходный кадр по маске титров на выходной после tfm
. Делается примерно так.
AVCSource("E:\!tt\source\src.dga") framecache(5) trim(39860,42557) assumetff() cs = blackmanresize(1920,720,taps=8).blackmanresize(1280,720,4,4,1906,711,taps=8) cn = nnedi3(0,pscrn=false).blackmanresize(1920,720,taps=8).blackmanresize(1280,720,4,4,1906,711,taps=8) c = mt_merge(cs,cn,mt_lutxy(last,VinverseD,"x y - abs 16 *").removegrain(4).mt_expand(mode=mt_rectangle(1,2)).blackmanresize(1920,720,taps=8).blackmanresize(1280,720,4,4,1906,711,taps=8).grayscale(),luma=true) destripe(360,6,4,3).evenly720(2,10) tfm(pp=0,mode=5,input="E:\!tt\tfm_edd.txt") blackmanresize(1280,720,4,4,1906,711,taps=8) mask = crop(100,0,-500,0).mt("""mt_luts(last,last,"range",mt_square(2),"y").mt_binarize(160).mt_expand(mode=mt_rectangle(10,10))""",4,10).addborders(100,0,500,0).mt_binarize(20,chroma="128")#.grayscale() mt_merge(last,c,mask,luma=true)Думаю, не надо рассказывать, что где на представленных ниже кадрах. Вот, пожалуй, и всё на сегодня. Конечно, результат не идеален - в маски всегда будет попадать то, что не должно туда попадать, интерполяторы будут сбоить а паттерны не совпадать. Но всё в ваших руках! P.S. Все медленные ф-ии mt_expand(mode=*****) можно заменить на несколько последовательных вызовов обычных mt_expand(), mt_expand(mode="verical") и т.п. Будет значительно быстрее.