File size: 7,909 Bytes
f04794a
00def7d
f04794a
 
 
7d93276
 
 
d556179
 
2ab4d4a
d556179
 
 
7d93276
7824523
e2710c9
aecd8c4
d556179
 
 
857b096
 
 
d556179
aecd8c4
d556179
 
 
 
 
 
ce3abb5
 
185cd3b
c0a27a1
185cd3b
 
c0a27a1
185cd3b
 
c0a27a1
185cd3b
 
c0a27a1
ce3abb5
d556179
aecd8c4
 
d556179
 
857b096
 
6045760
aecd8c4
6045760
 
857b096
 
7d93276
d556179
7d93276
 
 
857b096
d556179
7d93276
 
d556179
857b096
d556179
 
 
f04794a
 
d556179
aecd8c4
857b096
 
 
7d93276
 
aecd8c4
d556179
7d93276
857b096
7d93276
857b096
82c9256
 
 
 
 
 
 
 
 
857b096
 
 
 
 
 
 
 
 
 
 
 
 
 
7d93276
857b096
 
 
 
791ca28
 
857b096
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aecd8c4
857b096
 
 
 
 
 
 
 
 
 
 
 
aecd8c4
857b096
 
 
7824523
82c9256
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import gradio as gr
from google import genai
from google.genai import types
from PIL import Image
from io import BytesIO
import tempfile
import concurrent.futures
import os
import time
from threading import Lock

# Load API credentials from environment
api_key = os.environ.get("API_KEY")
univin_model = os.environ.get("univin")
client = genai.Client(api_key=api_key)

CALL_INTERVAL = 2.0 / 10.0  # seconds
_dt_lock = Lock()
_last_call_time = 0.0

def throttle():
    """
    Ensures there's at least CALL_INTERVAL seconds between API calls.
    """
    global _last_call_time
    with _dt_lock:
        now = time.time()
        elapsed = now - _last_call_time
        if elapsed < CALL_INTERVAL:
            time.sleep(CALL_INTERVAL - elapsed)
        _last_call_time = time.time()


PROMPT_VARIATIONS = [
    # 1: Front view
    "Create a high-resolution 800×800px image of the EXACT SAME product from a front view. CRITICAL INSTRUCTIONS: The product must be a PIXEL-PERFECT match to the original in EVERY aspect - EXACT same text (including font, size, spacing, and positioning), EXACT same colors (including all shades, gradients, and color codes), EXACT same materials and textures, EXACT same branding elements, EXACT same dimensions and proportions, EXACT same markings and labels, EXACT same finish and surface details, EXACT same shadows and highlights, EXACT same reflections and gloss, EXACT same embossing or debossing if present. DO NOT modify ANY aspect of the product. ONLY change the viewing angle to front view. Maintain a pure white background with professional studio lighting. The output must be a high-quality, e-commerce ready image with perfect color accuracy and sharp details.",
    
    # 2: Left-side view
    "Create a high-resolution 800×800px image of the EXACT SAME product from a left-side three-quarter (45-degree) angle. CRITICAL INSTRUCTIONS: The product must be a PIXEL-PERFECT match to the original in EVERY aspect - EXACT same text (including font, size, spacing, and positioning), EXACT same colors (including all shades, gradients, and color codes), EXACT same materials and textures, EXACT same branding elements, EXACT same dimensions and proportions, EXACT same markings and labels, EXACT same finish and surface details, EXACT same shadows and highlights, EXACT same reflections and gloss, EXACT same embossing or debossing if present. DO NOT modify ANY aspect of the product. ONLY change the viewing angle to left-side view. Maintain a pure white background with professional studio lighting. The output must be a high-quality, e-commerce ready image with perfect color accuracy and sharp details.",
    
    # 3: Right-side view
    "Create a high-resolution 800×800px image of the EXACT SAME product from a right-side three-quarter (45-degree) angle. CRITICAL INSTRUCTIONS: The product must be a PIXEL-PERFECT match to the original in EVERY aspect - EXACT same text (including font, size, spacing, and positioning), EXACT same colors (including all shades, gradients, and color codes), EXACT same materials and textures, EXACT same branding elements, EXACT same dimensions and proportions, EXACT same markings and labels, EXACT same finish and surface details, EXACT same shadows and highlights, EXACT same reflections and gloss, EXACT same embossing or debossing if present. DO NOT modify ANY aspect of the product. ONLY change the viewing angle to right-side view. Maintain a pure white background with professional studio lighting. The output must be a high-quality, e-commerce ready image with perfect color accuracy and sharp details.",
    
    # 4: Top-down view
    "Create a high-resolution 800×800px image of the EXACT SAME product from a top-down (bird's-eye) angle. CRITICAL INSTRUCTIONS: The product must be a PIXEL-PERFECT match to the original in EVERY aspect - EXACT same text (including font, size, spacing, and positioning), EXACT same colors (including all shades, gradients, and color codes), EXACT same materials and textures, EXACT same branding elements, EXACT same dimensions and proportions, EXACT same markings and labels, EXACT same finish and surface details, EXACT same shadows and highlights, EXACT same reflections and gloss, EXACT same embossing or debossing if present. DO NOT modify ANY aspect of the product. ONLY change the viewing angle to top-down view. Maintain a pure white background with professional studio lighting. The output must be a high-quality, e-commerce ready image with perfect color accuracy and sharp details."
]
# Detailed prompt variations for different backgrounds/angles

