Spaces:
Sleeping
Sleeping
Lcmind commited on
Commit ·
fc244a5
1
Parent(s): 249d349
fix: S-tier prompt - hex to color name, blur UI text, remove watermarks
Browse files- app/services/flux.py +70 -19
app/services/flux.py
CHANGED
|
@@ -6,6 +6,58 @@ import io
|
|
| 6 |
from app.core.config import settings
|
| 7 |
|
| 8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
async def generate_poster(analysis: dict) -> str:
|
| 10 |
"""
|
| 11 |
Generate a poster using Flux.1-schnell based on analysis.
|
|
@@ -22,34 +74,34 @@ async def generate_poster(analysis: dict) -> str:
|
|
| 22 |
"""
|
| 23 |
# Extract analysis data
|
| 24 |
brand_name = analysis.get('brand_name', 'BRAND').upper()
|
| 25 |
-
business_type = analysis.get('business_type', 'Productivity')
|
| 26 |
poster_objects = analysis.get('poster_objects', 'modern workspace elements')
|
| 27 |
background_style = analysis.get('background_style', 'Clean gradient')
|
| 28 |
primary_color = analysis.get('primary_color', '#4A90D9')
|
| 29 |
mood = analysis.get('mood', 'Clean')
|
| 30 |
|
| 31 |
-
#
|
|
|
|
|
|
|
|
|
|
| 32 |
#
|
| 33 |
-
# Rule 1:
|
| 34 |
-
# Rule 2:
|
| 35 |
-
# Rule 3:
|
| 36 |
-
# Rule 4:
|
| 37 |
-
# Rule 5:
|
| 38 |
|
| 39 |
-
prompt = f"""
|
| 40 |
-
|
| 41 |
-
Typography: Bold modern text "{brand_name}" prominently displayed at top center of the frame, clean sans-serif font, white text with subtle shadow, professional branding typography.
|
| 42 |
|
| 43 |
-
|
| 44 |
|
| 45 |
-
|
| 46 |
|
| 47 |
-
|
| 48 |
|
| 49 |
-
|
| 50 |
|
| 51 |
-
#
|
| 52 |
-
negative_prompt = "
|
| 53 |
|
| 54 |
headers = {
|
| 55 |
"Authorization": f"Bearer {settings.hf_token}",
|
|
@@ -61,15 +113,14 @@ Technical: Studio lighting, soft shadows, sharp focus, 8k resolution, commercial
|
|
| 61 |
"inputs": prompt,
|
| 62 |
"parameters": {
|
| 63 |
"negative_prompt": negative_prompt,
|
| 64 |
-
"num_inference_steps": 4,
|
| 65 |
-
"guidance_scale": 0.0,
|
| 66 |
"width": 768,
|
| 67 |
"height": 1344,
|
| 68 |
}
|
| 69 |
}
|
| 70 |
|
| 71 |
async with httpx.AsyncClient(timeout=60.0) as client:
|
| 72 |
-
# Use new HF Router endpoint
|
| 73 |
response = await client.post(
|
| 74 |
f"https://router.huggingface.co/hf-inference/models/{settings.flux_model}",
|
| 75 |
headers=headers,
|
|
|
|
| 6 |
from app.core.config import settings
|
| 7 |
|
| 8 |
|
| 9 |
+
def hex_to_color_name(hex_color: str) -> str:
|
| 10 |
+
"""Convert hex color to descriptive color name to prevent hex appearing in image."""
|
| 11 |
+
hex_color = hex_color.upper().replace('#', '')
|
| 12 |
+
|
| 13 |
+
# Common brand colors mapping
|
| 14 |
+
color_map = {
|
| 15 |
+
'4285F4': 'vibrant blue', # Google Blue
|
| 16 |
+
'DB4437': 'warm red', # Google Red
|
| 17 |
+
'F4B400': 'golden yellow', # Google Yellow
|
| 18 |
+
'0F9D58': 'fresh green', # Google Green
|
| 19 |
+
'1877F2': 'facebook blue',
|
| 20 |
+
'FF0000': 'bold red', # YouTube
|
| 21 |
+
'000000': 'deep black',
|
| 22 |
+
'FFFFFF': 'pure white',
|
| 23 |
+
'4A90D9': 'sky blue',
|
| 24 |
+
'FF6B6B': 'coral pink',
|
| 25 |
+
'6C5CE7': 'electric purple',
|
| 26 |
+
'00D4AA': 'mint green',
|
| 27 |
+
'FFD93D': 'sunny yellow',
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
if hex_color in color_map:
|
| 31 |
+
return color_map[hex_color]
|
| 32 |
+
|
| 33 |
+
# Parse RGB and describe
|
| 34 |
+
try:
|
| 35 |
+
r = int(hex_color[0:2], 16)
|
| 36 |
+
g = int(hex_color[2:4], 16)
|
| 37 |
+
b = int(hex_color[4:6], 16)
|
| 38 |
+
|
| 39 |
+
# Determine dominant color
|
| 40 |
+
if r > g and r > b:
|
| 41 |
+
if r > 200: return 'bright red tones'
|
| 42 |
+
return 'warm red tones'
|
| 43 |
+
elif g > r and g > b:
|
| 44 |
+
if g > 200: return 'vibrant green tones'
|
| 45 |
+
return 'fresh green tones'
|
| 46 |
+
elif b > r and b > g:
|
| 47 |
+
if b > 200: return 'bright blue tones'
|
| 48 |
+
return 'cool blue tones'
|
| 49 |
+
elif r > 200 and g > 200:
|
| 50 |
+
return 'warm golden tones'
|
| 51 |
+
elif r > 200 and b > 200:
|
| 52 |
+
return 'magenta pink tones'
|
| 53 |
+
elif g > 200 and b > 200:
|
| 54 |
+
return 'cyan aqua tones'
|
| 55 |
+
else:
|
| 56 |
+
return 'neutral tones'
|
| 57 |
+
except:
|
| 58 |
+
return 'harmonious tones'
|
| 59 |
+
|
| 60 |
+
|
| 61 |
async def generate_poster(analysis: dict) -> str:
|
| 62 |
"""
|
| 63 |
Generate a poster using Flux.1-schnell based on analysis.
|
|
|
|
| 74 |
"""
|
| 75 |
# Extract analysis data
|
| 76 |
brand_name = analysis.get('brand_name', 'BRAND').upper()
|
|
|
|
| 77 |
poster_objects = analysis.get('poster_objects', 'modern workspace elements')
|
| 78 |
background_style = analysis.get('background_style', 'Clean gradient')
|
| 79 |
primary_color = analysis.get('primary_color', '#4A90D9')
|
| 80 |
mood = analysis.get('mood', 'Clean')
|
| 81 |
|
| 82 |
+
# Convert hex to color name (prevents hex code appearing in image)
|
| 83 |
+
color_description = hex_to_color_name(primary_color)
|
| 84 |
+
|
| 85 |
+
# === S-TIER PROMPT ENGINEER RULES ===
|
| 86 |
#
|
| 87 |
+
# Rule 1: ONLY the brand name as readable text
|
| 88 |
+
# Rule 2: All other UI elements = blurred/abstract/no text
|
| 89 |
+
# Rule 3: Color as description, NOT hex code
|
| 90 |
+
# Rule 4: Explicit "no watermark" in both prompt and negative
|
| 91 |
+
# Rule 5: Clean, minimal composition
|
| 92 |
|
| 93 |
+
prompt = f"""Minimalist commercial poster, vertical 9:16 aspect ratio, ultra clean design.
|
|
|
|
|
|
|
| 94 |
|
| 95 |
+
The word "{brand_name}" in bold white sans-serif typography, centered at top, professional lettering with soft drop shadow.
|
| 96 |
|
| 97 |
+
Below: {poster_objects} with all screens and interfaces showing abstract colorful shapes and blurred gradients instead of text, no readable text on any UI element, purely visual design elements.
|
| 98 |
|
| 99 |
+
Background: {background_style}, {mood.lower()} mood, {color_description} color scheme.
|
| 100 |
|
| 101 |
+
Style: Apple keynote presentation quality, premium advertising, high-end product showcase, pristine studio photography, no watermarks, no signatures, no logos except the brand name."""
|
| 102 |
|
| 103 |
+
# Aggressive negative prompt for clean output
|
| 104 |
+
negative_prompt = "watermark, signature, logo, copyright, trademark, website url, username, id number, serial number, code, hex code, random letters, gibberish text, corrupted text, glitched text, small text, fine print, label, tag, badge, stamp, readable text on screens, text on monitors, text on UI, multiple texts, extra text, any text except brand name"
|
| 105 |
|
| 106 |
headers = {
|
| 107 |
"Authorization": f"Bearer {settings.hf_token}",
|
|
|
|
| 113 |
"inputs": prompt,
|
| 114 |
"parameters": {
|
| 115 |
"negative_prompt": negative_prompt,
|
| 116 |
+
"num_inference_steps": 4,
|
| 117 |
+
"guidance_scale": 0.0,
|
| 118 |
"width": 768,
|
| 119 |
"height": 1344,
|
| 120 |
}
|
| 121 |
}
|
| 122 |
|
| 123 |
async with httpx.AsyncClient(timeout=60.0) as client:
|
|
|
|
| 124 |
response = await client.post(
|
| 125 |
f"https://router.huggingface.co/hf-inference/models/{settings.flux_model}",
|
| 126 |
headers=headers,
|