Advertisement
Guest User

Untitled

a guest
Feb 1st, 2025
26
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.99 KB | None | 0 0
  1. import vapoursynth as vs
  2. from vapoursynth import core
  3.  
  4. '''
  5. call using:
  6.  
  7. from FillDuplicateFrames import FillDuplicateFrames
  8. fdf = FillDuplicateFrames(clip, debug=True, thresh=0.001, method='SVP')
  9. //fdf = FillDuplicateFrames(clip, debug=True, thresh=0.001, method='MV')
  10. //fdf = FillDuplicateFrames(clip, debug=True, thresh=0.001, method='RIFE')
  11. clip = fdf.out
  12.  
  13. Replaces duplicate frames with interpolations.
  14. v0.0.3
  15. 0.0.4 removed and added back RGBH support or RIFE
  16. 0.0.3 allow to set device_index for RIFE and support RGBH input for RIFE
  17. 0.0.4 removed RGBH since RIFE ncnn does not support it
  18. 0.0.5 add general sceneThr
  19. 0.0.6 add rifeModel parameter
  20. 0.0.7 add mode: FillDuplicate|FillDrops|Replace, add: rifeTTA, rifeUHD
  21. 0.0.8 add method: CCVFI
  22. '''
  23.  
  24. class FillDuplicateFrames:
  25. # constructor
  26. def __init__(self, clip: vs.VideoNode, mode='FillDuplicate', thresh: float=0.001, method: str='SVP', sceneThr: float=0.15, rifeModel: int=22, rifeTTA=False, rifeUHD=False, frames = [], debug: bool=False, device_index: int=0):
  27. # calculte stats
  28. self.thresh = thresh
  29. self.debug = debug
  30. self.method = method
  31. self.smooth = None
  32. self.sceneThr = sceneThr
  33. self.device_index = device_index
  34. self.rifeModel = rifeModel
  35. self.rifeTTA = rifeTTA
  36. self.rifeUHD = rifeUHD
  37.  
  38. self.mode = mode
  39. self.frames = frames
  40. if sceneThr > 0 and method.lower() == 'rife':
  41. clip = core.misc.SCDetect(clip=clip,threshold=sceneThr)
  42. if method.lower() == 'ccvfi':
  43. from ccvfi import AutoModel, BaseModelInterface, ConfigType
  44. core.num_threads = 1
  45. self.ccvfiModel: BaseModelInterface = AutoModel.from_pretrained(
  46. pretrained_model_name=ConfigType.DRBA_IFNet,
  47. )
  48. clip = core.resize.Bicubic(clip=clip, format=vs.RGBS)
  49. clip = core.std.PlaneStats(clip, clip[0]+clip)
  50. self.clip = core.resize.Bicubic(clip=clip, format=vs.RGBH)
  51. else:
  52. self.clip = core.std.PlaneStats(clip, clip[0]+clip)
  53. if method == 'Replace' and not frames:
  54. raise ValueError(f'FillDuplicateFrames: "frames" needs to be set when using \'{self.method}\'!')
  55.  
  56. def interpolate(self, n, f):
  57. if self.mode == 'FillDuplicate':
  58. out = self.get_current_or_interpolate(n)
  59. elif self.mode == 'FillDrops':
  60. out = self.get_current_or_interpolate_for_fill(n)
  61. elif self.mode == 'Replace':
  62. out = self.replaceFrame(n)
  63. else:
  64. raise ValueError(f'FillDuplicateFrames: Unknown mode \'{self.mode}\'!')
  65.  
  66. if self.debug:
  67. if out.format.id == vs.RGBH:
  68. out = core.resize.Bicubic(clip=out, format=vs.RGBS)
  69. out = out.text.Text(text="avg: "+str(f.props['PlaneStatsDiff']),alignment=8)
  70. return core.resize.Bicubic(clip=out, format=vs.RGBH)
  71. else:
  72. return out.text.Text(text="avg: "+str(f.props['PlaneStatsDiff']),alignment=8)
  73. return out
  74.  
  75. def interpolateWithCCVFI(self, clip, n, start, end):
  76. if clip.format.id != vs.RGBH:
  77. raise ValueError(f'FillDuplicateFrames: "clip" needs to be RGBH when using \'{self.method}\'!')
  78.  
  79. num = end - start
  80.  
  81. self.smooth = self.ccvfiModel.inference_video(clip, tar_fps=num)
  82. self.smooth_start = start
  83. self.smooth_end = end
  84. return self.smooth[n-start]
  85.  
  86. def interpolateWithRIFE(self, clip, n, start, end):
  87. if clip.format.id != vs.RGBS:
  88. raise ValueError(f'FillDuplicateFrames: "clip" needs to be RGBS when using \'{self.method}\'!')
  89.  
  90. num = end - start
  91.  
  92. self.smooth = core.rife.RIFE(clip, model=self.rifeModel, factor_num=num, tta=self.rifeTTA,uhd=self.rifeUHD,gpu_id=self.device_index)
  93. self.smooth_start = start
  94. self.smooth_end = end
  95. return self.smooth[n-start]
  96.  
  97. def interpolateWithMV(self, clip, n, start, end):
  98. num = end - start
  99. sup = core.mv.Super(clip, pel=2, hpad=0, vpad=0)
  100. bvec = core.mv.Analyse(sup, blksize=16, isb=True, chroma=True, search=3, searchparam=1)
  101. fvec = core.mv.Analyse(sup, blksize=16, isb=False, chroma=True, search=3, searchparam=1)
  102. self.smooth = core.mv.FlowFPS(clip, sup, bvec, fvec, num=num, den=1, mask=2)
  103. self.smooth_start = start
  104. self.smooth_end = end
  105. out = self.smooth[n-start]
  106. if self.debug:
  107. return out.text.Text(text="MV",alignment=9)
  108. return out
  109.  
  110. def interpolateWithSVP(self, clip, n, start, end):
  111. if clip.format.id != vs.YUV420P8:
  112. raise ValueError(f'FillDuplicateFrames: "clip" needs to be YUV420P8 when using \'{self.method}\'!')
  113. if self.method.lower() == 'svp_gpu' or self.method == 'SVP':
  114. super = core.svp1.Super(clip,"{gpu:1}")
  115. else: # self.method == 'SVPCPU':
  116. super = core.svp1.Super(clip,"{gpu:0}")
  117. vectors = core.svp1.Analyse(super["clip"],super["data"],clip,"{}")
  118. num = end - start
  119. self.smooth = core.svp2.SmoothFps(clip,super["clip"],super["data"],vectors["clip"],vectors["data"],f"{{rate:{{num:{num},den:1,abs:true}}}}")
  120. self.smooth_start = start
  121. self.smooth_end = end
  122. out = self.smooth[n-start]
  123. if self.debug:
  124. return out.text.Text(text="SVP",alignment=9)
  125. return out
  126.  
  127. def get_current_or_interpolate(self, n):
  128. if self.is_not_duplicate(n):
  129. if self.potential_scene_change(n):
  130. if self.debug:
  131. return self.clip[n].text.Text(text="Input (scene change - 1)", alignment=9)
  132. elif self.debug:
  133. #current non dublicate selected
  134. if self.clip.format.id == vs.RGBH:
  135. self.clip = core.resize.Bicubic(clip=self.clip, format=vs.RGBS)
  136. self.clip[n].text.Text(text="Input (1)", alignment=9)
  137. self.clip = core.resize.Bicubic(clip=self.clip, format=vs.RGBH)
  138. return self.clip[n]
  139. else:
  140. return self.clip[n].text.Text(text="Input (1)", alignment=9)
  141. return self.clip[n]
  142.  
  143. #dublicate frame, frame is interpolated
  144. for start in reversed(range(n+1)):
  145. if self.is_not_duplicate(start):
  146. break
  147. else: #there are all black frames preceding n, return current n frame // will be executed then for-look does not end with a break
  148. if self.debug:
  149. if self.clip.format.id == vs.RGBH:
  150. self.clip = core.resize.Bicubic(clip=self.clip, format=vs.RGBS)
  151. self.clip[n].text.Text(text="Input (2)", alignment=9)
  152. self.clip = core.resize.Bicubic(clip=self.clip, format=vs.RGBH)
  153. return self.clip[n]
  154. else:
  155. return self.clip[n].text.Text(text="Input (2)", alignment=9)
  156. return self.clip[n]
  157.  
  158. for end in range(n, len(self.clip)):
  159. if self.potential_scene_change(end):
  160. #there are all duplicate frames to the end, return current n frame
  161. if self.debug:
  162. return self.clip[n].text.Text(text="Input(before scene change)", alignment=9)
  163. return self.clip[n]
  164. if self.is_not_duplicate(end):
  165. break
  166. else:
  167. #there are all duplicate frames to the end, return current n frame
  168. if self.debug:
  169. return self.clip[n].text.Text(text="Input(3)", alignment=9)
  170. return self.clip[n]
  171.  
  172. #does interpolated smooth clip exist for requested n frame? Use n frame from it.
  173. if self.smooth is not None and start >= self.smooth_start and end <= self.smooth_end:
  174. if self.debug:
  175. return self.smooth[n-start].text.Text(text=self.method, alignment=9)
  176. return self.smooth[n-start]
  177.  
  178. #interpolating two frame clip into end-start+1 fps
  179. clip = self.clip[start] + self.clip[end]
  180. clip = clip.std.AssumeFPS(fpsnum=1, fpsden=1)
  181. if self.method.lower() == 'svp' or self.method == 'SVPcpu' or self.method == 'svp_gpu':
  182. return self.interpolateWithSVP(clip, n, start, end)
  183. elif self.method.lower() == 'rife':
  184. return self.interpolateWithRIFE(clip, n, start, end)
  185. elif self.method.lower() == 'mv':
  186. return self.interpolateWithMV(clip, n, start, end)
  187. elif self.method.lower() == 'ccvfi':
  188. return self.interpolateWithCCVFI(clip, n, start, end)
  189. else:
  190. raise ValueError(f'FillDuplicateFrames: {self.mode} "method" \'{self.method}\' is not supported atm.')
  191.  
  192. def get_current_or_interpolate_for_fill(self, n):
  193. if n == 0 or n >= self.clip.num_frames -1:
  194. if self.debug:
  195. return self.clip[n].text.Text(text="Input (0)", alignment=9)
  196. return self.clip[n]
  197. if self.is_not_duplicate(n):
  198. if self.potential_scene_change(n):
  199. if self.debug:
  200. return self.clip[n].text.Text(text="Input (scene change - 1)", alignment=9)
  201. elif self.debug:
  202. #current non dublicate selected
  203. return self.clip[n].text.Text(text="Input (1)", alignment=9)
  204. return self.clip[n]
  205.  
  206. start = n-1
  207. # previous frame is duplicate => nothing can be done
  208. if not self.is_not_duplicate(start):
  209. if self.debug:
  210. return self.clip[n].text.Text(text="Input (2)", alignment=9)
  211. return self.clip[n]
  212. # previous frame is scene change => nothing can be done
  213. if self.potential_scene_change(start):
  214. if self.debug:
  215. return self.clip[n].text.Text(text="Input(before scene change)", alignment=9)
  216. return self.clip[n]
  217.  
  218. end = n+1
  219. # next frame is duplicate => nothing can be done
  220. if not self.is_not_duplicate(start):
  221. if self.debug:
  222. return self.clip[n].text.Text(text="Input (2)", alignment=9)
  223. return self.clip[n]
  224. # nex frame is scene change => nothing can be done
  225. if self.potential_scene_change(start):
  226. if self.debug:
  227. return self.clip[n].text.Text(text="Input(before scene change)", alignment=9)
  228. return self.clip[n]
  229.  
  230. #does interpolated smooth clip exist for requested n frame? Use n frame from it.
  231. if self.smooth is not None and start >= self.smooth_start and end <= self.smooth_end:
  232. if self.debug:
  233. return self.smooth[n-start].text.Text(text=self.method, alignment=9)
  234. return self.smooth[n-start]
  235.  
  236. #interpolating two frame clip into end-start+1 fps
  237. clip = self.clip[start] + self.clip[end]
  238. clip = clip.std.AssumeFPS(fpsnum=1, fpsden=1)
  239. if self.method == 'svp_gpu' or self.method == 'svp':
  240. return self.interpolateWithSVP(clip, n, start, end)
  241. elif self.method.lower() == 'rife':
  242. return self.interpolateWithRIFE(clip, n, start, end)
  243. elif self.method.lower() == 'mv':
  244. return self.interpolateWithMV(clip, n, start, end)
  245. else:
  246. raise ValueError(f'FillDuplicateFrames: {self.mode} "method" \'{self.method}\' is not supported atm.')
  247.  
  248. def replaceFrame(self, n):
  249.  
  250. if not n in self.frames or n == 0 or n >= self.clip.num_frames -1:
  251. if self.debug:
  252. return self.clip[n].text.Text(text="Input (0)", alignment=9)
  253. return self.clip[n]
  254.  
  255. start = n-1
  256. # previous frame is duplicate => nothing can be done
  257. if not self.is_not_duplicate(start):
  258. if self.debug:
  259. return self.clip[n].text.Text(text="Input (1)", alignment=9)
  260. return self.clip[n]
  261. # previous frame is scene change => nothing can be done
  262. if self.potential_scene_change(start):
  263. if self.debug:
  264. return self.clip[n].text.Text(text="Input(before scene change)", alignment=9)
  265. return self.clip[n]
  266.  
  267. end = n+1
  268. # next frame is duplicate => nothing can be done
  269. if not self.is_not_duplicate(start):
  270. if self.debug:
  271. return self.clip[n].text.Text(text="Input (2)", alignment=9)
  272. return self.clip[n]
  273. # nex frame is scene change => nothing can be done
  274. if self.potential_scene_change(start):
  275. if self.debug:
  276. return self.clip[n].text.Text(text="Input(before scene change)", alignment=9)
  277. return self.clip[n]
  278.  
  279. #does interpolated smooth clip exist for requested n frame? Use n frame from it.
  280. if self.smooth is not None and start >= self.smooth_start and end <= self.smooth_end:
  281. if self.debug:
  282. return self.smooth[n-start].text.Text(text=self.method, alignment=9)
  283. return self.smooth[n-start]
  284.  
  285. #interpolating two frame clip into end-start+1 fps
  286. clip = self.clip[start] + self.clip[end]
  287. clip = clip.std.AssumeFPS(fpsnum=1, fpsden=1)
  288. if self.method.lower() == 'svp_gpu' or self.method.lower() == 'svp':
  289. return self.interpolateWithSVP(clip, n, start, end)
  290. elif self.method.lower() == 'rife':
  291. return self.interpolateWithRIFE(clip, n, start, end)
  292. elif self.method.lower() == 'mv':
  293. return self.interpolateWithMV(clip, n, start, end)
  294. else:
  295. raise ValueError(f'FillDuplicateFrames: {self.mode} "method" \'{self.method}\' is not supported atm.')
  296.  
  297.  
  298. def is_not_duplicate(self, n):
  299. return self.clip.get_frame(n).props['PlaneStatsDiff'] > self.thresh
  300.  
  301. def potential_scene_change(self, n):
  302. return self.sceneThr > 0 and self.clip.get_frame(n).props['PlaneStatsDiff'] > self.sceneThr
  303.  
  304. @property
  305. def out(self):
  306. return core.std.FrameEval(self.clip, self.interpolate, prop_src=self.clip)
  307.  
  308.  
  309.  
  310.  
  311.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement