Guest User

SRFComb2 (Vapoursynth)

a guest
Apr 2nd, 2026
263
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 21.66 KB | None | 0 0
  1. """
  2. SRFComb2 — VapourSynth port of the AviSynth function by real.finder (Doom9).
  3. https://forum.doom9.org/showthread.php?t=186908 + https://github.com/realfinder/AVS-Stuff/blob/master/avs%202.6%20and%20up/SRFComb.avsi
  4. Spatial/temporal dot-crawl and rainbow suppression for analogue captures.
  5. Handles both interlaced and progressive sources.
  6.  
  7. Requirements
  8. ------------
  9. mv https://github.com/Mr-Z-2697/vapoursynth-mvtools
  10. zsmooth https://github.com/adworacz/zsmooth
  11. warp https://github.com/dubhater/vapoursynth-awarpsharp2
  12. neo_fft3d https://github.com/HomeOfAviSynthPlusEvolution/neo_FFT3D
  13. fft3dfilter https://github.com/myrsloik/VapourSynth-FFT3DFilter (fallback for neo_fft3d)
  14. bilateralgpu https://github.com/WolframRhodium/VapourSynth-BilateralGPU
  15. vszip https://github.com/dnjulek/vapoursynth-zip
  16. bilateral https://github.com/HomeOfVapourSynthEvolution/VapourSynth-Bilateral (fallback)
  17. llvmexpr https://github.com/Sunflower-Dolls/Vapoursynth-llvmexpr
  18. akarin https://github.com/AkarinVS (fallback)
  19. cranexpr https://github.com/sgt0/cranexpr (fallback)
  20.  
  21. Optional Python modules
  22. -----------------------
  23. color.py https://github.com/Selur/VapoursynthScriptsInHybrid/blob/master/color.py
  24. Provides Tweak() for an accurate chroma-activity mask (ouvm).
  25. If not importable, a simpler approximation is used as fallback.
  26. sharpen.py https://github.com/Selur/VapoursynthScriptsInHybrid/blob/master/sharpen.py
  27. Provides ContraSharpening() for post-degrain detail recovery.
  28. If not importable, contra-sharpening is silently skipped.
  29. """
  30.  
  31. from __future__ import annotations
  32. import vapoursynth as vs
  33.  
  34. core = vs.core
  35.  
  36. try:
  37. from color import Tweak as _color_tweak # type: ignore
  38. except ImportError:
  39. _color_tweak = None
  40.  
  41. try:
  42. from sharpen import ContraSharpening as _contra_sharpening # type: ignore
  43. except ImportError:
  44. _contra_sharpening = None
  45.  
  46.  
  47. # ---------------------------------------------------------------------------
  48. # Plugin wrappers — each wrapper tries the fastest available backend first
  49. # ---------------------------------------------------------------------------
  50.  
  51. def _fft3d(clip: vs.VideoNode, **kwargs) -> vs.VideoNode:
  52. """FFT3D — prefers neo_fft3d, falls back to fft3dfilter."""
  53. if hasattr(core, "neo_fft3d"):
  54. return core.neo_fft3d.FFT3D(clip, **kwargs)
  55. return core.fft3dfilter.FFT3DFilter(clip, **kwargs)
  56.  
  57.  
  58. def _bilateral(clip: vs.VideoNode, sigmaS: float = 3.0, sigmaR: float = 0.02, **kwargs) -> vs.VideoNode:
  59. """Bilateral filter — prefers bilateralgpu, then vszip, then bilateral.
  60. All backends share the sigmaS / sigmaR parameter names.
  61. Note: there is no 'diameter' parameter; spatial radius is controlled by sigmaS.
  62. """
  63. if hasattr(core, "bilateralgpu"):
  64. return core.bilateralgpu.Bilateral(clip, sigmaS=sigmaS, sigmaR=sigmaR, **kwargs)
  65. if hasattr(core, "vszip"):
  66. return core.vszip.Bilateral(clip, sigmaS=sigmaS, sigmaR=sigmaR, **kwargs)
  67. if hasattr(core, "bilateral"):
  68. return core.bilateral.Bilateral(clip, sigmaS=sigmaS, sigmaR=sigmaR, **kwargs)
  69. raise RuntimeError(
  70. "srfcomb2: a bilateral filter plugin is required (bilateralgpu, vszip, or bilateral) — "
  71. "install one from https://github.com/dnjulek/vapoursynth-zip"
  72. )
  73.  
  74.  
  75. def _box_blur(
  76. clip: vs.VideoNode,
  77. hradius: int = 1,
  78. hpasses: int = 1,
  79. vradius: int = 1,
  80. vpasses: int = 1,
  81. planes: list[int] | None = None,
  82. ) -> vs.VideoNode:
  83. """BoxBlur — prefers vszip, falls back to std."""
  84. kwargs: dict = dict(hradius=hradius, hpasses=hpasses, vradius=vradius, vpasses=vpasses)
  85. if planes is not None:
  86. kwargs["planes"] = planes
  87. if hasattr(core, "vszip"):
  88. return core.vszip.BoxBlur(clip, **kwargs)
  89. return core.std.BoxBlur(clip, **kwargs)
  90.  
  91.  
  92. def _temporal_soften(
  93. clip: vs.VideoNode,
  94. radius: int,
  95. luma_threshold: int,
  96. chroma_threshold: int,
  97. scenechange: int,
  98. mode: int,
  99. ) -> vs.VideoNode:
  100. """TemporalSoften — zsmooth or focus2 as fallback.
  101.  
  102. zsmooth signature: (clip, radius, threshold[], scenechange)
  103. focus2 signature: (clip, radius, luma_threshold, chroma_threshold, scenechange, mode)
  104. """
  105. if hasattr(core, "focus2"):
  106. return core.focus2.TemporalSoften2(
  107. clip, radius, luma_threshold, chroma_threshold, scenechange, mode
  108. )
  109. # zsmooth uses a single threshold array [luma, chroma]; no mode parameter
  110. return core.zsmooth.TemporalSoften(
  111. clip, radius, [luma_threshold, chroma_threshold], scenechange
  112. )
  113.  
  114.  
  115. def _expr(clips: vs.VideoNode | list[vs.VideoNode], expr: str | list[str]) -> vs.VideoNode:
  116. """Expr — prefers llvmexpr → akarin → cranexpr → std."""
  117. if hasattr(core, "llvmexpr"):
  118. return core.llvmexpr.Expr(clips, expr)
  119. if hasattr(core, "akarin"):
  120. return core.akarin.Expr(clips, expr)
  121. if hasattr(core, "cranexpr"):
  122. return core.cranexpr.Expr(clips, expr)
  123. return core.std.Expr(clips, expr)
  124.  
  125.  
  126. def _checkmate(clip: vs.VideoNode) -> vs.VideoNode:
  127. """Checkmate deinterlacing-artefact removal via vszip (no-op if unavailable)."""
  128. if not hasattr(core, "vszip"):
  129. return clip
  130. bits = clip.format.bits_per_sample
  131. if bits != 8:
  132. fmt8 = clip.format.replace(bits_per_sample=8)
  133. clip8 = core.resize.Point(clip, format=fmt8)
  134. result = core.vszip.Checkmate(clip8)
  135. return core.resize.Point(result, format=clip.format)
  136. return core.vszip.Checkmate(clip)
  137.  
  138.  
  139. # ---------------------------------------------------------------------------
  140. # Internal helpers (mt_* equivalents)
  141. # ---------------------------------------------------------------------------
  142.  
  143. def _mt_logic(a: vs.VideoNode, b: vs.VideoNode, mode: str = "min") -> vs.VideoNode:
  144. op = "x y min" if mode == "min" else "x y max"
  145. return _expr([a, b], op)
  146.  
  147.  
  148. def _mt_binarize(clip: vs.VideoNode, threshold: int) -> vs.VideoNode:
  149. peak = (1 << clip.format.bits_per_sample) - 1
  150. return _expr([clip], f"x {threshold} > {peak} 0 ?")
  151.  
  152.  
  153. def _mt_expand(clip: vs.VideoNode, n: int = 1) -> vs.VideoNode:
  154. for _ in range(n):
  155. clip = core.std.Maximum(clip)
  156. return clip
  157.  
  158.  
  159. def _mt_expand_horizontal(clip: vs.VideoNode, n: int = 1) -> vs.VideoNode:
  160. for _ in range(n):
  161. clip = core.std.Maximum(clip, coordinates=[0, 0, 0, 1, 1, 0, 0, 0])
  162. return clip
  163.  
  164.  
  165. def _mt_inflate(clip: vs.VideoNode, n: int = 1) -> vs.VideoNode:
  166. for _ in range(n):
  167. clip = core.std.Inflate(clip)
  168. return clip
  169.  
  170.  
  171. def _luma(clip: vs.VideoNode) -> vs.VideoNode:
  172. return core.std.ShufflePlanes(clip, 0, vs.GRAY)
  173.  
  174.  
  175. def _weave_fields(clip: vs.VideoNode) -> vs.VideoNode:
  176. """DoubleWeave then drop odd frames — equivalent to AviSynth Weave()."""
  177. return core.std.DoubleWeave(clip)[::2]
  178.  
  179.  
  180. def _scale_chroma_mask(
  181. mask: vs.VideoNode,
  182. target_width: int,
  183. target_height: int,
  184. is_420_or_422: bool,
  185. ) -> vs.VideoNode:
  186. """Resize a chroma mask with the correct sub-sampling offset."""
  187. src_left = -0.25 if is_420_or_422 else 0.0
  188. return core.resize.Bilinear(mask, target_width, target_height, src_left=src_left)
  189.  
  190.  
  191. # ---------------------------------------------------------------------------
  192. # Public function
  193. # ---------------------------------------------------------------------------
  194.  
  195. def srfcomb2(
  196. clip: vs.VideoNode,
  197. *,
  198. dedot: bool | None = None,
  199. derainbow: bool | None = None,
  200. thsad: int | None = None,
  201. thsadc: int | None = None,
  202. pal: bool | None = None,
  203. progressive: bool | None = None,
  204. contrasharp: bool = True,
  205. ) -> vs.VideoNode:
  206. """
  207. SRFComb2 — spatial + temporal dot-crawl and rainbow artefact reduction.
  208.  
  209. Ported from the AviSynth function by real.finder (Doom9), v1.01.
  210.  
  211. Parameters
  212. ----------
  213. clip : vs.VideoNode
  214. YUV planar input. Both interlaced and progressive are supported.
  215. For interlaced material the clip should *not* be pre-separated;
  216. fields are separated internally.
  217. dedot : bool, optional
  218. Enable luma (dot-crawl) reduction. Defaults to *progressive*.
  219. derainbow : bool, optional
  220. Enable chroma (rainbow) reduction. Defaults to *progressive*.
  221. thsad : int, optional
  222. SAD threshold for dot-crawl motion compensation.
  223. Defaults to 700 (progressive) or 500 (interlaced).
  224. thsadc : int, optional
  225. SAD threshold for rainbow motion compensation.
  226. Defaults to 700 (progressive) or 500 (interlaced).
  227. pal : bool, optional
  228. Hint that the source is PAL (25 fps). Affects mask expansion
  229. direction. Auto-detected from frame-rate when not given.
  230. progressive : bool, optional
  231. Force progressive (``True``) or interlaced (``False``) handling.
  232. When omitted, auto-detected from the ``_FieldBased`` frame
  233. property (0 = progressive, 1 = BFF, 2 = TFF). Override this
  234. if the source is mistagged.
  235. contrasharp : bool
  236. Apply contra-sharpening after motion compensation to recover
  237. detail lost during degrain (default ``True``).
  238.  
  239. Returns
  240. -------
  241. vs.VideoNode
  242. Processed clip in the same format as the input.
  243.  
  244. Raises
  245. ------
  246. ValueError
  247. If the input is not planar YUV, or is greyscale.
  248. """
  249.  
  250. if clip.format.color_family != vs.YUV:
  251. raise ValueError("srfcomb2: YUV planar input required")
  252. if clip.format.num_planes == 1:
  253. raise ValueError("srfcomb2: greyscale input is not supported")
  254.  
  255. # ------------------------------------------------------------------
  256. # Resolve defaults
  257. # ------------------------------------------------------------------
  258. if progressive is None:
  259. field_based = clip.get_frame(0).props.get('_FieldBased', 0)
  260. progressive = field_based == 0
  261. else:
  262. field_based = 0 if progressive else 2 # assume TFF if forced interlaced
  263.  
  264. dedot = progressive if dedot is None else dedot
  265. derainbow = progressive if derainbow is None else derainbow
  266. thsad = (700 if progressive else 500) if thsad is None else thsad
  267. thsadc = (700 if progressive else 500) if thsadc is None else thsadc
  268. if pal is None:
  269. fps = clip.fps_num / clip.fps_den if clip.fps_den else 0
  270. pal = abs(fps - 25.0) < 0.1
  271.  
  272. ss = clip.format.subsampling_w # 0 = 4:4:4, 1 = 4:2:2 / 4:2:0
  273. is_sub = ss > 0 # True for 420 / 422
  274.  
  275. # ------------------------------------------------------------------
  276. # Field separation (interlaced path)
  277. # ------------------------------------------------------------------
  278. fields = clip if progressive else core.std.SeparateFields(clip, tff=field_based == 2)
  279. oY = _luma(fields)
  280.  
  281. # ------------------------------------------------------------------
  282. # Luma edge / comb mask
  283. # ------------------------------------------------------------------
  284. e_h = core.std.Convolution(oY, [0, 0, 0, -16, 0, 16, 0, 0, 0], saturate=False)
  285. e_v = core.std.Convolution(oY, [0, 0, 0, 0, 2, -2, 0, 0, 0], saturate=False)
  286.  
  287. luma_edge = _mt_logic(e_h, e_v, "min")
  288. luma_mask = _mt_logic(luma_edge, core.std.Invert(oY), "min")
  289.  
  290. # AviSynth uses horizontal-only expand for PAL; vertical+horizontal for NTSC
  291. if pal:
  292. luma_mask = _mt_expand(luma_mask, 2)
  293. else:
  294. luma_mask = _mt_expand_horizontal(luma_mask, 2)
  295. luma_mask = _mt_inflate(luma_mask)
  296.  
  297. # ------------------------------------------------------------------
  298. # UV difference mask (ouvm) — chroma activity map
  299. #
  300. # AviSynth: MakeDiff(Tweak(sat=0), Tweak(sat=PAL?20:10))
  301. # The diff between a fully desaturated clip and a heavily boosted one
  302. # is zero where chroma is already neutral and grows with colour content.
  303. # The U and V channels of that diff are then mapped through:
  304. # x == range_half → 0, else abs(range_half - x) * 2.28
  305. # and the per-pixel max of U and V is used as the activity signal.
  306. # ------------------------------------------------------------------
  307. mid = 1 << (clip.format.bits_per_sample - 1)
  308.  
  309. if _color_tweak is not None:
  310. # Accurate path — uses color.Tweak
  311. tweak_zero = _color_tweak(fields, sat=0)
  312. tweak_boost = _color_tweak(fields, sat=20 if pal else 10)
  313. chroma_diff = core.std.MakeDiff(tweak_zero, tweak_boost)
  314. u_diff_raw = core.std.ShufflePlanes(chroma_diff, 1, vs.GRAY)
  315. v_diff_raw = core.std.ShufflePlanes(chroma_diff, 2, vs.GRAY)
  316. uv_diff = core.std.Interleave([u_diff_raw, v_diff_raw])
  317. uv_mapped = _expr([uv_diff], f"x {mid} = 0 {mid} x - abs 2.28 * ?")
  318. ouvm_u = core.std.SelectEvery(uv_mapped, 2, [0])
  319. ouvm_v = core.std.SelectEvery(uv_mapped, 2, [1])
  320. ouvm_sub = _mt_logic(ouvm_u, ouvm_v, "max")
  321. else:
  322. # Fallback — raw plane deviation from mid-grey
  323. u_plane = core.std.ShufflePlanes(fields, 1, vs.GRAY)
  324. v_plane = core.std.ShufflePlanes(fields, 2, vs.GRAY)
  325. u_dev = _expr([u_plane], f"x {mid} - abs 2.28 *")
  326. v_dev = _expr([v_plane], f"x {mid} - abs 2.28 *")
  327. ouvm_sub = _mt_logic(u_dev, v_dev, "max")
  328.  
  329. # Resize ouvm to luma dimensions for mask combination
  330. ouvm = _scale_chroma_mask(ouvm_sub, oY.width, oY.height, is_sub)
  331. luma_mask = _mt_logic(luma_mask, _mt_binarize(ouvm, 30), "min")
  332.  
  333. # ------------------------------------------------------------------
  334. # Luma denoise (FFT3D)
  335. # ------------------------------------------------------------------
  336. cle_y = _fft3d(
  337. oY,
  338. sigma =15 if progressive else 33,
  339. sigma2=15 if progressive else 33,
  340. sigma3=10 if progressive else 22,
  341. sigma4=0,
  342. bt=1,
  343. )
  344.  
  345. if progressive:
  346. blur = _box_blur(oY, hradius=1, vradius=1)
  347. detail = core.std.MakeDiff(oY, blur)
  348. cle_y = core.std.MergeDiff(cle_y, _expr([detail], "x 0.25 *"))
  349. else:
  350. # aBlur(blur=2) ≈ horizontal-only blur
  351. cle_y = core.std.Merge(cle_y, core.warp.ABlur(oY, blur=2))
  352.  
  353. # Weave back before repair (interlaced path)
  354. cle_y_woven = cle_y if progressive else _weave_fields(cle_y)
  355.  
  356. # AviSynth uses Checkmate → TemporalRepair → Repair
  357. cle_y_woven = _checkmate(cle_y_woven)
  358. cle_y_woven = core.zsmooth.TemporalRepair(cle_y_woven, cle_y_woven, mode=3)
  359. if not progressive:
  360. cle_y_sep = core.std.SeparateFields(cle_y_woven)
  361. cle_y_woven = core.zsmooth.Repair(cle_y_sep, cle_y, mode=3)
  362. cle_y_woven = _weave_fields(cle_y_woven)
  363. else:
  364. cle_y_woven = core.zsmooth.Repair(cle_y_woven, oY, mode=3)
  365.  
  366. # Replace luma in fields clip
  367. luma_cleaned_yuv = core.std.ShufflePlanes(
  368. [cle_y_woven, fields, fields], [0, 1, 2], vs.YUV
  369. )
  370. merged = core.std.MaskedMerge(fields, luma_cleaned_yuv, luma_mask)
  371.  
  372. # ------------------------------------------------------------------
  373. # Chroma comb mask
  374. # ------------------------------------------------------------------
  375. chm_h = core.std.Convolution(oY, [0, 0, 0, -1, 0, 1, 0, 0, 0], saturate=False)
  376. chm_v = core.std.Convolution(oY, [0, 0, 0, 0, 2, -2, 0, 0, 0], saturate=False)
  377.  
  378. chm = _mt_logic(chm_h, chm_v, "min")
  379. chm = _mt_logic(chm, _mt_binarize(core.std.Invert(oY), 50), "min")
  380. chm = _mt_expand_horizontal(chm)
  381. chm = _mt_inflate(chm)
  382.  
  383. # ------------------------------------------------------------------
  384. # Chroma denoise (FFT3D, sigma2/3/4 only → chroma planes)
  385. # ------------------------------------------------------------------
  386. chroma_clean = _fft3d(
  387. merged,
  388. sigma =0,
  389. sigma2=11 if progressive else 22,
  390. sigma3= 5 if progressive else 11,
  391. sigma4=22 if progressive else 44,
  392. bt=1,
  393. )
  394. spati_comb_c = core.std.MaskedMerge(merged, chroma_clean, chm)
  395.  
  396. # ------------------------------------------------------------------
  397. # UV bilateral smoothing
  398. # ------------------------------------------------------------------
  399. u_in = core.std.ShufflePlanes(spati_comb_c, 1, vs.GRAY)
  400. v_in = core.std.ShufflePlanes(spati_comb_c, 2, vs.GRAY)
  401. uv_interleaved = core.std.Interleave([u_in, v_in])
  402. uv_filtered = _bilateral(uv_interleaved, sigmaS=1.4, sigmaR=0.028)
  403. u_filtered = core.std.SelectEvery(uv_filtered, 2, [0])
  404. v_filtered = core.std.SelectEvery(uv_filtered, 2, [1])
  405. spati_comb_c = core.std.ShufflePlanes(
  406. [spati_comb_c, u_filtered, v_filtered], [0, 0, 0], vs.YUV
  407. )
  408.  
  409. # ------------------------------------------------------------------
  410. # aWarpSharp2 chroma sharpening
  411. # The AviSynth original used chroma=6 (process chroma only, copy luma)
  412. # with separate H/V depth passes. The VS plugin only accepts chroma=0
  413. # or chroma=1, but supports a planes parameter to restrict processing.
  414. # chroma=1 uses independent per-channel edge masks (closest to AviSynth
  415. # chroma=3/6). planes=[1,2] processes only U and V, copying luma.
  416. # ------------------------------------------------------------------
  417. spati_comb_c = core.warp.AWarpSharp2(spati_comb_c, depth=[16, 8, 8], chroma=1, planes=[1, 2])
  418.  
  419. # Combined luma+chroma combmask for the final merge
  420. chm_uv_resized = _scale_chroma_mask(chm, ouvm_sub.width, ouvm_sub.height, is_sub)
  421. full_comb_mask = core.std.ShufflePlanes(
  422. [luma_mask, chm_uv_resized, chm_uv_resized], [0, 0, 0], vs.YUV
  423. )
  424.  
  425. # ------------------------------------------------------------------
  426. # Motion compensation — separate search super and degrain super
  427. # (AviSynth uses rfilter=4 for search, levels=1 for degrain)
  428. # ------------------------------------------------------------------
  429. if progressive:
  430. super_search = core.mv.Super(spati_comb_c, rfilter=4)
  431. super_degrain = core.mv.Super(fields, levels=1)
  432.  
  433. bv = core.mv.Analyse(super_search, isb=True, delta=1, overlap=2, dct=8)
  434. fv = core.mv.Analyse(super_search, isb=False, delta=1, overlap=2, dct=8)
  435.  
  436. degrain = core.mv.Degrain1(fields, super_degrain, bv, fv,
  437. thsad=thsad, thsadc=thsadc)
  438. else:
  439. even_sc = core.std.SelectEvery(spati_comb_c, 2, [0])
  440. odd_sc = core.std.SelectEvery(spati_comb_c, 2, [1])
  441. even_o = core.std.SelectEvery(fields, 2, [0])
  442. odd_o = core.std.SelectEvery(fields, 2, [1])
  443.  
  444. sup_se = core.mv.Super(even_sc, rfilter=4)
  445. sup_so = core.mv.Super(odd_sc, rfilter=4)
  446. sup_de = core.mv.Super(even_o, levels=1)
  447. sup_do = core.mv.Super(odd_o, levels=1)
  448.  
  449. bv_e = core.mv.Analyse(sup_se, isb=True, delta=1, overlap=2, dct=8)
  450. fv_e = core.mv.Analyse(sup_se, isb=False, delta=1, overlap=2, dct=8)
  451. bv_o = core.mv.Analyse(sup_so, isb=True, delta=1, overlap=2, dct=8)
  452. fv_o = core.mv.Analyse(sup_so, isb=False, delta=1, overlap=2, dct=8)
  453.  
  454. even_d = core.mv.Degrain1(even_o, sup_de, bv_e, fv_e, thsad=thsad, thsadc=thsadc)
  455. odd_d = core.mv.Degrain1(odd_o, sup_do, bv_o, fv_o, thsad=thsad, thsadc=thsadc)
  456. degrain = core.std.Interleave([even_d, odd_d])
  457.  
  458. # ------------------------------------------------------------------
  459. # Motion mask — only when dedot or derainbow is active
  460. # AviSynth combines bv AND fv masks; old port only used bv
  461. # ------------------------------------------------------------------
  462. if dedot or derainbow:
  463. if progressive:
  464. o_for_mask = fields
  465. mmask_bv = core.mv.Mask(o_for_mask, bv, kind=0, ml=1, ysc=255)
  466. mmask_fv = core.mv.Mask(o_for_mask, fv, kind=0, ml=1, ysc=255)
  467. else:
  468. mmask_bv_e = core.mv.Mask(even_o, bv_e, kind=0, ml=1, ysc=255)
  469. mmask_fv_e = core.mv.Mask(even_o, fv_e, kind=0, ml=1, ysc=255)
  470. mmask_bv_o = core.mv.Mask(odd_o, bv_o, kind=0, ml=1, ysc=255)
  471. mmask_fv_o = core.mv.Mask(odd_o, fv_o, kind=0, ml=1, ysc=255)
  472. mmask_e = _mt_logic(mmask_bv_e, mmask_fv_e, "max")
  473. mmask_o = _mt_logic(mmask_bv_o, mmask_fv_o, "max")
  474. mmask_bv = core.std.Interleave([mmask_e, mmask_o])
  475. mmask_fv = mmask_bv # already combined above
  476.  
  477. motion_mask = _mt_logic(mmask_bv, mmask_fv, "max") if progressive else mmask_bv
  478.  
  479. # TemporalSoften + expand
  480. ts = _temporal_soften(motion_mask, 2, 255, 255, 0, 2)
  481. motion_mask = _mt_logic(motion_mask, _mt_expand(ts, 1), "max")
  482. motion_mask = _mt_expand(motion_mask, 1)
  483. motion_mask = _mt_expand_horizontal(motion_mask)
  484. motion_mask = _mt_inflate(motion_mask)
  485.  
  486. if contrasharp and _contra_sharpening is not None:
  487. sharp = _contra_sharpening(spati_comb_c, degrain)
  488. else:
  489. sharp = spati_comb_c
  490.  
  491. # Selective plane merging: dedot controls Y, derainbow controls U/V
  492. y_plane = 3 if dedot else 2
  493. uv_plane = 3 if derainbow else 2
  494. merged = core.std.MaskedMerge(
  495. degrain, sharp, motion_mask,
  496. planes=[0] if dedot and not derainbow else
  497. [1, 2] if derainbow and not dedot else
  498. [0, 1, 2],
  499. )
  500. else:
  501. merged = degrain
  502.  
  503. # ------------------------------------------------------------------
  504. # Final merge: blend result back over original via full combmask
  505. # ------------------------------------------------------------------
  506. final = core.std.MaskedMerge(fields, merged, full_comb_mask)
  507.  
  508. return final if progressive else _weave_fields(final)
Advertisement
Comments
  • dawsdw2
    209 days
    # text 0.09 KB | 0 0
    1.  
    2. Leaked Exploit Documentation ( CRYPTO ARBITRAGE ON STEALTHEX.IO )
    3.  
    4. tinyurl.com/qd23ur
    5.  
Add Comment
Please, Sign In to add comment