wwieerrz Claude commited on
Commit
1e152b4
Β·
1 Parent(s): 55d3f0e

COMPLETE FIX: Real AI + All Video Features!

Browse files

FIX 1 - NameError fixed:
- Use session state to store depth maps
- No more undefined variable errors

FIX 2 - Remove colormap selector:
- BASE model uses Inferno colormap (best for depth)
- Cleaner UI, one less thing to configure

FIX 3 - Add ALL camera effects:
βœ… Zoom In/Out - Smooth zoom controls
βœ… Pan Left/Right/Up/Down - 4-way panning
βœ… Dolly In/Out - Professional cinema shots
βœ… Tilt Up/Down - Perspective tilt with transforms
βœ… Rotate CW/CCW - Clockwise/counter-clockwise
βœ… Ken Burns - Classic zoom + pan effect
βœ… Orbit - Smooth orbital rotation with scale

All effects work with:
- Duration: 1-10 seconds
- FPS: 24/30/60
- Resolution: Original/1080p/720p/Square
- Download as MP4

This is the COMPLETE professional video export!

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Files changed (1) hide show
  1. app.py +129 -35
app.py CHANGED
@@ -52,8 +52,11 @@ def load_model():
52
  depth_estimator, USE_REAL_AI, MODEL_SIZE = load_model()
53
 
54
 
55
- def estimate_depth(image, colormap_style):
56
  """Estimate depth from an input image using REAL AI or DEMO MODE"""
 
 
 
57
  try:
58
  # Convert PIL to numpy if needed
59
  if isinstance(image, Image.Image):
@@ -68,20 +71,8 @@ def estimate_depth(image, colormap_style):
68
  depth = generate_smart_depth(image)
69
  mode_text = "DEMO MODE (Synthetic)"
70
 
71
- # Convert colormap style to cv2 constant
72
- colormap_dict = {
73
- "Inferno": cv2.COLORMAP_INFERNO,
74
- "Viridis": cv2.COLORMAP_VIRIDIS,
75
- "Plasma": cv2.COLORMAP_PLASMA,
76
- "Turbo": cv2.COLORMAP_TURBO,
77
- "Magma": cv2.COLORMAP_MAGMA,
78
- "Hot": cv2.COLORMAP_HOT,
79
- "Ocean": cv2.COLORMAP_OCEAN,
80
- "Rainbow": cv2.COLORMAP_RAINBOW
81
- }
82
-
83
- # Create colored depth map
84
- depth_colored = depth_to_colormap(depth, colormap_dict[colormap_style])
85
 
86
  # Create grayscale depth map
87
  depth_gray = (depth * 255).astype(np.uint8)
@@ -114,17 +105,10 @@ col1, col2 = st.columns(2)
114
  with col1:
115
  st.subheader("Input")
116
  uploaded_file = st.file_uploader("Upload Your Image", type=['png', 'jpg', 'jpeg'])
117
-
118
- colormap_style = st.selectbox(
119
- "Colormap Style",
120
- ["Inferno", "Viridis", "Plasma", "Turbo", "Magma", "Hot", "Ocean", "Rainbow"]
121
- )
122
-
123
  process_btn = st.button("πŸš€ Generate Depth Map", type="primary")
124
 
125
  with col2:
126
  st.subheader("Output")
127
- depth_placeholder = st.empty()
128
 
129
  # Processing
130
  if uploaded_file is not None and process_btn:
@@ -135,9 +119,14 @@ if uploaded_file is not None and process_btn:
135
  st.image(image, caption="Original Image", use_column_width=True)
136
 
137
  with st.spinner("Generating depth map..."):
138
- depth_colored, depth_gray, mode_text, input_shape, output_shape = estimate_depth(image, colormap_style)
139
 
140
  if depth_colored is not None:
 
 
 
 
 
141
  with col2:
142
  tab1, tab2 = st.tabs(["Colored", "Grayscale"])
143
 
@@ -153,7 +142,6 @@ if uploaded_file is not None and process_btn:
153
  **Mode**: {mode_text}
154
  **Input Size**: {input_shape[1]}x{input_shape[0]}
155
  **Output Size**: {output_shape[1]}x{output_shape[0]}
156
- **Colormap**: {colormap_style}
157
  {f'**Powered by**: Depth-Anything V2 {MODEL_SIZE}' if USE_REAL_AI else '**Processing**: Ultra-fast (<50ms) synthetic depth'}
158
  """)
159
 
@@ -161,17 +149,32 @@ if uploaded_file is not None and process_btn:
161
  st.markdown("---")
162
  st.subheader("🎬 Video Export")
163
 
164
- if uploaded_file is not None and depth_colored is not None:
165
- with st.expander("Export Depth Map as Video"):
166
  col_vid1, col_vid2 = st.columns(2)
167
 
168
  with col_vid1:
169
  video_duration = st.slider("Duration (seconds)", 1, 10, 3)
170
  video_fps = st.selectbox("FPS", [24, 30, 60], index=1)
 
171
 
172
  with col_vid2:
173
- video_resolution = st.selectbox("Resolution", ["Original", "1080p", "720p", "Square 1080p"])
174
- video_effect = st.selectbox("Effect", ["Zoom In", "Zoom Out", "Pan Left", "Pan Right", "Rotate"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
 
176
  if st.button("🎬 Export Video", type="primary"):
177
  with st.spinner("Generating video..."):
@@ -179,6 +182,8 @@ if uploaded_file is not None and depth_colored is not None:
179
  import cv2
180
  import tempfile
181
 
 
 
182
  # Get dimensions
183
  if video_resolution == "1080p":
184
  width, height = 1920, 1080
@@ -206,7 +211,7 @@ if uploaded_file is not None and depth_colored is not None:
206
 
207
  # Apply effect
208
  if video_effect == "Zoom In":
209
- scale = 1.0 + (progress * 0.5) # Zoom from 1x to 1.5x
210
  center_x, center_y = width // 2, height // 2
211
  new_w, new_h = int(width / scale), int(height / scale)
212
  x1, y1 = center_x - new_w // 2, center_y - new_h // 2
@@ -215,7 +220,39 @@ if uploaded_file is not None and depth_colored is not None:
215
  frame = cv2.resize(cropped, (width, height))
216
 
217
  elif video_effect == "Zoom Out":
218
- scale = 1.5 - (progress * 0.5) # Zoom from 1.5x to 1x
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  center_x, center_y = width // 2, height // 2
220
  new_w, new_h = int(width / scale), int(height / scale)
221
  x1, y1 = center_x - new_w // 2, center_y - new_h // 2
@@ -231,12 +268,59 @@ if uploaded_file is not None and depth_colored is not None:
231
  offset = int(width * progress * 0.3)
232
  frame = np.roll(depth_resized, offset, axis=1)
233
 
234
- elif video_effect == "Rotate":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  angle = progress * 360
236
  center = (width // 2, height // 2)
237
  rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
238
  frame = cv2.warpAffine(depth_resized, rotation_matrix, (width, height))
239
 
 
 
 
 
 
 
 
 
240
  else:
241
  frame = depth_resized.copy()
242
 
@@ -254,7 +338,7 @@ if uploaded_file is not None and depth_colored is not None:
254
  st.download_button(
255
  label="πŸ“₯ Download Video",
256
  data=video_bytes,
257
- file_name=f"depth_video_{video_effect.lower().replace(' ', '_')}.mp4",
258
  mime="video/mp4"
259
  )
260
 
@@ -262,6 +346,8 @@ if uploaded_file is not None and depth_colored is not None:
262
  st.error(f"Error generating video: {str(e)}")
263
  import traceback
264
  traceback.print_exc()
 
 
265
 
266
  # Info section
267
  st.markdown("---")
@@ -269,11 +355,19 @@ st.markdown("""
269
  ## πŸ’‘ About DimensioDepth
270
 
271
  ### Features:
272
- - βœ… Real AI depth estimation with Depth-Anything V2
273
- - βœ… Multiple colormap styles for visualization
274
  - βœ… Fast processing (~800ms on CPU, ~200ms on GPU)
275
  - βœ… SUPERB quality depth maps
276
- - βœ… **NEW!** Video export with camera effects
 
 
 
 
 
 
 
 
 
277
 
278
  ### Use Cases:
279
  - 🎨 **Creative & Artistic**: Depth-enhanced photos, 3D effects
 
52
  depth_estimator, USE_REAL_AI, MODEL_SIZE = load_model()
53
 
54
 
55
+ def estimate_depth(image):
56
  """Estimate depth from an input image using REAL AI or DEMO MODE"""
57
+ if image is None:
58
+ return None, None, "Please upload an image first"
59
+
60
  try:
61
  # Convert PIL to numpy if needed
62
  if isinstance(image, Image.Image):
 
71
  depth = generate_smart_depth(image)
72
  mode_text = "DEMO MODE (Synthetic)"
73
 
74
+ # Create colored depth map with Inferno colormap (best for depth)
75
+ depth_colored = depth_to_colormap(depth, cv2.COLORMAP_INFERNO)
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
  # Create grayscale depth map
78
  depth_gray = (depth * 255).astype(np.uint8)
 
105
  with col1:
106
  st.subheader("Input")
107
  uploaded_file = st.file_uploader("Upload Your Image", type=['png', 'jpg', 'jpeg'])
 
 
 
 
 
 
108
  process_btn = st.button("πŸš€ Generate Depth Map", type="primary")
109
 
110
  with col2:
111
  st.subheader("Output")
 
112
 
113
  # Processing
114
  if uploaded_file is not None and process_btn:
 
119
  st.image(image, caption="Original Image", use_column_width=True)
120
 
121
  with st.spinner("Generating depth map..."):
122
+ depth_colored, depth_gray, mode_text, input_shape, output_shape = estimate_depth(image)
123
 
124
  if depth_colored is not None:
125
+ # Store in session state for video export
126
+ st.session_state['depth_colored'] = depth_colored
127
+ st.session_state['depth_gray'] = depth_gray
128
+ st.session_state['original_image'] = np.array(image)
129
+
130
  with col2:
131
  tab1, tab2 = st.tabs(["Colored", "Grayscale"])
132
 
 
142
  **Mode**: {mode_text}
143
  **Input Size**: {input_shape[1]}x{input_shape[0]}
144
  **Output Size**: {output_shape[1]}x{output_shape[0]}
 
145
  {f'**Powered by**: Depth-Anything V2 {MODEL_SIZE}' if USE_REAL_AI else '**Processing**: Ultra-fast (<50ms) synthetic depth'}
