XciD HF Staff commited on
Commit
faad11a
·
unverified ·
1 Parent(s): 0299827

fix: load models on CPU at startup for Zero GPU compatibility

Browse files
Files changed (1) hide show
  1. app.py +18 -49
app.py CHANGED
@@ -1,4 +1,3 @@
1
- import io
2
  import spaces
3
  import numpy as np
4
  import torch
@@ -11,58 +10,37 @@ from transformers import (
11
  AutoModelForDepthEstimation,
12
  )
13
 
14
- DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
15
- DTYPE = torch.float16 if DEVICE == "cuda" else torch.float32
16
-
17
  ADE20K_FLOOR_IDS = {3, 28} # 3=floor, 28=rug/carpet
18
 
19
- seg_processor = None
20
- seg_model = None
21
- depth_processor = None
22
- depth_model = None
23
-
24
-
25
- def load_models():
26
- global seg_processor, seg_model, depth_processor, depth_model
27
-
28
- if seg_model is None:
29
- seg_processor = OneFormerProcessor.from_pretrained(
30
- "shi-labs/oneformer_ade20k_swin_large"
31
- )
32
- seg_model = OneFormerForUniversalSegmentation.from_pretrained(
33
- "shi-labs/oneformer_ade20k_swin_large",
34
- torch_dtype=DTYPE,
35
- ).to(DEVICE)
36
 
37
- if depth_model is None:
38
- depth_processor = AutoImageProcessor.from_pretrained(
39
- "depth-anything/Depth-Anything-V2-Large-hf"
40
- )
41
- depth_model = AutoModelForDepthEstimation.from_pretrained(
42
- "depth-anything/Depth-Anything-V2-Large-hf",
43
- torch_dtype=DTYPE,
44
- ).to(DEVICE)
45
 
46
 
 
47
  @torch.inference_mode()
48
- def process_image(image: Image.Image):
49
- """Takes a room photo, returns floor mask + depth map as images."""
50
  if image is None:
51
  raise gr.Error("No image provided")
52
 
53
- load_models()
54
-
55
  orig_w, orig_h = image.size
56
  max_size = 1024
57
  scale = min(1.0, max_size / max(orig_w, orig_h))
58
  proc_w, proc_h = int(orig_w * scale), int(orig_h * scale)
59
  image_resized = image.resize((proc_w, proc_h), Image.LANCZOS)
60
 
61
- # --- Segmentation ---
62
- seg_inputs = seg_processor(
63
- images=image_resized, task_inputs=["semantic"], return_tensors="pt"
64
- )
65
- seg_inputs = {k: v.to(DEVICE, dtype=DTYPE) if v.dtype == torch.float32 else v.to(DEVICE) for k, v in seg_inputs.items()}
66
 
67
  seg_outputs = seg_model(**seg_inputs)
68
  seg_result = seg_processor.post_process_semantic_segmentation(
@@ -74,17 +52,15 @@ def process_image(image: Image.Image):
74
  for class_id in ADE20K_FLOOR_IDS:
75
  floor_mask[seg_map == class_id] = 255
76
 
77
- # Resize mask to original dimensions
78
  mask_img = Image.fromarray(floor_mask).resize((orig_w, orig_h), Image.NEAREST)
79
 
80
- # --- Depth estimation ---
81
  depth_inputs = depth_processor(images=image_resized, return_tensors="pt")
82
- depth_inputs = {k: v.to(DEVICE, dtype=DTYPE) if v.dtype == torch.float32 else v.to(DEVICE) for k, v in depth_inputs.items()}
83
 
84
  depth_outputs = depth_model(**depth_inputs)
85
  depth_map = depth_outputs.predicted_depth.squeeze().cpu().numpy()
86
 
87
- # Normalize to 0-255
88
  depth_min, depth_max = depth_map.min(), depth_map.max()
89
  if depth_max - depth_min > 0:
90
  depth_norm = ((depth_map - depth_min) / (depth_max - depth_min) * 255).astype(np.uint8)
@@ -96,15 +72,8 @@ def process_image(image: Image.Image):
96
  return mask_img, depth_img
97
 
98
 
99
- @spaces.GPU
100
- def predict(image):
101
- mask, depth = process_image(image)
102
- return mask, depth
103
-
104
-
105
  with gr.Blocks() as demo:
106
  gr.Markdown("# Tile Visualizer - Segmentation API")
107
- gr.Markdown("Upload a room photo to get floor mask + depth map.")
108
 
109
  with gr.Row():
110
  input_image = gr.Image(type="pil", label="Room photo")
 
 
1
  import spaces
2
  import numpy as np
3
  import torch
 
10
  AutoModelForDepthEstimation,
11
  )
12
 
 
 
 
13
  ADE20K_FLOOR_IDS = {3, 28} # 3=floor, 28=rug/carpet
14
 
15
+ # Load models on CPU at startup. @spaces.GPU moves them to CUDA automatically.
16
+ seg_processor = OneFormerProcessor.from_pretrained("shi-labs/oneformer_ade20k_swin_large")
17
+ seg_model = OneFormerForUniversalSegmentation.from_pretrained(
18
+ "shi-labs/oneformer_ade20k_swin_large", torch_dtype=torch.float16
19
+ )
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
+ depth_processor = AutoImageProcessor.from_pretrained("depth-anything/Depth-Anything-V2-Large-hf")
22
+ depth_model = AutoModelForDepthEstimation.from_pretrained(
23
+ "depth-anything/Depth-Anything-V2-Large-hf", torch_dtype=torch.float16
24
+ )
 
 
 
 
25
 
26
 
27
+ @spaces.GPU
28
  @torch.inference_mode()
29
+ def predict(image):
 
30
  if image is None:
31
  raise gr.Error("No image provided")
32
 
 
 
33
  orig_w, orig_h = image.size
34
  max_size = 1024
35
  scale = min(1.0, max_size / max(orig_w, orig_h))
36
  proc_w, proc_h = int(orig_w * scale), int(orig_h * scale)
37
  image_resized = image.resize((proc_w, proc_h), Image.LANCZOS)
38
 
39
+ device = seg_model.device
40
+
41
+ # Segmentation
42
+ seg_inputs = seg_processor(images=image_resized, task_inputs=["semantic"], return_tensors="pt")
43
+ seg_inputs = {k: v.to(device) for k, v in seg_inputs.items()}
44
 
45
  seg_outputs = seg_model(**seg_inputs)
46
  seg_result = seg_processor.post_process_semantic_segmentation(
 
52
  for class_id in ADE20K_FLOOR_IDS:
53
  floor_mask[seg_map == class_id] = 255
54
 
 
55
  mask_img = Image.fromarray(floor_mask).resize((orig_w, orig_h), Image.NEAREST)
56
 
57
+ # Depth estimation
58
  depth_inputs = depth_processor(images=image_resized, return_tensors="pt")
59
+ depth_inputs = {k: v.to(device) for k, v in depth_inputs.items()}
60
 
61
  depth_outputs = depth_model(**depth_inputs)
62
  depth_map = depth_outputs.predicted_depth.squeeze().cpu().numpy()
63
 
 
64
  depth_min, depth_max = depth_map.min(), depth_map.max()
65
  if depth_max - depth_min > 0:
66
  depth_norm = ((depth_map - depth_min) / (depth_max - depth_min) * 255).astype(np.uint8)
 
72
  return mask_img, depth_img
73
 
74
 
 
 
 
 
 
 
75
  with gr.Blocks() as demo:
76
  gr.Markdown("# Tile Visualizer - Segmentation API")
 
77
 
78
  with gr.Row():
79
  input_image = gr.Image(type="pil", label="Room photo")