ManvithGopu1394 commited on
Commit
ae96aca
·
1 Parent(s): 2c7b15b

add new function to support username and api key from DA

Browse files
Files changed (1) hide show
  1. functions/image3d/image3dRemoteDA.py +320 -0
functions/image3d/image3dRemoteDA.py ADDED
@@ -0,0 +1,320 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import time
4
+ import shutil
5
+ import tempfile
6
+ import subprocess
7
+ import base64
8
+ from pathlib import Path
9
+ from io import BytesIO
10
+ from PIL import Image
11
+
12
+ def run_kaggle_image_to_3d(image_input, output_dir: str, username: str, api_key: str):
13
+ """
14
+ Runs Apple's SHARP Image-to-3D Gaussian pipeline on Kaggle GPU
15
+
16
+ Args:
17
+ image_input: Can be either:
18
+ - str: Path to image file
19
+ - bytes: Image file bytes
20
+ - BytesIO: Image file-like object
21
+ output_dir: Output directory for the 3D model
22
+ username: Kaggle username
23
+ api_key: Kaggle API key
24
+ """
25
+ # ---------------------------------------------------------
26
+ # Handle different input types
27
+ # ---------------------------------------------------------
28
+ temp_image_path = None
29
+ try:
30
+ if isinstance(image_input, str):
31
+ # Already a path
32
+ image_path = Path(image_input).resolve()
33
+ elif isinstance(image_input, bytes):
34
+ # Save bytes to temp file
35
+ temp_image_path = Path(tempfile.mktemp(suffix='.jpg'))
36
+ temp_image_path.write_bytes(image_input)
37
+ image_path = temp_image_path
38
+ elif isinstance(image_input, BytesIO):
39
+ # Save BytesIO to temp file
40
+ temp_image_path = Path(tempfile.mktemp(suffix='.jpg'))
41
+ temp_image_path.write_bytes(image_input.read())
42
+ image_path = temp_image_path
43
+ image_input.seek(0) # Reset stream
44
+ else:
45
+ raise ValueError(f"Unsupported image_input type: {type(image_input)}")
46
+
47
+ # ---------------------------------------------------------
48
+ # Resolve and validate paths
49
+ # ---------------------------------------------------------
50
+ output_dir = Path(output_dir).resolve()
51
+
52
+ if not image_path.exists():
53
+ raise FileNotFoundError(f"Image not found: {image_path}")
54
+
55
+ print("Input image:", image_path)
56
+ print("Output directory:", output_dir)
57
+
58
+ # ---------------------------------------------------------
59
+ # Encode image as Base64
60
+ # ---------------------------------------------------------
61
+ print("Encoding image to Base64...")
62
+ image_b64 = base64.b64encode(image_path.read_bytes()).decode("utf-8")
63
+
64
+ # ---------------------------------------------------------
65
+ # Kaggle setup
66
+ # ---------------------------------------------------------
67
+ subprocess.run(["kaggle", "--version"], check=True)
68
+
69
+ print("Using the kaggle credentials sent from the Frontend App.... ")
70
+
71
+ # Set up Kaggle credentials
72
+ kaggle_dir = Path.home() / ".kaggle"
73
+ kaggle_dir.mkdir(parents=True, exist_ok=True)
74
+ kaggle_json = kaggle_dir / "kaggle.json"
75
+ kaggle_json.write_text(json.dumps({"username": username, "key": api_key}), encoding="utf-8")
76
+ os.chmod(kaggle_json, 0o600)
77
+
78
+ # ---------------------------------------------------------
79
+ # Create temporary kernel directory
80
+ # ---------------------------------------------------------
81
+ kernel_dir = Path(tempfile.mkdtemp(prefix="kaggle_3dgs_"))
82
+ kernel_slug = f"image-to-3d-{int(time.time())}"
83
+ # Generate unique output filename using kernel slug to ensure uniqueness
84
+ unique_output_name = f"output_{int(time.time() * 1000)}.ply"
85
+ print("Created temp kernel dir:", kernel_dir)
86
+ print("Kernel slug:", kernel_slug)
87
+ print("Unique output name:", unique_output_name)
88
+
89
+ try:
90
+ # -----------------------------------------------------
91
+ # Kaggle kernel script (FLOAT-SAFE & STABLE)
92
+ # -----------------------------------------------------
93
+ script_code = f"""
94
+ import base64
95
+ import subprocess
96
+ from pathlib import Path
97
+ import warnings
98
+ warnings.filterwarnings("ignore")
99
+
100
+ print("Installing SHARP...")
101
+ subprocess.run(
102
+ ["pip", "install", "--no-cache-dir", "git+https://github.com/apple/ml-sharp.git"],
103
+ check=True
104
+ )
105
+
106
+ import torch
107
+ import torch.nn.functional as F
108
+ from sharp.models import PredictorParams, create_predictor
109
+ from sharp.utils import io
110
+ from sharp.utils.gaussians import save_ply, unproject_gaussians
111
+
112
+ # -------------------------------------------------
113
+ # Device
114
+ # -------------------------------------------------
115
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
116
+ print("Using device:", device)
117
+
118
+ # -------------------------------------------------
119
+ # Restore image
120
+ # -------------------------------------------------
121
+ IMAGE_PATH = Path("/kaggle/working/input_image.jpg")
122
+ IMAGE_PATH.write_bytes(base64.b64decode(\"\"\"{image_b64}\"\"\"))
123
+
124
+ # -------------------------------------------------
125
+ # Output
126
+ # -------------------------------------------------
127
+ OUTPUT_DIR = Path("/kaggle/working/outputs")
128
+ OUTPUT_DIR.mkdir(exist_ok=True)
129
+ output_ply = OUTPUT_DIR / "{unique_output_name}"
130
+
131
+ # -------------------------------------------------
132
+ # Load image
133
+ # -------------------------------------------------
134
+ image, _, f_px = io.load_rgb(IMAGE_PATH)
135
+ h, w = image.shape[:2]
136
+
137
+ # -------------------------------------------------
138
+ # Load model
139
+ # -------------------------------------------------
140
+ state_dict = torch.hub.load_state_dict_from_url(
141
+ "https://ml-site.cdn-apple.com/models/sharp/sharp_2572gikvuh.pt",
142
+ progress=True
143
+ )
144
+ predictor = create_predictor(PredictorParams())
145
+ predictor.load_state_dict(state_dict)
146
+ predictor.eval().to(device)
147
+
148
+ # -------------------------------------------------
149
+ # Preprocess (STRICT float32)
150
+ # -------------------------------------------------
151
+ image_pt = (
152
+ torch.from_numpy(image)
153
+ .permute(2, 0, 1)
154
+ .contiguous()
155
+ .to(device)
156
+ .float()
157
+ / 255.0
158
+ )
159
+ image_resized = F.interpolate(
160
+ image_pt.unsqueeze(0),
161
+ size=(1536, 1536),
162
+ mode="bilinear",
163
+ align_corners=True
164
+ ).float()
165
+ disparity_factor = torch.tensor(
166
+ [float(f_px / w)],
167
+ device=device,
168
+ dtype=torch.float32
169
+ )
170
+
171
+ # -------------------------------------------------
172
+ # Predict
173
+ # -------------------------------------------------
174
+ print("Running SHARP inference...")
175
+ gaussians_ndc = predictor(image_resized, disparity_factor)
176
+
177
+ # -------------------------------------------------
178
+ # Unproject (STRICT float32)
179
+ # -------------------------------------------------
180
+ intrinsics = torch.tensor(
181
+ [[f_px, 0.0, w / 2.0, 0.0],
182
+ [0.0, f_px, h / 2.0, 0.0],
183
+ [0.0, 0.0, 1.0, 0.0],
184
+ [0.0, 0.0, 0.0, 1.0]],
185
+ device=device,
186
+ dtype=torch.float32
187
+ )
188
+ intrinsics_resized = intrinsics.clone()
189
+ intrinsics_resized[0] *= float(1536 / w)
190
+ intrinsics_resized[1] *= float(1536 / h)
191
+
192
+ gaussians = unproject_gaussians(
193
+ gaussians_ndc,
194
+ torch.eye(4, device=device, dtype=torch.float32),
195
+ intrinsics_resized,
196
+ (1536, 1536)
197
+ )
198
+
199
+ # -------------------------------------------------
200
+ # Save
201
+ # -------------------------------------------------
202
+ save_ply(gaussians, f_px, (h, w), output_ply)
203
+ print("Saved PLY:", output_ply)
204
+ """
205
+ # Write kernel files
206
+ (kernel_dir / "kernel.py").write_text(script_code, encoding="utf-8")
207
+ metadata = {
208
+ "id": f"{username}/{kernel_slug}",
209
+ "title": kernel_slug,
210
+ "code_file": "kernel.py",
211
+ "language": "python",
212
+ "kernel_type": "script",
213
+ "is_private": "true",
214
+ "enable_gpu": "true",
215
+ "enable_internet": "true",
216
+ }
217
+ with open(kernel_dir / "kernel-metadata.json", "w", encoding="utf-8") as f:
218
+ json.dump(metadata, f, indent=2)
219
+
220
+ # -----------------------------------------------------
221
+ # Check if a kernel with the same slug is already running
222
+ # -----------------------------------------------------
223
+ try:
224
+ print("Checking if a kernel with the same slug is already running...")
225
+ status = subprocess.run(
226
+ ["kaggle", "kernels", "status", f"{username}/{kernel_slug}"],
227
+ capture_output=True,
228
+ text=True
229
+ ).stdout.lower()
230
+
231
+ if "running" in status or "queued" in status:
232
+ print("Kernel is already running. Deleting the existing kernel...")
233
+ subprocess.run(
234
+ ["kaggle", "kernels", "delete", f"{username}/{kernel_slug}"],
235
+ check=True
236
+ )
237
+ print("Existing kernel deleted successfully.")
238
+ except subprocess.CalledProcessError as e:
239
+ print("No existing kernel found or unable to check status. Proceeding with new kernel.")
240
+
241
+ # -----------------------------------------------------
242
+ # Push kernel
243
+ # -----------------------------------------------------
244
+ print("Pushing Kaggle kernel...")
245
+ subprocess.run(
246
+ ["kaggle", "kernels", "push", "-p", str(kernel_dir)],
247
+ check=True
248
+ )
249
+ kernel_ref = f"{username}/{kernel_slug}"
250
+ print("Kernel ref:", kernel_ref)
251
+
252
+ # -----------------------------------------------------
253
+ # Wait for completion
254
+ # -----------------------------------------------------
255
+ print("Waiting for kernel to finish...")
256
+ while True:
257
+ time.sleep(6)
258
+ status = subprocess.run(
259
+ ["kaggle", "kernels", "status", kernel_ref],
260
+ capture_output=True,
261
+ text=True
262
+ ).stdout.lower()
263
+ if "complete" in status:
264
+ break
265
+ if "failed" in status or "error" in status:
266
+ raise RuntimeError(
267
+ f"Kaggle kernel failed: https://www.kaggle.com/code/{kernel_ref}"
268
+ )
269
+
270
+ # -----------------------------------------------------
271
+ # Download output (SAFE MODE)
272
+ # -----------------------------------------------------
273
+ print("Downloading kernel outputs...")
274
+ output_dir.mkdir(parents=True, exist_ok=True)
275
+ result = subprocess.run(
276
+ ["kaggle", "kernels", "output", kernel_ref, "-p", str(output_dir)],
277
+ capture_output=True,
278
+ text=True
279
+ )
280
+ if result.returncode != 0:
281
+ print("⚠ Kaggle CLI returned non-zero exit (harmless)")
282
+ print(result.stderr.strip())
283
+
284
+ # Find the PLY file - check both the unique name and any outputs subdirectory
285
+ expected_ply_file = None
286
+
287
+ # First, try to find the file with the unique name we specified
288
+ potential_paths = [
289
+ output_dir / "outputs" / unique_output_name, # Kaggle might create outputs/ subdirectory
290
+ output_dir / unique_output_name, # Or directly in output_dir
291
+ ]
292
+
293
+ for path in potential_paths:
294
+ if path.exists():
295
+ expected_ply_file = path
296
+ break
297
+
298
+ # If not found by unique name, find the most recently created PLY file
299
+ if expected_ply_file is None:
300
+ ply_files = list(output_dir.rglob("*.ply"))
301
+ if not ply_files:
302
+ raise FileNotFoundError("PLY output not found in downloaded files")
303
+
304
+ # Get the most recently modified file (should be our new output)
305
+ expected_ply_file = max(ply_files, key=lambda p: p.stat().st_mtime)
306
+ print(f"⚠ Could not find file by unique name, using most recent PLY: {expected_ply_file}")
307
+
308
+ if not expected_ply_file.exists():
309
+ raise FileNotFoundError(f"Expected PLY file not found: {expected_ply_file}")
310
+
311
+ print("✅ Final 3D model:", expected_ply_file)
312
+ return str(expected_ply_file)
313
+
314
+ finally:
315
+ shutil.rmtree(kernel_dir, ignore_errors=True)
316
+
317
+ finally:
318
+ # Clean up temporary image file if we created one
319
+ if temp_image_path and temp_image_path.exists():
320
+ temp_image_path.unlink()