walterlow commited on
Commit
7bca327
·
verified ·
1 Parent(s): 7fa12b8

Upload patch_rife_timestep.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. patch_rife_timestep.py +77 -0
patch_rife_timestep.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Expose RIFE's baked t=0.5 as a runtime scalar input.
3
+
4
+ FuryTMP/RIFE_fp32 (MIT) traced its IFNet with timestep=0.5 folded into a Constant, so the
5
+ exported graph takes only 6 channels and can produce the midpoint frame. The timestep plane
6
+ is still built internally as ((Slice * 0) + 1) * 0.5 and fed to the head Concat of every
7
+ IFBlock -- promoting that 0.5 to a graph input restores arbitrary-phase interpolation.
8
+
9
+ Verified: output is bit-identical to the original at t=0.5, and ORT folds the patched graph to
10
+ the same 336 nodes with zero shape-computation ops under freeDimensionOverrides.
11
+
12
+ Usage: python patch_rife_timestep.py RIFE_fp32.onnx RIFE_fp32_timestep.onnx
13
+ Feed the new input as a 0-d array: np.array(t, dtype=np.float32) (not np.float32(t)).
14
+ """
15
+
16
+ import sys
17
+
18
+ import onnx
19
+ from onnx import TensorProto, helper, numpy_helper
20
+
21
+ TIMESTEP_INPUT = "timestep"
22
+
23
+
24
+ def find_baked_timestep(graph):
25
+ """The scalar Constant whose only consumer is the Mul feeding the timestep plane."""
26
+ consumers = {}
27
+ for node in graph.node:
28
+ for name in node.input:
29
+ consumers.setdefault(name, []).append(node)
30
+
31
+ candidates = []
32
+ for node in graph.node:
33
+ if node.op_type != "Constant":
34
+ continue
35
+ value = next((a.t for a in node.attribute if a.name == "value"), None)
36
+ if value is None:
37
+ continue
38
+ array = numpy_helper.to_array(value)
39
+ if array.shape != () or not (0.0 < float(array) < 1.0):
40
+ continue
41
+ users = consumers.get(node.output[0], [])
42
+ if len(users) == 1 and users[0].op_type == "Mul":
43
+ candidates.append((node, float(array), users[0]))
44
+
45
+ if len(candidates) != 1:
46
+ raise SystemExit(
47
+ f"expected exactly one baked-timestep Constant, found {len(candidates)}: "
48
+ f"{[(n.name, v) for n, v, _ in candidates]}"
49
+ )
50
+ return candidates[0]
51
+
52
+
53
+ def main(src, dst):
54
+ model = onnx.load(src)
55
+ graph = model.graph
56
+
57
+ if any(i.name == TIMESTEP_INPUT for i in graph.input):
58
+ raise SystemExit(f"{src} already exposes a '{TIMESTEP_INPUT}' input")
59
+
60
+ const_node, baked_value, mul_node = find_baked_timestep(graph)
61
+ print(f"found {const_node.name} = {baked_value} -> consumed by {mul_node.name}")
62
+
63
+ graph.node.remove(const_node)
64
+ for i, name in enumerate(mul_node.input):
65
+ if name == const_node.output[0]:
66
+ mul_node.input[i] = TIMESTEP_INPUT
67
+ graph.input.append(helper.make_tensor_value_info(TIMESTEP_INPUT, TensorProto.FLOAT, []))
68
+
69
+ onnx.checker.check_model(model, full_check=True)
70
+ onnx.save(model, dst)
71
+ print(f"wrote {dst}; inputs = {[i.name for i in graph.input]}")
72
+
73
+
74
+ if __name__ == "__main__":
75
+ if len(sys.argv) != 3:
76
+ raise SystemExit(__doc__)
77
+ main(sys.argv[1], sys.argv[2])