Upload patch_rife_timestep.py with huggingface_hub
Browse files- 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])
|