MySafeCode commited on
Commit
2fe287e
·
verified ·
1 Parent(s): 73e3b5e

Upload 3 files

Browse files
Files changed (4) hide show
  1. .gitattributes +1 -0
  2. app.py +203 -0
  3. requirements.txt +4 -0
  4. tmpnignsigm.mp4 +3 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ tmpnignsigm.mp4 filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import cv2
3
+ import numpy as np
4
+ import trimesh
5
+ import tempfile
6
+ import os
7
+
8
+ # -------------------------
9
+ # GLOBAL (checkerboard persistence)
10
+ # -------------------------
11
+ _checkerboard_colors = None
12
+
13
+ # -------------------------
14
+ # VIDEO LOADING (BGR → RGB FIXED ✅)
15
+ # -------------------------
16
+ def read_video_frames(video_path, start=0, end=None, frame_step=1):
17
+ cap = cv2.VideoCapture(video_path)
18
+ frames = []
19
+
20
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
21
+ if end is None:
22
+ end = total_frames
23
+
24
+ count = 0
25
+
26
+ while True:
27
+ ret, frame = cap.read()
28
+ if not ret or count >= end:
29
+ break
30
+
31
+ if count >= start and (count - start) % frame_step == 0:
32
+ # FIX COLOR ORDER HERE
33
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
34
+ frames.append(frame)
35
+
36
+ count += 1
37
+
38
+ cap.release()
39
+ return np.array(frames)
40
+
41
+
42
+ # -------------------------
43
+ # DOWNSAMPLING
44
+ # -------------------------
45
+ def downsample_frames(frames, block_size=1, method='stride'):
46
+ if block_size == 1:
47
+ return frames
48
+
49
+ z, h, w, c = frames.shape
50
+
51
+ if method == 'stride':
52
+ return frames[:, ::block_size, ::block_size]
53
+
54
+ elif method == 'mean':
55
+ new_h = h // block_size
56
+ new_w = w // block_size
57
+ out = np.zeros((z, new_h, new_w, c), dtype=np.uint8)
58
+
59
+ for zi in range(z):
60
+ for i in range(new_h):
61
+ for j in range(new_w):
62
+ block = frames[
63
+ zi,
64
+ i*block_size:(i+1)*block_size,
65
+ j*block_size:(j+1)*block_size
66
+ ]
67
+ out[zi, i, j] = block.mean(axis=(0,1))
68
+ return out
69
+
70
+
71
+ # -------------------------
72
+ # VOXEL MASK
73
+ # -------------------------
74
+ def frames_to_voxels(frames, threshold=10):
75
+ return (np.sum(frames, axis=3) > threshold)
76
+
77
+
78
+ # -------------------------
79
+ # VOXEL → MESH (FIXED COLORS ✅)
80
+ # -------------------------
81
+ def voxels_to_mesh(frames, voxels, voxel_size=1.0):
82
+ meshes = []
83
+ z_len, h, w = voxels.shape
84
+
85
+ for z in range(z_len):
86
+ for y in range(h):
87
+ for x in range(w):
88
+ if voxels[z, y, x]:
89
+ color = frames[z, frames.shape[1] - 1 - y, x].astype(np.uint8)
90
+
91
+ cube = trimesh.creation.box(extents=[voxel_size]*3)
92
+ cube.apply_translation([x, y, z])
93
+
94
+ # Apply colors correctly (RGBA uint8)
95
+ rgba = np.append(color, 255)
96
+ cube.visual.face_colors = np.tile(rgba, (12,1))
97
+
98
+ meshes.append(cube)
99
+
100
+ if meshes:
101
+ return trimesh.util.concatenate(meshes)
102
+ return trimesh.Scene()
103
+
104
+
105
+ # -------------------------
106
+ # RANDOM CHECKERBOARD (ONE-TIME COLORS ✅)
107
+ # -------------------------
108
+ def default_checkerboard():
109
+ global _checkerboard_colors
110
+
111
+ h, w, z_len = 10, 10, 2
112
+ frames = np.zeros((z_len, h, w, 3), dtype=np.uint8)
113
+
114
+ if _checkerboard_colors is None:
115
+ _checkerboard_colors = np.random.randint(
116
+ 0, 256, size=(z_len, h, w, 3), dtype=np.uint8
117
+ )
118
+
119
+ for z in range(z_len):
120
+ for y in range(h):
121
+ for x in range(w):
122
+ if (x + y + z) % 2 == 0:
123
+ frames[z, y, x] = [0, 0, 0]
124
+ else:
125
+ frames[z, y, x] = _checkerboard_colors[z, y, x]
126
+
127
+ voxels = frames_to_voxels(frames, threshold=1)
128
+ mesh = voxels_to_mesh(frames, voxels, voxel_size=2)
129
+
130
+ tmp = tempfile.gettempdir()
131
+ obj = os.path.join(tmp, "checkerboard.obj")
132
+ glb = os.path.join(tmp, "checkerboard.glb")
133
+
134
+ mesh.export(obj)
135
+ mesh.export(glb)
136
+
137
+ return obj, glb, glb
138
+
139
+
140
+ # -------------------------
141
+ # MAIN GENERATOR
142
+ # -------------------------
143
+ def generate_voxel_files(
144
+ video_file,
145
+ start_frame,
146
+ end_frame,
147
+ frame_step,
148
+ block_size,
149
+ downsample_method
150
+ ):
151
+ if video_file is None:
152
+ return default_checkerboard()
153
+
154
+ frames = read_video_frames(
155
+ video_file.name,
156
+ start=start_frame,
157
+ end=end_frame,
158
+ frame_step=frame_step
159
+ )
160
+
161
+ frames = downsample_frames(
162
+ frames,
163
+ block_size=block_size,
164
+ method=downsample_method
165
+ )
166
+
167
+ voxels = frames_to_voxels(frames)
168
+ mesh = voxels_to_mesh(frames, voxels)
169
+
170
+ tmp = tempfile.gettempdir()
171
+ obj = os.path.join(tmp, "output.obj")
172
+ glb = os.path.join(tmp, "output.glb")
173
+
174
+ mesh.export(obj)
175
+ mesh.export(glb)
176
+
177
+ return obj, glb, glb
178
+
179
+
180
+ # -------------------------
181
+ # GRADIO UI
182
+ # -------------------------
183
+ iface = gr.Interface(
184
+ fn=generate_voxel_files,
185
+ inputs=[
186
+ gr.File(label="Upload MP4 (or leave empty for checkerboard)"),
187
+ gr.Slider(0, 500, value=0, step=1, label="Start Frame"),
188
+ gr.Slider(0, 500, value=50, step=1, label="End Frame"),
189
+ gr.Slider(1, 10, value=1, step=1, label="Frame Step"),
190
+ gr.Slider(1, 32, value=1, step=1, label="Pixel Block Size"),
191
+ gr.Radio(["stride", "mean"], value="stride", label="Downsample Method"),
192
+ ],
193
+ outputs=[
194
+ gr.File(label="OBJ"),
195
+ gr.File(label="GLB"),
196
+ gr.Model3D(label="3D Preview"),
197
+ ],
198
+ title="MP4 → Voxels → 3D",
199
+ description="If no file is uploaded, a random-color checkerboard appears."
200
+ )
201
+
202
+ if __name__ == "__main__":
203
+ iface.launch()
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio>=6.0
2
+ opencv-python
3
+ trimesh
4
+ numpy
tmpnignsigm.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:aee9d9cca960196b044ce151c7e9dcab598a15c18d425b15de4ce81d8acc5073
3
+ size 946080