JohnnyTheFox commited on
Commit
d749752
·
1 Parent(s): 01d4b32

Fix dependency versions and improve error handling for HuggingFace compatibility

Browse files
Files changed (4) hide show
  1. backup/app.py.improved +388 -0
  2. backup/requirements.txt.improved +27 -0
  3. deploy.py +157 -0
  4. test_space.py +167 -0
backup/app.py.improved ADDED
@@ -0,0 +1,388 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ ColorCraft SDXL + CPDS Line Art Generator
4
+ HuggingFace Space deployment of your working SDXL pipeline
5
+ """
6
+
7
+ import os
8
+ import cv2
9
+ import torch
10
+ import numpy as np
11
+ from PIL import Image
12
+ import logging
13
+ import gradio as gr
14
+ from typing import Optional
15
+ import gc
16
+ import time
17
+
18
+ # Configure logging
19
+ logging.basicConfig(level=logging.INFO)
20
+ logger = logging.getLogger(__name__)
21
+
22
+ # ControlNet imports with better error handling
23
+ try:
24
+ from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
25
+ # Handle different controlnet-aux versions
26
+ try:
27
+ from controlnet_aux import CannyDetector
28
+ CONTROLNET_AVAILABLE = True
29
+ logger.info("✅ ControlNet imports successful (newer version)")
30
+ except ImportError:
31
+ # Fallback for older versions
32
+ try:
33
+ from controlnet_aux.canny import CannyDetector
34
+ CONTROLNET_AVAILABLE = True
35
+ logger.info("✅ ControlNet imports successful (older version)")
36
+ except ImportError:
37
+ CONTROLNET_AVAILABLE = False
38
+ logger.warning("⚠️ ControlNet not available, using OpenCV fallback")
39
+ except ImportError as e:
40
+ CONTROLNET_AVAILABLE = False
41
+ logger.warning(f"⚠️ ControlNet not available: {e}")
42
+
43
+ class ColorCraftSDXLProcessor:
44
+ """Your exact SDXL + CPDS pipeline deployed to HuggingFace"""
45
+
46
+ def __init__(self):
47
+ self.pipeline = None
48
+ self.canny_detector = None
49
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
50
+ logger.info(f"🔧 Initializing ColorCraft SDXL Processor on {self.device}")
51
+
52
+ # Initialize detectors
53
+ if CONTROLNET_AVAILABLE:
54
+ try:
55
+ self.canny_detector = CannyDetector()
56
+ logger.info("✅ Canny detector initialized")
57
+ except Exception as e:
58
+ logger.warning(f"⚠️ Canny detector failed to load: {e}")
59
+ self.canny_detector = None
60
+
61
+ # Load models on initialization
62
+ self._load_models()
63
+
64
+ def _load_models(self):
65
+ """Load your exact SDXL + CPDS setup"""
66
+ logger.info("🔧 Loading SDXL with CPDS ControlNet...")
67
+
68
+ # Clear GPU memory
69
+ if torch.cuda.is_available():
70
+ torch.cuda.empty_cache()
71
+ torch.cuda.synchronize()
72
+ gc.collect()
73
+
74
+ try:
75
+ # Load SDXL-compatible ControlNet (same as your local setup)
76
+ controlnet_model = "diffusers/controlnet-canny-sdxl-1.0"
77
+
78
+ logger.info(f"🔄 Loading ControlNet: {controlnet_model}")
79
+ self.controlnet = ControlNetModel.from_pretrained(
80
+ controlnet_model,
81
+ torch_dtype=torch.float16,
82
+ use_safetensors=True
83
+ )
84
+ logger.info("✅ ControlNet loaded successfully")
85
+
86
+ # Load SDXL base model (same as your local setup)
87
+ sdxl_model = "stabilityai/stable-diffusion-xl-base-1.0"
88
+
89
+ logger.info(f"🔄 Loading SDXL: {sdxl_model}")
90
+ self.pipeline = StableDiffusionControlNetPipeline.from_pretrained(
91
+ sdxl_model,
92
+ controlnet=self.controlnet,
93
+ torch_dtype=torch.float16,
94
+ use_safetensors=True,
95
+ safety_checker=None,
96
+ requires_safety_checker=False,
97
+ variant="fp16"
98
+ )
99
+
100
+ # Move to GPU
101
+ if torch.cuda.is_available():
102
+ self.pipeline = self.pipeline.to("cuda")
103
+ logger.info("✅ Pipeline moved to GPU")
104
+
105
+ # Enable memory optimizations (same as your local setup)
106
+ logger.info("🔧 Applying memory optimizations...")
107
+
108
+ try:
109
+ self.pipeline.enable_attention_slicing("max")
110
+ logger.info("✅ Enabled attention slicing")
111
+ except:
112
+ pass
113
+
114
+ try:
115
+ # Try xformers first, fallback to CPU offload
116
+ self.pipeline.enable_xformers_memory_efficient_attention()
117
+ logger.info("✅ Enabled xformers")
118
+ except:
119
+ try:
120
+ self.pipeline.enable_model_cpu_offload()
121
+ logger.info("✅ Enabled CPU offload")
122
+ except:
123
+ logger.info("⚠️ Memory optimizations not available")
124
+
125
+ logger.info("🎉 SDXL pipeline ready!")
126
+
127
+ except Exception as e:
128
+ logger.error(f"❌ Failed to load models: {e}")
129
+ # Don't raise here, let the fallback handle it
130
+ self.pipeline = None
131
+
132
+ def process_image_to_lineart(
133
+ self,
134
+ image: Image.Image,
135
+ style: str = "clean",
136
+ detail_level: float = 0.7,
137
+ noise_reduction: bool = True
138
+ ) -> Image.Image:
139
+ """Convert image to line art using your SDXL + CPDS pipeline"""
140
+
141
+ start_time = time.time()
142
+ logger.info(f"🎨 Processing image: {image.size}")
143
+
144
+ # Check if SDXL pipeline is available
145
+ if self.pipeline is None:
146
+ logger.warning("⚠️ SDXL pipeline not available, using fallback")
147
+ return self._fallback_lineart(image, style, detail_level, noise_reduction)
148
+
149
+ try:
150
+ # Preprocess image (same as your local pipeline)
151
+ if image.mode != 'RGB':
152
+ image = image.convert('RGB')
153
+
154
+ # Resize to optimal size for SDXL (1024x1024 or maintain aspect ratio)
155
+ max_size = 1024
156
+ if max(image.size) > max_size:
157
+ ratio = max_size / max(image.size)
158
+ new_size = (int(image.width * ratio), int(image.height * ratio))
159
+ image = image.resize(new_size, Image.Resampling.LANCZOS)
160
+
161
+ # Generate Canny edges as control
162
+ if self.canny_detector:
163
+ logger.info("🔍 Generating Canny edges")
164
+ canny_image = self.canny_detector(image)
165
+ else:
166
+ # Fallback Canny detection
167
+ img_array = np.array(image)
168
+ gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
169
+ edges = cv2.Canny(gray, 50, 150)
170
+ canny_image = Image.fromarray(edges).convert('RGB')
171
+
172
+ # Your exact prompt engineering for line art
173
+ positive_prompt = f"""
174
+ black and white line art, coloring book style, clean outlines,
175
+ simple lines, minimal shading, high contrast, white background,
176
+ detailed line drawing, professional illustration, {style} style,
177
+ monochrome, pen and ink drawing, clear boundaries
178
+ """
179
+
180
+ negative_prompt = """
181
+ color, colored, painting, photorealistic, photograph, realistic,
182
+ shading, gradients, complex details, noise, blurry, low quality,
183
+ watermark, signature, text, cropped, worst quality, low quality,
184
+ normal quality, jpeg artifacts, signature, watermark, username,
185
+ blurry, artist name, trademark, title, multiple views, Reference sheet
186
+ """
187
+
188
+ # Generate with your exact parameters
189
+ logger.info("🚀 Generating line art with SDXL...")
190
+
191
+ result = self.pipeline(
192
+ prompt=positive_prompt.strip(),
193
+ negative_prompt=negative_prompt.strip(),
194
+ image=canny_image,
195
+ num_inference_steps=20, # Same as your local setup
196
+ guidance_scale=7.5, # Same as your local setup
197
+ controlnet_conditioning_scale=0.7, # Your detail_level
198
+ width=image.width,
199
+ height=image.height,
200
+ generator=torch.Generator().manual_seed(42) # Reproducible results
201
+ ).images[0]
202
+
203
+ # Post-process for optimal coloring book quality (same as your pipeline)
204
+ result_array = np.array(result)
205
+ if len(result_array.shape) == 3 and result_array.shape[2] == 3:
206
+ # Convert to grayscale
207
+ gray = cv2.cvtColor(result_array, cv2.COLOR_RGB2GRAY)
208
+
209
+ # Enhance contrast and clean up
210
+ enhanced = cv2.convertScaleAbs(gray, alpha=1.3, beta=10)
211
+
212
+ # Apply bilateral filter to reduce noise while preserving edges
213
+ denoised = cv2.bilateralFilter(enhanced, 9, 75, 75)
214
+
215
+ # Convert back to PIL
216
+ final_result = Image.fromarray(denoised).convert('RGB')
217
+ else:
218
+ final_result = result
219
+
220
+ processing_time = time.time() - start_time
221
+ logger.info(f"✅ Processing completed in {processing_time:.2f}s")
222
+
223
+ return final_result
224
+
225
+ except Exception as e:
226
+ logger.error(f"❌ SDXL processing failed: {e}")
227
+ # Return enhanced edge detection as fallback
228
+ return self._fallback_lineart(image, style, detail_level, noise_reduction)
229
+
230
+ def _fallback_lineart(self, image: Image.Image, style: str, detail_level: float, noise_reduction: bool) -> Image.Image:
231
+ """High-quality fallback using OpenCV when SDXL fails"""
232
+ logger.info("🔄 Using high-quality fallback processing")
233
+
234
+ try:
235
+ # Convert to numpy array
236
+ img_array = np.array(image.convert('RGB'))
237
+
238
+ # Convert to grayscale
239
+ gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
240
+
241
+ # Apply bilateral filter for noise reduction
242
+ if noise_reduction:
243
+ gray = cv2.bilateralFilter(gray, 9, 75, 75)
244
+
245
+ # Enhanced edge detection with multiple methods
246
+ # Canny edges
247
+ canny_edges = cv2.Canny(gray, 50, 150)
248
+
249
+ # Laplacian edges
250
+ laplacian = cv2.Laplacian(gray, cv2.CV_64F)
251
+ laplacian_edges = np.uint8(np.absolute(laplacian))
252
+
253
+ # Combine edges
254
+ combined_edges = cv2.bitwise_or(canny_edges, laplacian_edges)
255
+
256
+ # Apply morphological operations to clean up
257
+ kernel = np.ones((2, 2), np.uint8)
258
+ cleaned_edges = cv2.morphologyEx(combined_edges, cv2.MORPH_CLOSE, kernel)
259
+
260
+ # Invert for coloring book style (white background, black lines)
261
+ final_edges = cv2.bitwise_not(cleaned_edges)
262
+
263
+ # Convert back to PIL
264
+ result = Image.fromarray(final_edges).convert('RGB')
265
+
266
+ logger.info("✅ Fallback processing completed successfully")
267
+ return result
268
+
269
+ except Exception as e:
270
+ logger.error(f"❌ Fallback processing failed: {e}")
271
+ # Ultimate fallback - simple edge detection
272
+ img_array = np.array(image.convert('RGB'))
273
+ gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
274
+ edges = cv2.Canny(gray, 50, 150)
275
+ edges = cv2.bitwise_not(edges)
276
+ return Image.fromarray(edges).convert('RGB')
277
+
278
+ # Initialize processor
279
+ try:
280
+ processor = ColorCraftSDXLProcessor()
281
+ logger.info("🎉 ColorCraft processor initialized successfully!")
282
+ except Exception as e:
283
+ logger.error(f"❌ Failed to initialize processor: {e}")
284
+ processor = None
285
+
286
+ def process_image_interface(image, style, detail_level, noise_reduction):
287
+ """Gradio interface function"""
288
+ if processor is None:
289
+ return None, "Error: Processor not available"
290
+
291
+ if image is None:
292
+ return None, "Please upload an image"
293
+
294
+ try:
295
+ result = processor.process_image_to_lineart(
296
+ image=image,
297
+ style=style,
298
+ detail_level=detail_level,
299
+ noise_reduction=noise_reduction
300
+ )
301
+ return result, "✅ Processing completed successfully!"
302
+ except Exception as e:
303
+ return None, f"❌ Error: {str(e)}"
304
+
305
+ # Create Gradio interface
306
+ def create_interface():
307
+ with gr.Blocks(title="ColorCraft SDXL Line Art Generator") as demo:
308
+ gr.Markdown("""
309
+ # 🎨 ColorCraft SDXL Line Art Generator
310
+
311
+ **High-quality line art generation using SDXL + ControlNet**
312
+
313
+ Upload an image and get professional coloring book line art in seconds!
314
+ """)
315
+
316
+ with gr.Row():
317
+ with gr.Column():
318
+ image_input = gr.Image(
319
+ label="Upload Image",
320
+ type="pil",
321
+ height=400
322
+ )
323
+
324
+ style_input = gr.Dropdown(
325
+ choices=["clean", "detailed", "artistic", "simple"],
326
+ value="clean",
327
+ label="Style"
328
+ )
329
+
330
+ detail_input = gr.Slider(
331
+ minimum=0.1,
332
+ maximum=1.0,
333
+ value=0.7,
334
+ step=0.1,
335
+ label="Detail Level"
336
+ )
337
+
338
+ noise_input = gr.Checkbox(
339
+ value=True,
340
+ label="Noise Reduction"
341
+ )
342
+
343
+ process_btn = gr.Button("🎨 Generate Line Art", variant="primary")
344
+
345
+ with gr.Column():
346
+ result_image = gr.Image(
347
+ label="Line Art Result",
348
+ type="pil",
349
+ height=400
350
+ )
351
+
352
+ status_text = gr.Textbox(
353
+ label="Status",
354
+ value="Ready to process images",
355
+ interactive=False
356
+ )
357
+
358
+ # Connect the interface
359
+ process_btn.click(
360
+ fn=process_image_interface,
361
+ inputs=[image_input, style_input, detail_input, noise_input],
362
+ outputs=[result_image, status_text]
363
+ )
364
+
365
+ gr.Markdown("""
366
+ ### 🚀 API Usage
367
+
368
+ You can also call this Space as an API:
369
+
370
+ ```python
371
+ import requests
372
+
373
+ response = requests.post(
374
+ "https://YOUR_USERNAME-colorcraft-sdxl.hf.space/api/predict",
375
+ json={"data": [image_base64, "clean", 0.7, True]}
376
+ )
377
+ ```
378
+ """)
379
+
380
+ return demo
381
+
382
+ if __name__ == "__main__":
383
+ demo = create_interface()
384
+ demo.launch(
385
+ server_name="0.0.0.0",
386
+ server_port=7860,
387
+ share=True
388
+ )
backup/requirements.txt.improved ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ColorCraft SDXL + CPDS Dependencies
2
+ # Core ML libraries - use compatible versions
3
+ torch>=2.0.0
4
+ torchvision
5
+ torchaudio
6
+ transformers>=4.25.0
7
+ diffusers>=0.21.0
8
+ accelerate>=0.20.0
9
+
10
+ # ControlNet and preprocessing - use available versions
11
+ controlnet-aux>=0.0.10
12
+ opencv-python>=4.8.0
13
+
14
+ # Image processing
15
+ Pillow>=9.5.0
16
+ numpy>=1.24.0
17
+
18
+ # HuggingFace libraries
19
+ huggingface-hub>=0.16.0
20
+ safetensors>=0.3.0
21
+
22
+ # Gradio for interface
23
+ gradio>=3.35.0
24
+
25
+ # Utils
26
+ requests>=2.28.0
27
+ typing-extensions>=4.5.0
deploy.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ ColorCraft SDXL Space Deployment Script
4
+ Helps deploy your SDXL pipeline to HuggingFace Spaces
5
+ """
6
+
7
+ import os
8
+ import subprocess
9
+ import sys
10
+ from pathlib import Path
11
+
12
+ def check_dependencies():
13
+ """Check if required tools are installed"""
14
+ try:
15
+ import huggingface_hub
16
+ print("✅ huggingface_hub is installed")
17
+ except ImportError:
18
+ print("❌ Installing huggingface_hub...")
19
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "huggingface_hub"])
20
+
21
+ try:
22
+ subprocess.run(["git", "--version"], capture_output=True, check=True)
23
+ print("✅ Git is available")
24
+ except (subprocess.CalledProcessError, FileNotFoundError):
25
+ print("❌ Git is required. Please install Git first.")
26
+ sys.exit(1)
27
+
28
+ def setup_space(username, space_name, hf_token):
29
+ """Create and setup HuggingFace Space"""
30
+ from huggingface_hub import HfApi, create_repo
31
+
32
+ # Initialize HF API
33
+ api = HfApi(token=hf_token)
34
+
35
+ # Create repository
36
+ repo_id = f"{username}/{space_name}"
37
+
38
+ try:
39
+ print(f"🚀 Creating HuggingFace Space: {repo_id}")
40
+ create_repo(
41
+ repo_id=repo_id,
42
+ token=hf_token,
43
+ repo_type="space",
44
+ space_sdk="gradio",
45
+ space_hardware="t4-medium", # GPU for SDXL
46
+ private=False
47
+ )
48
+ print(f"✅ Space created successfully!")
49
+ except Exception as e:
50
+ if "already exists" in str(e):
51
+ print(f"ℹ️ Space {repo_id} already exists, updating...")
52
+ else:
53
+ print(f"❌ Error creating space: {e}")
54
+ sys.exit(1)
55
+
56
+ return repo_id
57
+
58
+ def deploy_files(repo_id, hf_token):
59
+ """Deploy files to HuggingFace Space"""
60
+ from huggingface_hub import HfApi
61
+
62
+ api = HfApi(token=hf_token)
63
+ space_dir = Path(__file__).parent
64
+
65
+ files_to_upload = [
66
+ ("app.py", "app.py"),
67
+ ("requirements.txt", "requirements.txt"),
68
+ ("README.md", "README.md")
69
+ ]
70
+
71
+ print("📁 Uploading files to Space...")
72
+
73
+ for local_file, remote_file in files_to_upload:
74
+ local_path = space_dir / local_file
75
+ if local_path.exists():
76
+ print(f" 📤 Uploading {local_file}...")
77
+ api.upload_file(
78
+ path_or_fileobj=str(local_path),
79
+ path_in_repo=remote_file,
80
+ repo_id=repo_id,
81
+ repo_type="space",
82
+ token=hf_token
83
+ )
84
+ else:
85
+ print(f" ⚠️ File not found: {local_file}")
86
+
87
+ print("✅ All files uploaded successfully!")
88
+
89
+ def update_backend_config(repo_id):
90
+ """Update backend to use the deployed Space"""
91
+ backend_dir = Path(__file__).parent.parent
92
+ hf_client_path = backend_dir / "app" / "huggingface_client.py"
93
+
94
+ if hf_client_path.exists():
95
+ print("🔧 Updating backend configuration...")
96
+
97
+ # Read current file
98
+ with open(hf_client_path, 'r', encoding='utf-8') as f:
99
+ content = f.read()
100
+
101
+ # Replace placeholder URL with actual Space URL
102
+ space_url = f"https://{repo_id.replace('/', '-')}.hf.space/api/predict"
103
+ content = content.replace(
104
+ "https://YOUR_USERNAME-colorcraft-sdxl.hf.space/api/predict",
105
+ space_url
106
+ )
107
+
108
+ # Write back
109
+ with open(hf_client_path, 'w', encoding='utf-8') as f:
110
+ f.write(content)
111
+
112
+ print(f"✅ Backend updated to use: {space_url}")
113
+ else:
114
+ print("⚠️ Backend HuggingFace client not found")
115
+
116
+ def main():
117
+ print("🎨 ColorCraft SDXL Space Deployment")
118
+ print("=" * 50)
119
+
120
+ # Check dependencies
121
+ check_dependencies()
122
+
123
+ # Get user input
124
+ print("\n📝 Configuration:")
125
+ username = input("Enter your HuggingFace username: ").strip()
126
+ space_name = input("Enter Space name (e.g., 'colorcraft-sdxl'): ").strip()
127
+ hf_token = input("Enter your HuggingFace token: ").strip()
128
+
129
+ if not all([username, space_name, hf_token]):
130
+ print("❌ All fields are required!")
131
+ sys.exit(1)
132
+
133
+ # Setup and deploy
134
+ try:
135
+ repo_id = setup_space(username, space_name, hf_token)
136
+ deploy_files(repo_id, hf_token)
137
+ update_backend_config(repo_id)
138
+
139
+ space_url = f"https://{repo_id.replace('/', '-')}.hf.space"
140
+
141
+ print("\n🎉 Deployment Complete!")
142
+ print("=" * 50)
143
+ print(f"🌐 Space URL: {space_url}")
144
+ print(f"📊 Space Dashboard: https://huggingface.co/spaces/{repo_id}")
145
+ print("\n📋 Next Steps:")
146
+ print("1. Visit your Space URL to verify it's working")
147
+ print("2. Wait for the Space to build (5-10 minutes)")
148
+ print("3. Test image processing through the interface")
149
+ print("4. Your backend will now use SDXL quality!")
150
+ print("\n🚀 Your ColorCraft app now has cloud SDXL processing!")
151
+
152
+ except Exception as e:
153
+ print(f"❌ Deployment failed: {e}")
154
+ sys.exit(1)
155
+
156
+ if __name__ == "__main__":
157
+ main()
test_space.py ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Test script for ColorCraft SDXL Space
4
+ Verifies that your deployed Space is working correctly
5
+ """
6
+
7
+ import requests
8
+ import base64
9
+ import json
10
+ from PIL import Image
11
+ import io
12
+ import time
13
+
14
+ def create_test_image():
15
+ """Create a simple test image"""
16
+ from PIL import Image, ImageDraw
17
+
18
+ # Create a simple test image with shapes
19
+ img = Image.new('RGB', (512, 512), 'white')
20
+ draw = ImageDraw.Draw(img)
21
+
22
+ # Draw some shapes for testing
23
+ draw.rectangle([100, 100, 400, 400], outline='black', width=3)
24
+ draw.ellipse([150, 150, 350, 350], outline='blue', width=2)
25
+ draw.line([200, 200, 300, 300], fill='red', width=4)
26
+
27
+ return img
28
+
29
+ def test_space_api(space_url, test_image_path=None):
30
+ """Test the deployed ColorCraft SDXL Space"""
31
+
32
+ print(f"🧪 Testing ColorCraft SDXL Space: {space_url}")
33
+
34
+ # Use provided image or create test image
35
+ if test_image_path:
36
+ try:
37
+ image = Image.open(test_image_path)
38
+ print(f"📸 Using test image: {test_image_path}")
39
+ except Exception as e:
40
+ print(f"❌ Could not load image {test_image_path}: {e}")
41
+ return False
42
+ else:
43
+ image = create_test_image()
44
+ print("📸 Using generated test image")
45
+
46
+ # Convert image to base64
47
+ img_buffer = io.BytesIO()
48
+ image.save(img_buffer, format='PNG')
49
+ img_buffer.seek(0)
50
+ image_b64 = base64.b64encode(img_buffer.getvalue()).decode()
51
+
52
+ # Prepare API request
53
+ payload = {
54
+ "data": [
55
+ f"data:image/png;base64,{image_b64}", # image
56
+ "clean", # style
57
+ 0.7, # detail_level
58
+ True # noise_reduction
59
+ ]
60
+ }
61
+
62
+ try:
63
+ print("🚀 Sending request to Space...")
64
+ start_time = time.time()
65
+
66
+ response = requests.post(
67
+ f"{space_url}/api/predict",
68
+ json=payload,
69
+ timeout=120, # 2 minute timeout for SDXL
70
+ headers={"Content-Type": "application/json"}
71
+ )
72
+
73
+ processing_time = time.time() - start_time
74
+ print(f"⏱️ Request completed in {processing_time:.2f} seconds")
75
+
76
+ if response.status_code == 200:
77
+ result = response.json()
78
+ print("✅ Space responded successfully!")
79
+
80
+ # Check if we got a valid result
81
+ if "data" in result and len(result["data"]) >= 2:
82
+ result_image_data = result["data"][0]
83
+ status_message = result["data"][1]
84
+
85
+ print(f"📋 Status: {status_message}")
86
+
87
+ if result_image_data:
88
+ # Try to decode and save result
89
+ try:
90
+ if isinstance(result_image_data, str):
91
+ if result_image_data.startswith('data:image'):
92
+ image_data = result_image_data.split(',')[1]
93
+ else:
94
+ image_data = result_image_data
95
+
96
+ # Decode and save
97
+ image_bytes = base64.b64decode(image_data)
98
+ result_image = Image.open(io.BytesIO(image_bytes))
99
+
100
+ # Save result
101
+ output_path = "test_result.png"
102
+ result_image.save(output_path)
103
+ print(f"🎨 Result saved as: {output_path}")
104
+ print(f"📏 Result size: {result_image.size}")
105
+
106
+ return True
107
+
108
+ elif isinstance(result_image_data, dict) and "url" in result_image_data:
109
+ print(f"🔗 Result URL: {result_image_data['url']}")
110
+ return True
111
+
112
+ except Exception as e:
113
+ print(f"⚠️ Could not process result image: {e}")
114
+ print(f"Raw result type: {type(result_image_data)}")
115
+ return False
116
+ else:
117
+ print("⚠️ No image data in response")
118
+ return False
119
+ else:
120
+ print("⚠️ Unexpected response format")
121
+ print(f"Response: {json.dumps(result, indent=2)}")
122
+ return False
123
+
124
+ else:
125
+ print(f"❌ Space returned status {response.status_code}")
126
+ print(f"Response: {response.text}")
127
+ return False
128
+
129
+ except requests.exceptions.Timeout:
130
+ print("⏰ Request timed out - Space might be loading or overloaded")
131
+ return False
132
+ except Exception as e:
133
+ print(f"❌ Error testing Space: {e}")
134
+ return False
135
+
136
+ def main():
137
+ print("🧪 ColorCraft SDXL Space Tester")
138
+ print("=" * 40)
139
+
140
+ # Get Space URL
141
+ space_url = input("Enter your Space URL (e.g., https://username-colorcraft-sdxl.hf.space): ").strip()
142
+
143
+ if not space_url:
144
+ print("❌ Space URL is required!")
145
+ return
146
+
147
+ # Remove trailing slash
148
+ space_url = space_url.rstrip('/')
149
+
150
+ # Optional test image
151
+ test_image = input("Enter path to test image (or press Enter for generated test): ").strip()
152
+ test_image = test_image if test_image else None
153
+
154
+ # Run test
155
+ success = test_space_api(space_url, test_image)
156
+
157
+ if success:
158
+ print("\n🎉 Space Test PASSED!")
159
+ print("✅ Your SDXL Space is working correctly")
160
+ print("🚀 Ready for production use!")
161
+ else:
162
+ print("\n❌ Space Test FAILED!")
163
+ print("🔧 Check your Space logs and configuration")
164
+ print("💡 Try waiting a few minutes if the Space is still building")
165
+
166
+ if __name__ == "__main__":
167
+ main()