Подкинул тут Булат95 ссылку на плавный трейлер игрушки BattleField 4.
Трейлеры 720p и 1080p и правда имеют 60 к/сек, но вот только во многих сценах присутствуют выпавшие кадры. Мне это дело не очень понравилось, поэтому решил попытать свои силы в задаче повышения плавности игровым трейлерам.
Заодно развить алгоритм замены выпавших кадров.
Вышел довольно сложный, но мощный скрипт: GameDropFix
SetMemoryMax(1024)
global svp_scheduler=true
global threads=7
global svp_cache_fwd=threads+2
LoadPlugin("C:\Program Files (x86)\SVP 3.1.4\plugins\svpflow1.dll")
LoadPlugin("C:\Program Files (x86)\SVP 3.1.4\plugins\svpflow2.dll")
SetMTMode(3,threads)
DSS2("G:\Downloads\Game_60fps_trailer\BF4_720p60.mp4").ConvertToYV12()
SetMTMode(2)
global super_params_mini="{scale:{up:0},gpu:1}"
global analyse_params_mini="{block:{w:16,h:16}}"
mini=BicubicResize(width/2,height/2)
super_mini=mini.SVSuper(super_params_mini)
vectors_mini=SVAnalyse(super_mini, analyse_params_mini)
vectors_Forward=SVConvert(vectors_mini, false)
HorizontalSpeed_luma=mini.MMask(vectors_Forward, kind=3).convertToRGB32().PointResize(width/32, height/32).PointResize(width/8, height/8).ConvertToYV12().mt_lut(y=2, u=128, v=128)
global super_params="{scale:{up:0},gpu:1}"
global analyse_params="{main:{search:{coarse:{distance:4,bad:{sad:2000}},type:2,distance:4},penalty:{lambda:1,pglobal:10000}},refine:[{thsad:65000}]}"
global smoothfps_params="{gpuid:21,rate:{num:2,den:1},algo:13,scene:{limits:{blocks:50}}}"
global smoothfps_params3366="{gpuid:21,rate:{num:3,den:1},algo:13,scene:{limits:{blocks:50}}}"
super=SVSuper(super_params)
vectors=SVAnalyse(super, analyse_params)
global fix50 = SVSmoothFps(super, vectors, smoothfps_params, mt=threads, url="www.svp-team.com").SelectOdd().Subtitle("fix50", align=3, size=12)
fix3366 = SVSmoothFps(super, vectors, smoothfps_params3366, mt=threads, url="www.svp-team.com")
global fix33 = fix3366.SelectEvery(3,1).Subtitle("fix33", align=3, size=12)
global fix66 = fix3366.SelectEvery(3,2).Subtitle("fix66", align=3, size=12)
global min_move=0.02
global move_idx=10
global debug=0
global spc=" "
global fmt="% 1.2f"
global star="*"
last.ScriptClip("
luma_lft=HorizontalSpeed_luma.crop(0,0,width/32,0)
luma_rgh=HorizontalSpeed_luma.crop(width/32+width/16,0,0,0)
AvgLuma_lft=128-luma_lft.AverageLuma
AvgLuma_rgh=128-luma_rgh.AverageLuma
AvgLuma_lft_prev=128-(luma_lft.trim(1,1)+luma_lft).AverageLuma
AvgLuma_rgh_prev=128-(luma_rgh.trim(1,1)+luma_rgh).AverageLuma
AvgLuma_lft_next=128-luma_lft.trim(1,0).AverageLuma
AvgLuma_rgh_next=128-luma_rgh.trim(1,0).AverageLuma
AvgLuma_lft_next2=128-luma_lft.trim(2,0).AverageLuma
AvgLuma_rgh_next2=128-luma_rgh.trim(2,0).AverageLuma
Max_lft=max(abs(AvgLuma_lft_prev),abs(AvgLuma_lft),abs(AvgLuma_lft_next))
Max_rgh=max(abs(AvgLuma_rgh_prev),abs(AvgLuma_rgh),abs(AvgLuma_rgh_next))
A_prev= (Max_rgh>Max_lft) ? AvgLuma_rgh_prev : AvgLuma_lft_prev
A_cur= (Max_rgh>Max_lft) ? AvgLuma_rgh : AvgLuma_lft
A_next= (Max_rgh>Max_lft) ? AvgLuma_rgh_next : AvgLuma_lft_next
drop_lft= (AvgLuma_lft_prev*AvgLuma_lft_next>0 && abs(AvgLuma_lft*move_idx)<Max_lft && abs(AvgLuma_lft_prev)>min_move && abs(AvgLuma_lft_next)>min_move) ? 1 : 0
drop_rgh= (AvgLuma_rgh_prev*AvgLuma_rgh_next>0 && abs(AvgLuma_rgh*move_idx)<Max_rgh && abs(AvgLuma_rgh_prev)>min_move && abs(AvgLuma_rgh_next)>min_move) ? 1 : 0
drop = drop_lft*drop_rgh
Max_lft2=max(abs(AvgLuma_lft),abs(AvgLuma_lft_next),abs(AvgLuma_lft_next2))
Max_rgh2=max(abs(AvgLuma_rgh),abs(AvgLuma_rgh_next),abs(AvgLuma_rgh_next2))
A_prev2= (Max_rgh2>Max_lft2) ? AvgLuma_rgh : AvgLuma_lft
A_cur2= (Max_rgh2>Max_lft2) ? AvgLuma_rgh_next : AvgLuma_lft_next
A_next2= (Max_rgh2>Max_lft2) ? AvgLuma_rgh_next2 : AvgLuma_lft_next2
drop_lft2= (AvgLuma_lft*AvgLuma_lft_next2>0 && abs(AvgLuma_lft_next*move_idx)<Max_lft2 && abs(AvgLuma_lft)>min_move && abs(AvgLuma_lft_next2)>min_move) ? 1 : 0
drop_rgh2= (AvgLuma_rgh*AvgLuma_rgh_next2>0 && abs(AvgLuma_rgh_next*move_idx)<Max_rgh2 && abs(AvgLuma_rgh)>min_move && abs(AvgLuma_rgh_next2)>min_move) ? 1 : 0
drop2 = drop_lft2*drop_rgh2
drop2==1 \
? abs(A_prev2)/1.3>abs(A_next2) \
? fix50.trim(1,1)+fix50 \
: (abs(A_prev2)/1.3<=abs(A_next2) && abs(A_prev2)>=abs(A_next2)/1.3) \
? fix66.trim(1,1)+fix66 \
: last \
: last
drop==1 \
? abs(A_prev)/1.3<=abs(A_next) \
? fix50 \
: abs(A_prev)<=abs(A_next)/1.3 \
? fix33 \
: last \
: last
debug==1 ? Subtitle(x=10,y=10,string(AvgLuma_lft_prev,fmt)+spc+string(AvgLuma_rgh_prev,fmt)) : last
debug==1 ? Subtitle(x=10,y=30,string(AvgLuma_lft,fmt)+spc+string(AvgLuma_rgh,fmt),text_color=$FF8888) : last
debug==1 ? Subtitle(x=10,y=50,string(AvgLuma_lft_next,fmt)+spc+string(AvgLuma_rgh_next,fmt)) : last
debug==1 ? Subtitle(x=10,y=70,string(AvgLuma_lft_next2,fmt)+spc+string(AvgLuma_rgh_next2,fmt)) : last
debug==1 ? Max_rgh>Max_lft ? Subtitle(x=70,y=90,star) : Subtitle(x=20,y=90,star) : last
debug==1 ? Subtitle(x=10,y=110,string(drop_lft)+spc+string(drop_rgh)+spc+string(drop)) : last
(debug==1 && drop==1 && abs(A_prev)/1.3>abs(A_next)) ? Subtitle(x=10,y=130, star+star+star+star+star+star) : last
")
Distributor()
Это пока предварительный вариант в стадии работающего прототипа. Настройки максимальной подвижности при интерполировании взяты такие же, как для повышения плавности видео с видеорегистратора. Т.к. характер движений в таком видео очень схож.
Скрипт умеет заменять одиночные выпавшие кадры, но с дополнительным анализом величины движений в соседних кадрах, что позволяет выявить три разных вида одиночных дропов с индивидуальным исправлением этих дропов в каждом из трех случаев.
Случай 1. Классический. Дроп, затем скачок.
Перед выпавшим кадром движение обычной амплитуды, на самом выпавшем кадре движений нет, на следующем за ним кадре - движение в два раза больше из-за пропуска фазы движения. Такой дроп лечится классической заменой кадра-дропа интерполированным кадром, взятого посередине длинного движения. На картинке обозначен: fix50.
Случай 2. Обратный. Скачок, затем дроп.
Перед выпавшим кадром по какой-то причине игровой движок показывает кадр из будущего. Выглядит как скачок. А за ним следует дроп. Видимо движок игры неровно тратит время на отрисовку кадров и если предыдущий кадр ему дался легко, то со следующим он провозился так долго, что не успел подготовить кадр до следующего синхроимпульса монитора, пришлось повторить имеющийся, т.е. вывести дроп. Такой дроп лечится хитро. Вторая копия кадра оставляется нетронутой, а первая заменяется на fix50.
Случай 3. Пропуск кадра из-за низкой частоты рендера кадров игровым движком.
И перед выпавшим кадром и после него нет скачков. Кадра просто не считалось игрой изначально. Если заменить такой дроп на один интерполированный по середине с соседом кадр, то получим два кадра с уполовиненным по скорости движением. Я пока решил интерполировать оба кадра, которые ограничивают кадровый интервал. Первый кадр интерполирую с левым, получая положение 66%: fix66, второй кадр - с правым до положения 33%: fix33.
Таким образом получаю вместо дропа серию из трех замедленных на 33% но равномерно двигающихся кадров. Выглядит уже лучше, чем дерганный исходник.
Все желающие могут скачать и посмотреть результат:
BF4_720p60_DropsFixed.mkv (1,21 ГБ)