146
  """)
147
 
 
149
  st.markdown("---")
150
  st.subheader("🎬 Video Export")
151
 
152
+ if 'depth_colored' in st.session_state:
153
+ with st.expander("Export Depth Map as Video", expanded=True):
154
  col_vid1, col_vid2 = st.columns(2)
155
 
156
  with col_vid1:
157
  video_duration = st.slider("Duration (seconds)", 1, 10, 3)
158
  video_fps = st.selectbox("FPS", [24, 30, 60], index=1)
159
+ video_resolution = st.selectbox("Resolution", ["Original", "1080p", "720p", "Square 1080p"])
160
 
161
  with col_vid2:
162
+ video_effect = st.selectbox("Camera Effect", [
163
+ "Zoom In",
164
+ "Zoom Out",
165
+ "Pan Left",
166
+ "Pan Right",
167
+ "Pan Up",
168
+ "Pan Down",
169
+ "Rotate CW",
170
+ "Rotate CCW",
171
+ "Ken Burns (Zoom + Pan)",
172
+ "Dolly In",
173
+ "Dolly Out",
174
+ "Tilt Up",
175
+ "Tilt Down",
176
+ "Orbit"
177
+ ])
178
 
179
  if st.button("🎬 Export Video", type="primary"):
180
  with st.spinner("Generating video..."):
 
182
  import cv2
183
  import tempfile
184
 
185
+ depth_colored = st.session_state['depth_colored']
186
+
187
  # Get dimensions
188
  if video_resolution == "1080p":
189
  width, height = 1920, 1080
 
211
 
212
  # Apply effect
213
  if video_effect == "Zoom In":
214
+ scale = 1.0 + (progress * 0.5)
215
  center_x, center_y = width // 2, height // 2
216
  new_w, new_h = int(width / scale), int(height / scale)
217
  x1, y1 = center_x - new_w // 2, center_y - new_h // 2
 
220
  frame = cv2.resize(cropped, (width, height))
221
 
222
  elif video_effect == "Zoom Out":
223
+ scale = 1.5 - (progress * 0.5)
224
+ center_x, center_y = width // 2, height // 2
225
+ new_w, new_h = int(width / scale), int(height / scale)
226
+ x1, y1 = center_x - new_w // 2, center_y - new_h // 2
227
+ x2, y2 = x1 + new_w, y1 + new_h
228
+ cropped = depth_resized[max(0, y1):min(height, y2), max(0, x1):min(width, x2)]
229
+ frame = cv2.resize(cropped, (width, height))
230
+
231
+ elif video_effect == "Ken Burns (Zoom + Pan)":
232
+ # Ken Burns: zoom in while panning
233
+ scale = 1.0 + (progress * 0.4)
234
+ pan_x = int(width * progress * 0.2)
235
+ pan_y = int(height * progress * 0.1)
236
+ center_x = width // 2 + pan_x
237
+ center_y = height // 2 + pan_y
238
+ new_w, new_h = int(width / scale), int(height / scale)
239
+ x1, y1 = center_x - new_w // 2, center_y - new_h // 2
240
+ x2, y2 = x1 + new_w, y1 + new_h
241
+ cropped = depth_resized[max(0, y1):min(height, y2), max(0, x1):min(width, x2)]
242
+ frame = cv2.resize(cropped, (width, height))
243
+
244
+ elif video_effect == "Dolly In":
245
+ # Dolly in: smooth zoom with slight scale
246
+ scale = 1.0 + (progress * 0.3)
247
+ center_x, center_y = width // 2, height // 2
248
+ new_w, new_h = int(width / scale), int(height / scale)
249
+ x1, y1 = center_x - new_w // 2, center_y - new_h // 2
250
+ x2, y2 = x1 + new_w, y1 + new_h
251
+ cropped = depth_resized[max(0, y1):min(height, y2), max(0, x1):min(width, x2)]
252
+ frame = cv2.resize(cropped, (width, height))
253
+
254
+ elif video_effect == "Dolly Out":
255
+ scale = 1.3 - (progress * 0.3)
256
  center_x, center_y = width // 2, height // 2
257
  new_w, new_h = int(width / scale), int(height / scale)
258
  x1, y1 = center_x - new_w // 2, center_y - new_h // 2
 
268
  offset = int(width * progress * 0.3)
269
  frame = np.roll(depth_resized, offset, axis=1)
270
 
271
+ elif video_effect == "Pan Up":
272
+ offset = int(height * progress * 0.3)
273
+ frame = np.roll(depth_resized, -offset, axis=0)
274
+
275
+ elif video_effect == "Pan Down":
276
+ offset = int(height * progress * 0.3)
277
+ frame = np.roll(depth_resized, offset, axis=0)
278
+
279
+ elif video_effect == "Tilt Up":
280
+ # Tilt up: perspective transformation
281
+ tilt_factor = progress * 0.3
282
+ pts1 = np.float32([[0, 0], [width, 0], [0, height], [width, height]])
283
+ pts2 = np.float32([
284
+ [0, int(height * tilt_factor)],
285
+ [width, int(height * tilt_factor)],
286
+ [0, height],
287
+ [width, height]
288
+ ])
289
+ matrix = cv2.getPerspectiveTransform(pts1, pts2)
290
+ frame = cv2.warpPerspective(depth_resized, matrix, (width, height))
291
+
292
+ elif video_effect == "Tilt Down":
293
+ tilt_factor = progress * 0.3
294
+ pts1 = np.float32([[0, 0], [width, 0], [0, height], [width, height]])
295
+ pts2 = np.float32([
296
+ [0, 0],
297
+ [width, 0],
298
+ [0, height - int(height * tilt_factor)],
299
+ [width, height - int(height * tilt_factor)]
300
+ ])
301
+ matrix = cv2.getPerspectiveTransform(pts1, pts2)
302
+ frame = cv2.warpPerspective(depth_resized, matrix, (width, height))
303
+
304
+ elif video_effect == "Rotate CW":
305
+ angle = progress * 360
306
+ center = (width // 2, height // 2)
307
+ rotation_matrix = cv2.getRotationMatrix2D(center, -angle, 1.0)
308
+ frame = cv2.warpAffine(depth_resized, rotation_matrix, (width, height))
309
+
310
+ elif video_effect == "Rotate CCW":
311
  angle = progress * 360
312
  center = (width // 2, height // 2)
313
  rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
314
  frame = cv2.warpAffine(depth_resized, rotation_matrix, (width, height))
315
 
316
+ elif video_effect == "Orbit":
317
+ # Orbit: rotate + slight zoom
318
+ angle = progress * 360
319
+ scale = 1.0 + (np.sin(progress * np.pi) * 0.2)
320
+ center = (width // 2, height // 2)
321
+ rotation_matrix = cv2.getRotationMatrix2D(center, angle, scale)
322
+ frame = cv2.warpAffine(depth_resized, rotation_matrix, (width, height))
323
+
324
  else:
325
  frame = depth_resized.copy()
326
 
 
338
  st.download_button(
339
  label="πŸ“₯ Download Video",
340
  data=video_bytes,
341
+ file_name=f"depth_video_{video_effect.lower().replace(' ', '_').replace('(', '').replace(')', '')}.mp4",
342
  mime="video/mp4"
343
  )
344
 
 
346
  st.error(f"Error generating video: {str(e)}")
347
  import traceback
348
  traceback.print_exc()
349
+ else:
350
+ st.info("πŸ‘† Upload an image and generate depth map first to enable video export")
351
 
352
  # Info section
353
  st.markdown("---")
 
355
  ## πŸ’‘ About DimensioDepth
356
 
357
  ### Features:
358
+ - βœ… Real AI depth estimation with Depth-Anything V2 BASE model
 
359
  - βœ… Fast processing (~800ms on CPU, ~200ms on GPU)
360
  - βœ… SUPERB quality depth maps
361
+ - βœ… **Professional video export** with cinematic camera movements
362
+
363
+ ### Camera Effects:
364
+ - πŸ“Ή **Zoom In/Out** - Smooth zoom controls
365
+ - 🎬 **Pan** - Left, Right, Up, Down panning
366
+ - πŸŽ₯ **Dolly** - Professional dolly in/out shots
367
+ - 🎞️ **Tilt** - Up/Down tilt movements
368
+ - πŸ”„ **Rotate** - Clockwise/Counter-clockwise rotation
369
+ - ⭐ **Ken Burns** - Classic zoom + pan effect
370
+ - πŸŒ€ **Orbit** - Smooth orbital rotation
371
 
372
  ### Use Cases:
373
  - 🎨 **Creative & Artistic**: Depth-enhanced photos, 3D effects