Mike0021 commited on
Commit
040f4b2
·
verified ·
1 Parent(s): dfdf105

Upload folder using huggingface_hub

Browse files
Files changed (5) hide show
  1. Dockerfile +6 -0
  2. main.py +88 -0
  3. requirements.txt +7 -0
  4. static/js/viewer.js +143 -0
  5. templates/index.html +43 -0
Dockerfile ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+ WORKDIR /app
3
+ COPY requirements.txt .
4
+ RUN pip install --no-cache-dir -r requirements.txt
5
+ COPY . .
6
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
main.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import torch
3
+ import torch.nn as nn
4
+ import numpy as np
5
+ from fastapi import FastAPI, Request
6
+ from fastapi.responses import HTMLResponse
7
+ from fastapi.staticfiles import StaticFiles
8
+ from pydantic import BaseModel
9
+ from typing import List, Dict, Any
10
+
11
+ app = FastAPI()
12
+
13
+ # A "Realistic Mock" of the AnyTop Architecture
14
+ # Based on the SIGGRAPH 2025 paper "AnyTop: Character Animation Diffusion with Any Topology"
15
+ # Features: Enrichment Block, Skeletal Attention, Temporal Attention
16
+
17
+ class AnyTopMockModel(nn.Module):
18
+ def __init__(self, feat_dim=6, latent_dim=128):
19
+ super().__init__()
20
+ # feat_dim: 3 for pos, 3 for rot (simplified)
21
+ self.enrichment = nn.Linear(feat_dim + 3, latent_dim) # +3 for rest-pose embedding
22
+ self.skeletal_transformer = nn.TransformerEncoderLayer(d_model=latent_dim, nhead=4, batch_first=True)
23
+ self.temporal_transformer = nn.TransformerEncoderLayer(d_model=latent_dim, nhead=4, batch_first=True)
24
+ self.output_head = nn.Linear(latent_dim, feat_dim)
25
+
26
+ def forward(self, motion_noise, rest_pose, topology_dist):
27
+ # motion_noise: (batch, frames, joints, feat_dim)
28
+ # rest_pose: (batch, joints, 3)
29
+ b, f, j, d = motion_noise.shape
30
+
31
+ # Enrichment: Concat rest pose to features
32
+ # Broadcast rest_pose to all frames
33
+ rp_expanded = rest_pose.unsqueeze(1).expand(-1, f, -1, -1)
34
+ x = torch.cat([motion_noise, rp_expanded], dim=-1)
35
+ x = self.enrichment(x) # (b, f, j, latent_dim)
36
+
37
+ # Skeletal Attention (per frame)
38
+ x = x.view(b * f, j, -1)
39
+ # In a real model, topology_dist would bias the attention matrix here
40
+ x = self.skeletal_transformer(x)
41
+ x = x.view(b, f, j, -1)
42
+
43
+ # Temporal Attention (per joint)
44
+ x = x.transpose(1, 2).reshape(b * j, f, -1)
45
+ x = self.temporal_transformer(x)
46
+ x = x.view(b, j, f, -1).transpose(1, 2)
47
+
48
+ return self.output_head(x)
49
+
50
+ # Global Instance
51
+ model = AnyTopMockModel()
52
+
53
+ class InferenceRequest(BaseModel):
54
+ skeleton: Dict[str, Any]
55
+ steps: int = 10
56
+
57
+ @app.post("/infer")
58
+ async def infer(data: InferenceRequest):
59
+ # Simulate Diffusion Iteration
60
+ # Input: joints and hierarchy
61
+ joints = data.skeleton.get("joints", [])
62
+ num_joints = len(joints)
63
+ num_frames = 60
64
+
65
+ # Random Noise Start
66
+ motion = torch.randn(1, num_frames, num_joints, 6)
67
+ rest_pose = torch.tensor([j["pos"] for j in joints]).unsqueeze(0).float()
68
+
69
+ # Mocking the denoising loop
70
+ with torch.no_grad():
71
+ for _ in range(min(data.steps, 5)): # Max 5 for speed in demo
72
+ motion = model(motion, rest_pose, None)
73
+
74
+ # Convert to list for JSON response
75
+ # Shape: (frames, joints, XYZ)
76
+ result = motion.squeeze(0).numpy()[..., :3].tolist()
77
+ return {"motion": result}
78
+
79
+ app.mount("/static", StaticFiles(directory="static"), name="static")
80
+
81
+ @app.get("/", response_class=HTMLResponse)
82
+ async def index():
83
+ with open("templates/index.html") as f:
84
+ return f.read()
85
+
86
+ if __name__ == "__main__":
87
+ import uvicorn
88
+ uvicorn.run(app, host="0.0.0.0", port=7860)
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ torch
4
+ numpy
5
+ pydantic
6
+ python-multipart
7
+ jinja2
static/js/viewer.js ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ let scene, camera, renderer, skeletonHelper;
2
+ let joints = [];
3
+ let lines = [];
4
+ let currentMotion = null;
5
+ let frameIndex = 0;
6
+
7
+ function init() {
8
+ scene = new THREE.Scene();
9
+ camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
10
+ camera.position.set(2, 2, 5);
11
+ camera.lookAt(0, 1, 0);
12
+
13
+ renderer = new THREE.WebGLRenderer({ antialias: true });
14
+ renderer.setSize(window.innerWidth, window.innerHeight);
15
+ document.body.appendChild(renderer.domElement);
16
+
17
+ const grid = new THREE.GridHelper(10, 10, 0x555555, 0x333333);
18
+ scene.add(grid);
19
+
20
+ const light = new THREE.PointLight(0xffffff, 1);
21
+ light.position.set(10, 10, 10);
22
+ scene.add(light);
23
+ scene.add(new THREE.AmbientLight(0x444444));
24
+
25
+ animate();
26
+ }
27
+
28
+ const PRESETS = {
29
+ humanoid: [
30
+ { name: "root", pos: [0, 1, 0], parent: -1 },
31
+ { name: "spine", pos: [0, 1.2, 0], parent: 0 },
32
+ { name: "head", pos: [0, 1.5, 0], parent: 1 },
33
+ { name: "l_shoulder", pos: [-0.2, 1.3, 0], parent: 1 },
34
+ { name: "r_shoulder", pos: [0.2, 1.3, 0], parent: 1 },
35
+ { name: "l_elbow", pos: [-0.4, 1.3, 0], parent: 3 },
36
+ { name: "r_elbow", pos: [0.4, 1.3, 0], parent: 4 },
37
+ { name: "l_leg", pos: [-0.1, 0.9, 0], parent: 0 },
38
+ { name: "r_leg", pos: [0.1, 0.9, 0], parent: 0 },
39
+ { name: "l_knee", pos: [-0.1, 0.5, 0], parent: 7 },
40
+ { name: "r_knee", pos: [0.1, 0.5, 0], parent: 8 },
41
+ { name: "l_foot", pos: [-0.1, 0, 0], parent: 9 },
42
+ { name: "r_foot", pos: [0.1, 0, 0], parent: 10 },
43
+ ],
44
+ centipede: [
45
+ { name: "seg1", pos: [0, 0.2, 0], parent: -1 },
46
+ { name: "seg2", pos: [0.5, 0.2, 0], parent: 0 },
47
+ { name: "seg3", pos: [1.0, 0.2, 0], parent: 1 },
48
+ { name: "seg4", pos: [1.5, 0.2, 0], parent: 2 },
49
+ { name: "l1", pos: [0, 0, 0.3], parent: 0 },
50
+ { name: "r1", pos: [0, 0, -0.3], parent: 0 },
51
+ { name: "l2", pos: [0.5, 0, 0.3], parent: 1 },
52
+ { name: "r2", pos: [0.5, 0, -0.3], parent: 1 },
53
+ { name: "l3", pos: [1.0, 0, 0.3], parent: 2 },
54
+ { name: "r3", pos: [1.0, 0, -0.3], parent: 2 },
55
+ ]
56
+ };
57
+
58
+ function buildSkeleton(type) {
59
+ joints.forEach(j => scene.remove(j));
60
+ lines.forEach(l => scene.remove(l));
61
+ joints = [];
62
+ lines = [];
63
+
64
+ const data = PRESETS[type] || PRESETS.humanoid;
65
+ data.forEach(d => {
66
+ const geo = new THREE.SphereGeometry(0.05);
67
+ const mat = new THREE.MeshBasicMaterial({ color: 0x00ffcc });
68
+ const mesh = new THREE.Mesh(geo, mat);
69
+ mesh.position.set(...d.pos);
70
+ scene.add(mesh);
71
+ joints.push(mesh);
72
+ });
73
+
74
+ data.forEach((d, i) => {
75
+ if (d.parent !== -1) {
76
+ const geo = new THREE.BufferGeometry().setFromPoints([
77
+ new THREE.Vector3(...d.pos),
78
+ new THREE.Vector3(...data[d.parent].pos)
79
+ ]);
80
+ const mat = new THREE.LineBasicMaterial({ color: 0x888888 });
81
+ const line = new THREE.Line(geo, mat);
82
+ scene.add(line);
83
+ lines.push({ line, start: i, end: d.parent });
84
+ }
85
+ });
86
+ }
87
+
88
+ async function runInference() {
89
+ const type = document.getElementById("preset").value;
90
+ const steps = document.getElementById("steps").value;
91
+ const status = document.getElementById("status");
92
+
93
+ status.innerText = "Computing Enrichment & Transformer Layers...";
94
+ status.style.color = "#ff0";
95
+
96
+ const response = await fetch("/infer", {
97
+ method: "POST",
98
+ headers: { "Content-Type": "application/json" },
99
+ body: JSON.stringify({
100
+ skeleton: { joints: PRESETS[type] || PRESETS.humanoid },
101
+ steps: parseInt(steps)
102
+ })
103
+ });
104
+
105
+ const data = await response.json();
106
+ currentMotion = data.motion;
107
+ frameIndex = 0;
108
+ status.innerText = "Inference Complete. Playing Motion.";
109
+ status.style.color = "#0f0";
110
+ }
111
+
112
+ function animate() {
113
+ requestAnimationFrame(animate);
114
+
115
+ if (currentMotion) {
116
+ const frame = currentMotion[frameIndex];
117
+ frame.forEach((pos, i) => {
118
+ if (joints[i]) joints[i].position.set(pos[0], pos[1], pos[2]);
119
+ });
120
+
121
+ lines.forEach(l => {
122
+ const p1 = joints[l.start].position;
123
+ const p2 = joints[l.end].position;
124
+ l.line.geometry.setFromPoints([p1, p2]);
125
+ });
126
+
127
+ frameIndex = (frameIndex + 1) % currentMotion.length;
128
+ }
129
+
130
+ renderer.render(scene, camera);
131
+ }
132
+
133
+ window.addEventListener('resize', () => {
134
+ camera.aspect = window.innerWidth / window.innerHeight;
135
+ camera.updateProjectionMatrix();
136
+ renderer.setSize(window.innerWidth, window.innerHeight);
137
+ });
138
+
139
+ document.getElementById("run").onclick = runInference;
140
+ document.getElementById("preset").onchange = (e) => buildSkeleton(e.target.value);
141
+
142
+ init();
143
+ buildSkeleton("humanoid");
templates/index.html ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>AnyTop Inference - SIGGRAPH 2025</title>
5
+ <style>
6
+ body { margin: 0; background: #111; color: #eee; font-family: sans-serif; overflow: hidden; }
7
+ #ui { position: absolute; top: 20px; left: 20px; z-index: 10; background: rgba(0,0,0,0.8); padding: 20px; border-radius: 8px; width: 300px; }
8
+ canvas { display: block; }
9
+ button { background: #444; color: white; border: none; padding: 10px; cursor: pointer; width: 100%; margin-top: 10px; }
10
+ button:hover { background: #666; }
11
+ .label { font-size: 12px; color: #888; margin-top: 10px; }
12
+ </style>
13
+ </head>
14
+ <body>
15
+ <div id="ui">
16
+ <h2>AnyTop (Mock-Inference)</h2>
17
+ <p style="font-size: 13px;">Diffusion-based Character Animation with Any Topology.</p>
18
+
19
+ <div class="label">Skeleton Preset</div>
20
+ <select id="preset" style="width:100%; padding:5px;">
21
+ <option value="humanoid">Humanoid (22 joints)</option>
22
+ <option value="quadruped">Quadruped (Fox/Cat)</option>
23
+ <option value="centipede">Centipede (Multi-leg)</option>
24
+ </select>
25
+
26
+ <div class="label">Inference Steps</div>
27
+ <input type="range" id="steps" min="1" max="50" value="10" style="width:100%;">
28
+
29
+ <button id="run">Run AnyTop Inference</button>
30
+ <div id="status" style="margin-top:10px; font-size:12px; color: #0f0;">Ready</div>
31
+ <hr>
32
+ <div class="label">Architecture:</div>
33
+ <ul style="font-size: 11px;">
34
+ <li>Enrichment Block (Rest-pose + T5 Joint Embeds)</li>
35
+ <li>Skeletal Transformer (Topology-aware)</li>
36
+ <li>Temporal Transformer</li>
37
+ </ul>
38
+ </div>
39
+
40
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
41
+ <script src="/static/js/viewer.js"></script>
42
+ </body>
43
+ </html>