Guest User

Python vapoursynth-awarpsharp2

a guest
Jun 28th, 2026
30
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 20.74 KB | None | 0 0
  1. """
  2. AWarpSharp2 - Pixel-accurate Python port of dubhater's original VapourSynth-AWarpSharp2.
  3.  
  4. Original C++: https://github.com/dubhater/vapoursynth-awarpsharp2
  5.  
  6. License: MIT
  7. """
  8.  
  9. import vapoursynth as vs
  10. import numpy as np
  11.  
  12. __version__ = '5.3.0'
  13.  
  14.  
  15. # =====================================================================
  16. # SOBEL (pixel-accurate to C++ sobel_c)
  17. # =====================================================================
  18.  
  19. def _sobel_c(src: np.ndarray, thresh: int, bits_per_sample: int) -> np.ndarray:
  20. """Port of dubhater's sobel_c() function."""
  21. height, width = src.shape
  22. dst = np.empty_like(src)
  23. pixel_max = (1 << bits_per_sample) - 1
  24.  
  25. src_int = src.astype(np.int32)
  26.  
  27. for y in range(1, height - 1):
  28. for x in range(1, width - 1):
  29. a11 = src_int[y - 1, x - 1]
  30. a21 = src_int[y - 1, x]
  31. a31 = src_int[y - 1, x + 1]
  32. a12 = src_int[y, x - 1]
  33. a32 = src_int[y, x + 1]
  34. a13 = src_int[y + 1, x - 1]
  35. a23 = src_int[y + 1, x]
  36. a33 = src_int[y + 1, x + 1]
  37.  
  38. avg_up = (a21 + ((a11 + a31 + 1) >> 1) + 1) >> 1
  39. avg_down = (a23 + ((a13 + a33 + 1) >> 1) + 1) >> 1
  40. avg_left = (a12 + ((a13 + a11 + 1) >> 1) + 1) >> 1
  41. avg_right = (a32 + ((a33 + a31 + 1) >> 1) + 1) >> 1
  42.  
  43. abs_v = abs(avg_up - avg_down)
  44. abs_h = abs(avg_left - avg_right)
  45.  
  46. absolute = min(abs_v + abs_h, pixel_max)
  47. abs_max = max(abs_h, abs_v)
  48.  
  49. absolute = min(absolute + abs_max, pixel_max)
  50. absolute = min(min(absolute * 2, pixel_max) + absolute, pixel_max)
  51. absolute = min(absolute * 2, pixel_max)
  52.  
  53. dst[y, x] = min(absolute, thresh)
  54.  
  55. for y in range(1, height - 1):
  56. dst[y, 0] = dst[y, 1]
  57. dst[y, width - 1] = dst[y, width - 2]
  58.  
  59. dst[0, :] = dst[1, :]
  60. dst[height - 1, :] = dst[height - 2, :]
  61.  
  62. return dst
  63.  
  64.  
  65. # =====================================================================
  66. # BLUR R6 (pixel-accurate to C++ blur_r6_c)
  67. # =====================================================================
  68.  
  69. def _blur_r6_c(mask: np.ndarray, temp: np.ndarray) -> None:
  70. """Port of dubhater's blur_r6_c() function."""
  71. height, width = mask.shape
  72. mask_int = mask.astype(np.int32)
  73. temp_int = temp.astype(np.int32)
  74.  
  75. for y in range(height):
  76. for x in range(6):
  77. avg12 = (mask_int[y, x + 1] + mask_int[y, x + 2] + 1) >> 1
  78. avg34 = (mask_int[y, x + 3] + mask_int[y, x + 4] + 1) >> 1
  79. avg56 = (mask_int[y, x + 5] + mask_int[y, x + 6] + 1) >> 1
  80. avg012 = (mask_int[y, x] + avg12 + 1) >> 1
  81. avg3456 = (avg34 + avg56 + 1) >> 1
  82. avg0123456 = (avg012 + avg3456 + 1) >> 1
  83. avg = (avg012 + avg0123456 + 1) >> 1
  84. temp_int[y, x] = avg
  85.  
  86. for x in range(6, width - 6):
  87. avg11 = (mask_int[y, x - 1] + mask_int[y, x + 1] + 1) >> 1
  88. avg22 = (mask_int[y, x - 2] + mask_int[y, x + 2] + 1) >> 1
  89. avg33 = (mask_int[y, x - 3] + mask_int[y, x + 3] + 1) >> 1
  90. avg44 = (mask_int[y, x - 4] + mask_int[y, x + 4] + 1) >> 1
  91. avg55 = (mask_int[y, x - 5] + mask_int[y, x + 5] + 1) >> 1
  92. avg66 = (mask_int[y, x - 6] + mask_int[y, x + 6] + 1) >> 1
  93. avg12 = (avg11 + avg22 + 1) >> 1
  94. avg34 = (avg33 + avg44 + 1) >> 1
  95. avg56 = (avg55 + avg66 + 1) >> 1
  96. avg012 = (mask_int[y, x] + avg12 + 1) >> 1
  97. avg3456 = (avg34 + avg56 + 1) >> 1
  98. avg0123456 = (avg012 + avg3456 + 1) >> 1
  99. avg = (avg012 + avg0123456 + 1) >> 1
  100. temp_int[y, x] = avg
  101.  
  102. for x in range(width - 6, width):
  103. avg12 = (mask_int[y, x - 1] + mask_int[y, x - 2] + 1) >> 1
  104. avg34 = (mask_int[y, x - 3] + mask_int[y, x - 4] + 1) >> 1
  105. avg56 = (mask_int[y, x - 5] + mask_int[y, x - 6] + 1) >> 1
  106. avg012 = (mask_int[y, x] + avg12 + 1) >> 1
  107. avg3456 = (avg34 + avg56 + 1) >> 1
  108. avg0123456 = (avg012 + avg3456 + 1) >> 1
  109. avg = (avg012 + avg0123456 + 1) >> 1
  110. temp_int[y, x] = avg
  111.  
  112. for y in range(6):
  113. for x in range(width):
  114. l0 = temp_int[y, x]
  115. l1 = temp_int[y + 1, x] if y + 1 < height else temp_int[y, x]
  116. l2 = temp_int[y + 2, x] if y + 2 < height else temp_int[y, x]
  117. l3 = temp_int[y + 3, x] if y + 3 < height else temp_int[y, x]
  118. l4 = temp_int[y + 4, x] if y + 4 < height else temp_int[y, x]
  119. l5 = temp_int[y + 5, x] if y + 5 < height else temp_int[y, x]
  120. l6 = temp_int[y + 6, x] if y + 6 < height else temp_int[y, x]
  121.  
  122. avg12 = (l1 + l2 + 1) >> 1
  123. avg34 = (l3 + l4 + 1) >> 1
  124. avg56 = (l5 + l6 + 1) >> 1
  125. avg3456 = (avg34 + avg56 + 1) >> 1
  126. avg012 = (l0 + avg12 + 1) >> 1
  127. avg0123456 = (avg012 + avg3456 + 1) >> 1
  128. avg = (avg012 + avg0123456 + 1) >> 1
  129. mask[y, x] = avg
  130.  
  131. for y in range(6, height - 6):
  132. for x in range(width):
  133. m6 = temp_int[y - 6, x]
  134. m5 = temp_int[y - 5, x]
  135. m4 = temp_int[y - 4, x]
  136. m3 = temp_int[y - 3, x]
  137. m2 = temp_int[y - 2, x]
  138. m1 = temp_int[y - 1, x]
  139. l0 = temp_int[y, x]
  140. l1 = temp_int[y + 1, x]
  141. l2 = temp_int[y + 2, x]
  142. l3 = temp_int[y + 3, x]
  143. l4 = temp_int[y + 4, x]
  144. l5 = temp_int[y + 5, x]
  145. l6 = temp_int[y + 6, x]
  146.  
  147. avg11 = (m1 + l1 + 1) >> 1
  148. avg22 = (m2 + l2 + 1) >> 1
  149. avg33 = (m3 + l3 + 1) >> 1
  150. avg44 = (m4 + l4 + 1) >> 1
  151. avg55 = (m5 + l5 + 1) >> 1
  152. avg66 = (m6 + l6 + 1) >> 1
  153. avg12 = (avg11 + avg22 + 1) >> 1
  154. avg34 = (avg33 + avg44 + 1) >> 1
  155. avg56 = (avg55 + avg66 + 1) >> 1
  156. avg012 = (l0 + avg12 + 1) >> 1
  157. avg3456 = (avg34 + avg56 + 1) >> 1
  158. avg0123456 = (avg012 + avg3456 + 1) >> 1
  159. avg = (avg012 + avg0123456 + 1) >> 1
  160. mask[y, x] = avg
  161.  
  162. for y in range(height - 6, height):
  163. for x in range(width):
  164. m6 = temp_int[y - 6, x] if y - 6 >= 0 else temp_int[0, x]
  165. m5 = temp_int[y - 5, x] if y - 5 >= 0 else temp_int[0, x]
  166. m4 = temp_int[y - 4, x] if y - 4 >= 0 else temp_int[0, x]
  167. m3 = temp_int[y - 3, x] if y - 3 >= 0 else temp_int[0, x]
  168. m2 = temp_int[y - 2, x] if y - 2 >= 0 else temp_int[0, x]
  169. m1 = temp_int[y - 1, x] if y - 1 >= 0 else temp_int[0, x]
  170. l0 = temp_int[y, x]
  171.  
  172. avg12 = (m1 + m2 + 1) >> 1
  173. avg34 = (m3 + m4 + 1) >> 1
  174. avg56 = (m5 + m6 + 1) >> 1
  175. avg012 = (l0 + avg12 + 1) >> 1
  176. avg3456 = (avg34 + avg56 + 1) >> 1
  177. avg0123456 = (avg012 + avg3456 + 1) >> 1
  178. avg = (avg012 + avg0123456 + 1) >> 1
  179. mask[y, x] = avg
  180.  
  181.  
  182. # =====================================================================
  183. # BLUR R2 (pixel-accurate to C++ blur_r2_c)
  184. # =====================================================================
  185.  
  186. def _blur_r2_c(mask: np.ndarray, temp: np.ndarray) -> None:
  187. """Port of dubhater's blur_r2_c() function."""
  188. height, width = mask.shape
  189. mask_int = mask.astype(np.int32)
  190. temp_int = temp.astype(np.int32)
  191.  
  192. for y in range(height):
  193. avg1 = (mask_int[y, 0] + mask_int[y, 1] + 1) >> 1
  194. avg2 = (mask_int[y, 0] + mask_int[y, 2] + 1) >> 1 if width > 2 else (mask_int[y, 0] + mask_int[y, 1] + 1) >> 1
  195. avg = (avg2 + mask_int[y, 0] + 1) >> 1
  196. avg = (avg + mask_int[y, 0] + 1) >> 1
  197. avg = (avg + avg1 + 1) >> 1
  198. temp_int[y, 0] = avg
  199.  
  200. if width > 1:
  201. avg1 = (mask_int[y, 0] + mask_int[y, 2] + 1) >> 1 if width > 2 else (mask_int[y, 0] + mask_int[y, 1] + 1) >> 1
  202. avg2 = (mask_int[y, 0] + mask_int[y, 3] + 1) >> 1 if width > 3 else (mask_int[y, 0] + mask_int[y, 1] + 1) >> 1
  203. avg = (avg2 + mask_int[y, 1] + 1) >> 1
  204. avg = (avg + mask_int[y, 1] + 1) >> 1
  205. avg = (avg + avg1 + 1) >> 1
  206. temp_int[y, 1] = avg
  207.  
  208. for x in range(2, width - 2):
  209. avg1 = (mask_int[y, x - 1] + mask_int[y, x + 1] + 1) >> 1
  210. avg2 = (mask_int[y, x - 2] + mask_int[y, x + 2] + 1) >> 1
  211. avg = (avg2 + mask_int[y, x] + 1) >> 1
  212. avg = (avg + mask_int[y, x] + 1) >> 1
  213. avg = (avg + avg1 + 1) >> 1
  214. temp_int[y, x] = avg
  215.  
  216. if width > 2:
  217. x = width - 2
  218. avg1 = (mask_int[y, x - 1] + mask_int[y, x + 1] + 1) >> 1 if x + 1 < width else (mask_int[y, x - 1] + mask_int[y, x] + 1) >> 1
  219. avg2 = (mask_int[y, x - 2] + mask_int[y, x + 1] + 1) >> 1 if x + 1 < width else (mask_int[y, x - 2] + mask_int[y, x] + 1) >> 1
  220. avg = (avg2 + mask_int[y, x] + 1) >> 1
  221. avg = (avg + mask_int[y, x] + 1) >> 1
  222. avg = (avg + avg1 + 1) >> 1
  223. temp_int[y, x] = avg
  224.  
  225. x = width - 1
  226. avg1 = (mask_int[y, x - 1] + mask_int[y, x] + 1) >> 1
  227. avg2 = (mask_int[y, x - 2] + mask_int[y, x] + 1) >> 1 if x - 2 >= 0 else (mask_int[y, x - 1] + mask_int[y, x] + 1) >> 1
  228. avg = (avg2 + mask_int[y, x] + 1) >> 1
  229. avg = (avg + mask_int[y, x] + 1) >> 1
  230. avg = (avg + avg1 + 1) >> 1
  231. temp_int[y, x] = avg
  232.  
  233. for y in range(height):
  234. stride_p1 = -1 if y > 0 else 0
  235. stride_p2 = stride_p1 * 2 if y > 1 else stride_p1
  236. stride_n1 = 1 if y < height - 1 else 0
  237. stride_n2 = stride_n1 * 2 if y < height - 2 else stride_n1
  238.  
  239. for x in range(width):
  240. m2 = temp_int[max(0, y + stride_p2), x]
  241. m1 = temp_int[max(0, y + stride_p1), x]
  242. l0 = temp_int[y, x]
  243. l1 = temp_int[min(height - 1, y + stride_n1), x]
  244. l2 = temp_int[min(height - 1, y + stride_n2), x]
  245.  
  246. avg1 = (m1 + l1 + 1) >> 1
  247. avg2 = (m2 + l2 + 1) >> 1
  248. avg = (avg2 + l0 + 1) >> 1
  249. avg = (avg + l0 + 1) >> 1
  250. avg = (avg + avg1 + 1) >> 1
  251. mask[y, x] = avg
  252.  
  253.  
  254. # =====================================================================
  255. # BILINEAR DOWNSCALE
  256. # =====================================================================
  257.  
  258. def _bilinear_downscale_h_c(src: np.ndarray, cplace: str) -> None:
  259. """Port of dubhater's bilinear_downscale_h_c() function."""
  260. height, width = src.shape
  261.  
  262. if cplace == 'mpeg1':
  263. for y in range(height):
  264. for x in range(width // 2):
  265. src[y, x] = (src[y, x * 2] + src[y, x * 2 + 1] + 1) >> 1
  266. else: # mpeg2
  267. for y in range(height):
  268. c2 = src[y, 0]
  269. for x in range(width // 2):
  270. c0 = c2
  271. c1 = src[y, x * 2]
  272. c2 = src[y, x * 2 + 1]
  273. src[y, x] = (c0 + 2 * c1 + c2 + 2) >> 2
  274.  
  275.  
  276. def _bilinear_downscale_v_c(src: np.ndarray) -> None:
  277. """Port of dubhater's bilinear_downscale_v_c() function."""
  278. height, width = src.shape
  279.  
  280. for y in range(height // 2):
  281. for x in range(width):
  282. src[y, x] = (src[y, x] + src[y + 1, x] + 1) >> 1
  283.  
  284.  
  285. def _bilinear_downscale_hv_c(src: np.ndarray, cplace: str) -> None:
  286. """Port of dubhater's bilinear_downscale_hv_c() function."""
  287. height, width = src.shape
  288.  
  289. if cplace == 'mpeg1':
  290. for y in range(height // 2):
  291. for x in range(width // 2):
  292. avg1 = (src[y, x * 2] + src[y, x * 2 + 1] + 1) >> 1
  293. avg2 = (src[y + 1, x * 2] + src[y + 1, x * 2 + 1] + 1) >> 1
  294. src[y, x] = (avg1 + avg2 + 1) >> 1
  295. else: # mpeg2
  296. for y in range(height // 2):
  297. c2 = src[y, 0] + src[y + 1, 0]
  298. for x in range(width // 2):
  299. c0 = c2
  300. c1 = src[y, x * 2] + src[y + 1, x * 2]
  301. c2 = src[y, x * 2 + 1] + src[y + 1, x * 2 + 1]
  302. src[y, x] = (c0 + 2 * c1 + c2 + 4) >> 3
  303.  
  304.  
  305. # =====================================================================
  306. # WARP (pixel-accurate to C++ warp_c)
  307. # =====================================================================
  308.  
  309. def _warp_c(src: np.ndarray, edge: np.ndarray, depth: int, bits_per_sample: int, smagl: int = 0) -> np.ndarray:
  310. """Port of dubhater's warp_c() function."""
  311. height, width = edge.shape
  312. dst = np.empty_like(edge)
  313.  
  314. extra_bits = bits_per_sample - 8
  315. pixel_max = (1 << bits_per_sample) - 1
  316. SMAG = 1 << smagl
  317.  
  318. depth_shifted = depth << 8
  319.  
  320. x_limit_min = 0 * SMAG
  321. x_limit_max = (width - 1) * SMAG
  322.  
  323. for y in range(height):
  324. y_limit_min = -y * 128
  325. y_limit_max = (height - y) * 128 - 129
  326.  
  327. for x in range(width):
  328. if y == 0:
  329. above = edge[y, x]
  330. else:
  331. above = edge[y - 1, x]
  332.  
  333. if y == height - 1:
  334. below = edge[y, x]
  335. else:
  336. below = edge[y + 1, x]
  337.  
  338. if x == 0:
  339. left = edge[y, x]
  340. else:
  341. left = edge[y, x - 1]
  342.  
  343. if x == width - 1:
  344. right = edge[y, x]
  345. else:
  346. right = edge[y, x + 1]
  347.  
  348. h = int(left) - int(right)
  349. v = int(above) - int(below)
  350.  
  351. if extra_bits > 0:
  352. h >>= extra_bits
  353. v >>= extra_bits
  354.  
  355. h <<= 7
  356. v <<= 7
  357.  
  358. h *= depth_shifted
  359. h >>= 16
  360. v *= depth_shifted
  361. v >>= 16
  362.  
  363. v = max(v, y_limit_min)
  364. v = min(v, y_limit_max)
  365.  
  366. remainder_h = h
  367. remainder_v = v
  368.  
  369. if smagl:
  370. remainder_h <<= smagl
  371. remainder_v <<= smagl
  372.  
  373. remainder_h &= 127
  374. remainder_v &= 127
  375.  
  376. h >>= 7 - smagl
  377. v >>= 7 - smagl
  378.  
  379. h += x << smagl
  380. # CRITICAL FIX: v must be converted to an absolute row coordinate
  381. v += y << smagl
  382.  
  383. remainder_needed = (x_limit_max > h) and not (x_limit_min > h)
  384. if not remainder_needed:
  385. remainder_h = 0
  386.  
  387. h = min(h, x_limit_max)
  388. h = max(h, x_limit_min)
  389.  
  390. h = min(h, width - 2)
  391. v = min(v, height - 2)
  392.  
  393. s00 = int(src[v, h])
  394. s01 = int(src[v, h + 1])
  395. s10 = int(src[v + 1, h])
  396. s11 = int(src[v + 1, h + 1])
  397.  
  398. s0 = s00 * (128 - remainder_h)
  399. s1 = s10 * (128 - remainder_h)
  400.  
  401. s0 += s01 * remainder_h
  402. s1 += s11 * remainder_h
  403.  
  404. s0 += 64
  405. s1 += 64
  406.  
  407. s0 >>= 7
  408. s1 >>= 7
  409.  
  410. s0 *= 128 - remainder_v
  411. s1 *= remainder_v
  412.  
  413. s = s0 + s1
  414.  
  415. s += 64
  416.  
  417. s >>= 7
  418.  
  419. dst[y, x] = min(max(s, 0), pixel_max)
  420.  
  421. return dst
  422.  
  423.  
  424. # =====================================================================
  425. # Helpers
  426. # =====================================================================
  427.  
  428. def _copy_to_plane(frame: vs.VideoFrame, plane: int, data: np.ndarray) -> None:
  429. """Copy processed data back into a frame plane, handling R60+ API."""
  430. try:
  431. arr = frame.get_write_array(plane)
  432. except AttributeError:
  433. arr = np.asarray(frame[plane])
  434. np.copyto(arr, data)
  435.  
  436.  
  437. # =====================================================================
  438. # VAPOURSYNTH INTERFACE
  439. # =====================================================================
  440.  
  441. def AWarpSharp2(
  442. clip: vs.VideoNode,
  443. thresh: int = 128,
  444. blur: int = None,
  445. type: int = 0,
  446. depth: int | list[int] = None,
  447. chroma: int = 0,
  448. planes: list[int] | None = None,
  449. cplace: str = 'mpeg1'
  450. ) -> vs.VideoNode:
  451. """
  452. AWarpSharp2 - pixel-accurate port of dubhater's original.
  453. """
  454. core = vs.core
  455. fmt = clip.format
  456.  
  457. if not fmt:
  458. raise vs.Error("AWarpSharp2: clip must have constant format")
  459.  
  460. if fmt.sample_type != vs.INTEGER or fmt.bits_per_sample > 16 or fmt.color_family == vs.RGB:
  461. raise vs.Error("AWarpSharp2: only 8-16 bit integer, non-RGB clips supported")
  462.  
  463. is_gray = fmt.color_family == vs.GRAY
  464.  
  465. if planes is None:
  466. planes = [0] if is_gray else [0, 1, 2]
  467.  
  468. if depth is None:
  469. depth_list = [16] if is_gray else [16, 8, 8]
  470. elif isinstance(depth, int):
  471. depth_list = [depth]
  472. else:
  473. depth_list = list(depth)
  474.  
  475. while len(depth_list) < fmt.num_planes:
  476. depth_list.append(depth_list[-1])
  477.  
  478. if blur is None:
  479. blur = 3 if type == 1 else 2
  480.  
  481. if thresh < 0 or thresh > 255:
  482. raise vs.Error("AWarpSharp2: thresh must be between 0 and 255")
  483. if blur < 0:
  484. raise vs.Error("AWarpSharp2: blur must be at least 0")
  485. if type < 0 or type > 1:
  486. raise vs.Error("AWarpSharp2: type must be 0 or 1")
  487. for d in depth_list:
  488. if d < -128 or d > 127:
  489. raise vs.Error("AWarpSharp2: depth must be between -128 and 127")
  490. if chroma < 0 or chroma > 1:
  491. raise vs.Error("AWarpSharp2: chroma must be 0 or 1")
  492.  
  493. thresh_scaled = thresh << (fmt.bits_per_sample - 8)
  494.  
  495. def process_frame(n, f):
  496. fout = f.copy()
  497.  
  498. mask_y = None
  499. if 0 in planes or (chroma == 0 and not is_gray and (1 in planes or 2 in planes)):
  500. src_y = np.array(f[0])
  501.  
  502. mask_y = _sobel_c(src_y.copy(), thresh_scaled, fmt.bits_per_sample)
  503.  
  504. temp_y = np.empty_like(mask_y)
  505. bl = blur
  506. for _ in range(bl):
  507. if type == 0:
  508. _blur_r6_c(mask_y, temp_y)
  509. else:
  510. _blur_r2_c(mask_y, temp_y)
  511.  
  512. if 0 in planes:
  513. dst_y = _warp_c(src_y, mask_y, depth_list[0], fmt.bits_per_sample)
  514. _copy_to_plane(fout, 0, dst_y)
  515.  
  516. if not is_gray and (1 in planes or 2 in planes):
  517. if chroma == 0 and mask_y is not None:
  518. # WarpAlongLuma: use luma mask (downscaled if needed)
  519. if fmt.subsampling_w or fmt.subsampling_h:
  520. mask_y_copy = mask_y.copy()
  521. if fmt.subsampling_w and fmt.subsampling_h:
  522. _bilinear_downscale_hv_c(mask_y_copy, cplace)
  523. mask_y_copy = mask_y_copy[:mask_y.shape[0]//2, :mask_y.shape[1]//2]
  524. elif fmt.subsampling_w:
  525. _bilinear_downscale_h_c(mask_y_copy, cplace)
  526. mask_y_copy = mask_y_copy[:, :mask_y.shape[1]//2]
  527. elif fmt.subsampling_h:
  528. _bilinear_downscale_v_c(mask_y_copy)
  529. mask_y_copy = mask_y_copy[:mask_y.shape[0]//2, :]
  530. else:
  531. mask_y_copy = mask_y
  532.  
  533. for plane_idx in [1, 2]:
  534. if plane_idx not in planes:
  535. continue
  536. src_uv = np.array(f[plane_idx])
  537. dst_uv = _warp_c(src_uv, mask_y_copy, depth_list[plane_idx], fmt.bits_per_sample)
  538. _copy_to_plane(fout, plane_idx, dst_uv)
  539. else:
  540. # WarpAlongChroma: each plane has its own mask
  541. for plane_idx in [1, 2]:
  542. if plane_idx not in planes:
  543. continue
  544. src_uv = np.array(f[plane_idx])
  545.  
  546. mask_uv = _sobel_c(src_uv.copy(), thresh_scaled, fmt.bits_per_sample)
  547. temp_uv = np.empty_like(mask_uv)
  548. bl = (blur + 1) // 2
  549. for _ in range(bl):
  550. if type == 0:
  551. _blur_r6_c(mask_uv, temp_uv)
  552. else:
  553. _blur_r2_c(mask_uv, temp_uv)
  554.  
  555. dst_uv = _warp_c(src_uv, mask_uv, depth_list[plane_idx], fmt.bits_per_sample)
  556. _copy_to_plane(fout, plane_idx, dst_uv)
  557.  
  558. return fout
  559.  
  560. return core.std.ModifyFrame(clip=clip, clips=clip, selector=process_frame)
  561.  
  562.  
  563. # VapourSynth R60+ plugin registration
  564. try:
  565. if hasattr(vs, 'register_function'):
  566. @vs.register_function()
  567. def AWarpSharp2_VS(clip: vs.VideoNode, thresh: int = 128, blur: int = None,
  568. type: int = 0, depth: int = None,
  569. chroma: int = 0, planes: list[int] = None,
  570. cplace: str = 'mpeg1') -> vs.VideoNode:
  571. return AWarpSharp2(clip, thresh, blur, type, depth, chroma, planes, cplace)
  572. except Exception:
  573. pass
Advertisement
Add Comment
Please, Sign In to add comment