henaiv2 / image.py
joashsam's picture
Upload image.py
f3c8958 verified
Raw
History Blame Contribute Delete
21.3 kB
"""
image.py - Free AI Image Generation Module
NO API KEYS REQUIRED - All methods are 100% free!
"""
import os
import io
import base64
import requests
from pathlib import Path
from typing import Optional, Dict, Any, List
from datetime import datetime
import json
# Check availability flags
LOCAL_SD_AVAILABLE = False
REPLICATE_AVAILABLE = False
# Try to import local generation libraries
try:
from diffusers import (
StableDiffusionPipeline,
StableDiffusionXLPipeline,
FluxPipeline,
)
import torch
LOCAL_SD_AVAILABLE = True
print("✅ Local Stable Diffusion available (no API keys needed)")
except ImportError:
print("⚠️ Local generation not available. Install with: pip install diffusers transformers torch accelerate")
try:
import replicate
REPLICATE_AVAILABLE = True
print("✅ Replicate available (free credits, no credit card required)")
except ImportError:
REPLICATE_AVAILABLE = False
print("⚠️ Replicate not available (optional)")
# Global instances for lazy loading
_sd_pipe = None
_sdxl_pipe = None
_flux_pipe = None
class FreeImageGenerator:
"""
100% Free AI Image Generator
NO API KEYS REQUIRED - All methods work without any keys!
"""
def __init__(self, output_dir: str = "generated_images", user_id: str = None):
"""
Initialize the free image generator.
NO API KEYS NEEDED!
Args:
output_dir: Directory to save generated images
user_id: User ID for isolated storage (for multi-user support)
"""
self.user_id = user_id
# Use user-specific directory if user_id is provided
if user_id:
# User-specific images under /data/users/{user_id}/generated_images
base_dir = Path('/data/users') / str(user_id) / 'generated_images'
self.output_dir = base_dir
else:
# Fallback to provided output_dir for backward compatibility
self.output_dir = Path(output_dir)
self.output_dir.mkdir(parents=True, exist_ok=True)
# Free models (no API keys needed)
self.free_models = {
'sd1.5': 'runwayml/stable-diffusion-v1-5',
'sd2.1': 'stabilityai/stable-diffusion-2-1',
'sdxl': 'stabilityai/stable-diffusion-xl-base-1.0',
'sdxl-turbo': 'stabilityai/sdxl-turbo',
'playground': 'playgroundai/playground-v2.5-1024px-aesthetic',
'kandinsky': 'kandinsky-community/kandinsky-2-2-decoder',
'pixart': 'PixArt-alpha/PixArt-XL-2-1024-MS',
'flux-schnell': 'black-forest-labs/FLUX.1-schnell',
}
print(f"🎨 Free Image Generator initialized (NO API KEYS NEEDED)")
print(f" Output directory: {self.output_dir}")
print(f" User ID: {user_id if user_id else 'default'}")
print(f" Local GPU available: {LOCAL_SD_AVAILABLE}")
print(f" Free methods: Hugging Face API, Local GPU, Replicate (free credits)")
# ==================== METHOD 1: HUGGING FACE API (100% FREE, NO KEY NEEDED) ====================
def generate_huggingface(self, prompt: str, output_name: Optional[str] = None,
model: str = 'sd1.5', negative_prompt: str = '',
width: int = 512, height: int = 512,
num_inference_steps: int = 30) -> str:
"""
Generate image using Hugging Face Inference API.
100% FREE - NO API KEY REQUIRED!
Free tier: 30,000 requests per month, rate limited.
Args:
prompt: Text description of the image
output_name: Optional output filename
model: Model to use (sd1.5, sdxl, flux-schnell, etc.)
negative_prompt: What to avoid in the image
width: Image width (512-1024 depending on model)
height: Image height
num_inference_steps: Quality vs speed (20-50)
Returns:
Path to generated image
"""
if model not in self.free_models:
raise ValueError(f"Model {model} not found. Available: {list(self.free_models.keys())}")
model_id = self.free_models[model]
# UPDATED: New API endpoint (old one is deprecated)
API_URL = f"https://router.huggingface.co/hf-inference/models/{model_id}"
payload = {
"inputs": prompt,
"parameters": {
"negative_prompt": negative_prompt,
"width": width,
"height": height,
"num_inference_steps": num_inference_steps,
"guidance_scale": 7.5
}
}
print(f"Generating with Hugging Face API (free, no key needed)...")
# Make request (no authentication!)
response = requests.post(API_URL, json=payload)
if response.status_code == 200:
# Save image
if not output_name:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_name = f"hf_{model}_{timestamp}.png"
output_path = self.output_dir / output_name
with open(output_path, 'wb') as f:
f.write(response.content)
print(f"✅ Image generated via Hugging Face: {output_path}")
return str(output_path)
elif response.status_code == 503:
# Model is loading, wait and retry
print("Model is loading, retrying in 5 seconds...")
import time
time.sleep(5)
return self.generate_huggingface(prompt, output_name, model, negative_prompt, width, height, num_inference_steps)
else:
error_msg = f"API Error {response.status_code}: {response.text[:200]}"
if response.status_code == 401:
error_msg += "\nNote: No API key needed! This error usually means the model is busy. Try again in a few seconds."
raise Exception(error_msg)
# ==================== METHOD 2: LOCAL STABLE DIFFUSION (100% FREE, NO INTERNET NEEDED) ====================
def generate_local_sd(self, prompt: str, output_name: Optional[str] = None,
model: str = 'sd1.5', negative_prompt: str = '',
width: int = 512, height: int = 512,
num_inference_steps: int = 30,
guidance_scale: float = 7.5) -> str:
"""
Generate image using local Stable Diffusion.
100% FREE - NO API KEYS, NO INTERNET NEEDED (after models downloaded)!
Requires GPU with 4GB+ VRAM.
Args:
prompt: Text description
output_name: Optional filename
model: Model to use (sd1.5, sdxl, sdxl-turbo, flux-schnell)
negative_prompt: What to avoid
width: Image width
height: Image height
num_inference_steps: Quality vs speed
guidance_scale: How closely to follow prompt (1-20)
"""
if not LOCAL_SD_AVAILABLE:
raise ImportError("Local generation not installed. Run: pip install diffusers transformers torch accelerate")
global _sd_pipe, _sdxl_pipe, _flux_pipe
print(f"Generating locally with {model} (100% free, no API keys)...")
# Select appropriate pipeline
if model == 'sdxl' or model == 'sdxl-turbo':
if _sdxl_pipe is None:
print(f"Loading {model} model (first time, may take a while)...")
model_id = self.free_models[model]
# Check if CUDA is available, otherwise use float32 for CPU
if torch.cuda.is_available():
_sdxl_pipe = StableDiffusionXLPipeline.from_pretrained(
model_id,
torch_dtype=torch.float16,
variant="fp16" if model == 'sdxl' else None
)
else:
_sdxl_pipe = StableDiffusionXLPipeline.from_pretrained(
model_id,
torch_dtype=torch.float32
)
if model == 'sdxl-turbo':
from diffusers import EulerDiscreteScheduler
_sdxl_pipe.scheduler = EulerDiscreteScheduler.from_pretrained(
model_id, subfolder="scheduler"
)
device = "cuda" if torch.cuda.is_available() else "cpu"
_sdxl_pipe = _sdxl_pipe.to(device)
pipe = _sdxl_pipe
elif model == 'flux-schnell':
if _flux_pipe is None:
print(f"Loading Flux model (first time, may take a while)...")
model_id = self.free_models[model]
_flux_pipe = FluxPipeline.from_pretrained(
model_id,
torch_dtype=torch.bfloat16
)
device = "cuda" if torch.cuda.is_available() else "cpu"
_flux_pipe = _flux_pipe.to(device)
pipe = _flux_pipe
else: # sd1.5, sd2.1, etc.
if _sd_pipe is None:
print(f"Loading {model} model (first time, may take a while)...")
model_id = self.free_models[model]
# Check if CUDA is available, otherwise use float32 for CPU
if torch.cuda.is_available():
_sd_pipe = StableDiffusionPipeline.from_pretrained(
model_id,
torch_dtype=torch.float16
)
else:
_sd_pipe = StableDiffusionPipeline.from_pretrained(
model_id,
torch_dtype=torch.float32
)
device = "cuda" if torch.cuda.is_available() else "cpu"
_sd_pipe = _sd_pipe.to(device)
pipe = _sd_pipe
# Special handling for turbo models
if model == 'sdxl-turbo':
num_inference_steps = min(num_inference_steps, 4) # Turbo works best with 1-4 steps
guidance_scale = 0.0 # Turbo uses no guidance
# Generate image
with torch.no_grad():
result = pipe(
prompt,
negative_prompt=negative_prompt if negative_prompt else None,
num_inference_steps=num_inference_steps,
guidance_scale=guidance_scale,
width=width,
height=height
)
image = result.images[0]
# Save image
if not output_name:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_name = f"local_{model}_{timestamp}.png"
output_path = self.output_dir / output_name
image.save(output_path)
print(f"✅ Image generated locally: {output_path}")
return str(output_path)
# ==================== METHOD 3: REPLICATE (FREE CREDITS, NO CREDIT CARD) ====================
def generate_replicate(self, prompt: str, output_name: Optional[str] = None,
model: str = 'sd1.5') -> str:
"""
Generate using Replicate API.
100% FREE - $10 initial credits (NO CREDIT CARD REQUIRED)!
That's about 500-1000 free images.
Args:
prompt: Text description
output_name: Optional filename
model: Model to use (sd1.5, sdxl, flux)
"""
if not REPLICATE_AVAILABLE:
raise ImportError("Replicate not installed. Run: pip install replicate")
# Model IDs for Replicate
replicate_models = {
'sd1.5': "stability-ai/stable-diffusion:db21e45d3f7023abc2a46ee38a23973f6dce16bb082a930b0c49861f96d1e5bf",
'sdxl': "stability-ai/sdxl:39ed52f2a78e934b3ba6e2a89f5b1c712de7dfea535525255b1aa35c5565e08b",
'flux': "black-forest-labs/flux-schnell",
}
model_id = replicate_models.get(model, replicate_models['sd1.5'])
print(f"Generating with Replicate (free credits, no credit card required)...")
# Run generation
output = replicate.run(
model_id,
input={"prompt": prompt}
)
# Download image
if not output_name:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_name = f"replicate_{model}_{timestamp}.png"
output_path = self.output_dir / output_name
# Handle different output formats
if isinstance(output, list):
image_url = output[0]
else:
image_url = output
response = requests.get(image_url)
with open(output_path, 'wb') as f:
f.write(response.content)
print(f"✅ Image generated via Replicate: {output_path}")
return str(output_path)
# ==================== METHOD 4: AUTOMATIC1111 API (100% FREE LOCAL SERVER) ====================
def generate_automatic1111(self, prompt: str, output_name: Optional[str] = None,
server_url: str = "http://127.0.0.1:7860",
negative_prompt: str = "",
width: int = 512, height: int = 512,
steps: int = 20) -> str:
"""
Generate using Automatic1111 Web UI API.
100% FREE - Runs locally, NO API KEYS NEEDED!
Requires running Automatic1111 server locally.
Args:
prompt: Text description
output_name: Optional filename
server_url: URL of Automatic1111 server
negative_prompt: What to avoid
width: Image width
height: Image height
steps: Number of inference steps
"""
payload = {
"prompt": prompt,
"negative_prompt": negative_prompt,
"steps": steps,
"width": width,
"height": height,
"cfg_scale": 7,
"sampler_index": "Euler a",
"batch_size": 1
}
print(f"Generating with Automatic1111 (local, no API keys)...")
response = requests.post(f"{server_url}/sdapi/v1/txt2img", json=payload)
if response.status_code == 200:
r = response.json()
# Decode base64 image
image_data = base64.b64decode(r['images'][0])
# Save image
if not output_name:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_name = f"auto1111_{timestamp}.png"
output_path = self.output_dir / output_name
with open(output_path, 'wb') as f:
f.write(image_data)
print(f"✅ Image generated via Automatic1111: {output_path}")
return str(output_path)
else:
raise Exception(f"Automatic1111 error: {response.text}")
# ==================== UTILITY METHODS ====================
def get_available_methods(self) -> Dict[str, bool]:
"""Check which generation methods are available (all free!)"""
return {
'huggingface': True, # Always available, no key needed
'local_sd': LOCAL_SD_AVAILABLE,
'replicate': REPLICATE_AVAILABLE,
'automatic1111': self._check_automatic1111(),
}
def _check_automatic1111(self, server_url: str = "http://127.0.0.1:7860") -> bool:
"""Check if Automatic1111 server is running"""
try:
response = requests.get(f"{server_url}/sdapi/v1/sd-models", timeout=2)
return response.status_code == 200
except:
return False
def list_free_models(self) -> Dict[str, str]:
"""List all available free models"""
return self.free_models.copy()
def generate_with_fallback(self, prompt: str, output_name: Optional[str] = None,
preferred_methods: List[str] = None) -> str:
"""
Generate image with automatic fallback to available methods.
All methods are 100% free!
Args:
prompt: Text description
output_name: Optional filename
preferred_methods: Order of methods to try
Returns:
Path to generated image
"""
if preferred_methods is None:
preferred_methods = ['local_sd', 'huggingface', 'replicate', 'automatic1111']
methods = {
'local_sd': self.generate_local_sd,
'huggingface': self.generate_huggingface,
'replicate': self.generate_replicate,
'automatic1111': self.generate_automatic1111,
}
for method_name in preferred_methods:
if method_name in methods and self.get_available_methods().get(method_name, False):
try:
print(f"\nTrying {method_name}...")
return methods[method_name](prompt, output_name)
except Exception as e:
print(f"{method_name} failed: {e}")
continue
raise Exception("All generation methods failed. Try: pip install diffusers transformers torch accelerate")
def get_user_image_generator(user_id: str) -> FreeImageGenerator:
"""
Get a FreeImageGenerator instance for a specific user.
This ensures images are stored in the user's isolated directory.
Args:
user_id: User ID for isolated storage
Returns:
FreeImageGenerator instance configured for the user
"""
return FreeImageGenerator(user_id=user_id)
def get_user_generated_image_path(user_id: str, filename: str) -> Path:
"""
Get the full path to a user's generated image.
Returns None if file doesn't exist.
Args:
user_id: User ID
filename: Image filename
Returns:
Path to the image file if it exists, None otherwise
"""
user_images_dir = Path('/data/users') / str(user_id) / 'generated_images'
filepath = user_images_dir / filename
if filepath.exists():
return filepath
return None
def list_user_generated_images(user_id: str) -> List[Dict[str, Any]]:
"""
List all generated images for a user.
Args:
user_id: User ID
Returns:
List of image info dictionaries
"""
user_images_dir = Path('/data/users') / str(user_id) / 'generated_images'
if not user_images_dir.exists():
return []
images = []
for filename in user_images_dir.iterdir():
if filename.is_file() and filename.suffix.lower() in ('.png', '.jpg', '.jpeg', '.gif', '.webp'):
stat = filename.stat()
images.append({
'filename': filename.name,
'path': str(filename),
'size': stat.st_size,
'created_at': datetime.fromtimestamp(stat.st_ctime).isoformat()
})
# Sort by creation date (newest first)
images.sort(key=lambda x: x['created_at'], reverse=True)
return images
# ==================== CONVENIENCE FUNCTIONS ====================
def create_free_image_generator(output_dir: str = "generated_images", user_id: str = None) -> FreeImageGenerator:
"""Factory function to create FreeImageGenerator instance (NO API KEYS NEEDED!)
Args:
output_dir: Directory to save generated images (ignored if user_id provided)
user_id: User ID for isolated storage (for multi-user support)
"""
return FreeImageGenerator(output_dir, user_id=user_id)
# ==================== USAGE EXAMPLES ====================
if __name__ == "__main__":
# Create generator (NO API KEYS!)
generator = create_free_image_generator()
# Check available methods
print("\n" + "="*50)
print("AVAILABLE METHODS (ALL FREE):")
print("="*50)
for method, available in generator.get_available_methods().items():
status = "✅ Available" if available else "❌ Not available"
print(f" {method}: {status}")
print(f"\nAvailable models: {', '.join(generator.list_free_models().keys())}")
# Example: Generate via Hugging Face (no key needed!)
print("\n" + "="*50)
print("EXAMPLE: Generating via Hugging Face (NO API KEY)")
print("="*50)
try:
path = generator.generate_huggingface(
"a beautiful sunset over mountains, highly detailed, 4k",
output_name="sunset_hf.png"
)
print(f"\n✅ Image saved to: {path}")
except Exception as e:
print(f"\n⚠️ Could not generate: {e}")
print("This may be due to rate limiting. Try again in a few seconds.")
print("\n✅ All examples completed!")