def process_variation(variation, input_image):
    # Throttle to respect API rate limit
    throttle()

    # Build text + image input
    text_input = (
        "Hi, this is a picture of a product.",
        variation
    )

    # Call the GenAI API
    response = client.models.generate_content(
        model=univin_model,
        contents=[text_input, input_image],
        config=types.GenerateContentConfig(response_modalities=['Text', 'Image'])
    )

    # Extract generated image bytes
    for part in response.candidates[0].content.parts:
        if part.inline_data is not None:
            img = Image.open(BytesIO(part.inline_data.data))
            # Save to temp file and return its path
            with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as tmp:
                img.save(tmp, format="PNG")
                return tmp.name
    return None


def generate_images(input_image):
    """
    Generate one image per prompt variation in parallel, respecting rate limits.
    """
    with concurrent.futures.ThreadPoolExecutor() as executor:
        futures = [
            executor.submit(process_variation, var, input_image)
            for var in PROMPT_VARIATIONS
        ]
        return [f.result() for f in futures if f.result()]

# Gradio UI setup with custom styling
def build_interface():
    custom_css = """
    #generate-button {
        background-color: #4A90E2;
        color: white;
        border-radius: 8px;
        padding: 12px 24px;
        font-size: 16px;
        margin-top: 10px;
    }
    #gallery .gallery-item {
        border-radius: 12px;
        box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        transition: transform 0.2s ease;
    }
    #gallery .gallery-item:hover {
        transform: scale(1.05);
    }
    #input-row {
        gap: 20px;
        align-items: start;
    }
    """

    demo = gr.Blocks(css=custom_css, theme=gr.themes.Soft())
    with demo:
        gr.Markdown(
            """
            ## 🎨 Uni-Imaginator
            **Create professional, e-commerce–ready product images effortlessly without worrying abot copyrights.**
            """
        )
        with gr.Row(elem_id="input-row"):
            with gr.Column(scale=1):
                input_image = gr.Image(
                    type="pil", label="Upload Product Image", elem_id="upload-img"
                )
                generate_button = gr.Button(
                    "Generate Images", variant="primary", elem_id="generate-button"
                )
            with gr.Column(scale=1):
                gr.Markdown(
                    """
                    **How to Use:**
                    1. Upload a clear photo of your product.
                    2. Click **Generate Images** and wait a few moments while images are processed within rate limits.
                    """
                )
        gallery = gr.Gallery(
            label="Generated Images",
            elem_id="gallery",
            columns=4,
            object_fit="contain",
            height="auto",
            show_label=False
        )
        generate_button.click(
            fn=generate_images,
            inputs=[input_image],
            outputs=gallery
        )
    return demo

# Launch the app
if __name__ == "__main__":
    demo_app = build_interface()
    demo_app.launch()