Настоящие слёзы IVTC.

От того что я разобрался в i мне стало только хуже.

Предисловие

Возможно, очень смешно видеть попытки написания мануала по чему-либо, связанному с 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 я не буду).

OP

Итак, первым делом, после лечения расслоения у нас убиваются титры. Это не так серьезно, как могло бы быть, но НЕ ИДЕАЛЬНО, поэтому лечить их мы тоже будем.

overlap overlap

Далее, другая проблема - разные паттерны внутри одного кадра. Приведенные ниже скриншоты - два последовательных кадра. Видно, что окошко внутри и задник идут на разных паттернах. Но оба с 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. В противном случае, если маска не идеальна (а без некредитовок она не идеальна), получится немного не то, что должно было быть.

Пожалуй, на этом с опенингом можно закончить.

ED

Тут всё гораздо интересней. Титры так же прогрессивны, но находятся в постоянном движении поверх видео с телесином, а значит, можно сразу забыть о заранее заготовленных масках (это реально, но слишком муторно). Так же можно сразу забыть о pp > 1, ибо интерполяция убьет титры сразу же. А ведь у нас еще расслоение...

Оригинальный кадр - кадр после 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 по маске.

Но тут внезапно на помощь приходит MCBob! Внутри замечательного скрипта от Didee лежит функция, которая позволяет лечить combed-области кадра без всяких медленных интерполяций.
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") и т.п. Будет значительно быстрее.
(с) tp7