openfree commited on
Commit
4e0e71e
·
verified ·
1 Parent(s): f83f60a

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +299 -0
app.py ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import replicate
3
+ import os
4
+ from PIL import Image
5
+ import requests
6
+ from io import BytesIO
7
+ import time
8
+ import tempfile
9
+ import base64
10
+
11
+ # Set up Replicate API key from environment variable
12
+ os.environ['REPLICATE_API_TOKEN'] = os.getenv('REPLICATE_API_TOKEN')
13
+
14
+ def upload_image_to_hosting(image):
15
+ """
16
+ Upload image to multiple hosting services with fallback
17
+ """
18
+ # Method 1: Try imgbb.com (most reliable)
19
+ try:
20
+ buffered = BytesIO()
21
+ image.save(buffered, format="PNG")
22
+ buffered.seek(0)
23
+ img_base64 = base64.b64encode(buffered.getvalue()).decode()
24
+
25
+ response = requests.post(
26
+ "https://api.imgbb.com/1/upload",
27
+ data={
28
+ 'key': '6d207e02198a847aa98d0a2a901485a5',
29
+ 'image': img_base64,
30
+ }
31
+ )
32
+
33
+ if response.status_code == 200:
34
+ data = response.json()
35
+ if data.get('success'):
36
+ return data['data']['url']
37
+ except:
38
+ pass
39
+
40
+ # Method 2: Try 0x0.st (simple and reliable)
41
+ try:
42
+ buffered = BytesIO()
43
+ image.save(buffered, format="PNG")
44
+ buffered.seek(0)
45
+
46
+ files = {'file': ('image.png', buffered, 'image/png')}
47
+ response = requests.post("https://0x0.st", files=files)
48
+
49
+ if response.status_code == 200:
50
+ return response.text.strip()
51
+ except:
52
+ pass
53
+
54
+ # Method 3: Fallback to base64
55
+ buffered = BytesIO()
56
+ image.save(buffered, format="PNG")
57
+ buffered.seek(0)
58
+ img_base64 = base64.b64encode(buffered.getvalue()).decode()
59
+ return f"data:image/png;base64,{img_base64}"
60
+
61
+ def process_images(prompt, image1, image2=None):
62
+ """
63
+ Process uploaded images with Replicate API
64
+ """
65
+ if not image1:
66
+ return None, "Please upload at least one image"
67
+
68
+ if not os.getenv('REPLICATE_API_TOKEN'):
69
+ return None, "Please set REPLICATE_API_TOKEN"
70
+
71
+ try:
72
+ image_urls = []
73
+
74
+ # Upload images
75
+ url1 = upload_image_to_hosting(image1)
76
+ image_urls.append(url1)
77
+
78
+ if image2:
79
+ url2 = upload_image_to_hosting(image2)
80
+ image_urls.append(url2)
81
+
82
+ # Run the model
83
+ output = replicate.run(
84
+ "google/nano-banana",
85
+ input={
86
+ "prompt": prompt,
87
+ "image_input": image_urls
88
+ }
89
+ )
90
+
91
+ if output is None:
92
+ return None, "No output received"
93
+
94
+ # Get the generated image
95
+ try:
96
+ if hasattr(output, 'read'):
97
+ img_data = output.read()
98
+ img = Image.open(BytesIO(img_data))
99
+ return img, "✨ Generated successfully!"
100
+ except:
101
+ pass
102
+
103
+ try:
104
+ if hasattr(output, 'url'):
105
+ output_url = output.url()
106
+ response = requests.get(output_url, timeout=30)
107
+ if response.status_code == 200:
108
+ img = Image.open(BytesIO(response.content))
109
+ return img, "✨ Generated successfully!"
110
+ except:
111
+ pass
112
+
113
+ output_url = None
114
+ if isinstance(output, str):
115
+ output_url = output
116
+ elif isinstance(output, list) and len(output) > 0:
117
+ output_url = output[0]
118
+
119
+ if output_url:
120
+ response = requests.get(output_url, timeout=30)
121
+ if response.status_code == 200:
122
+ img = Image.open(BytesIO(response.content))
123
+ return img, "✨ Generated successfully!"
124
+
125
+ return None, "Could not process output"
126
+
127
+ except Exception as e:
128
+ return None, f"Error: {str(e)[:100]}"
129
+
130
+ # Enhanced CSS with modern, minimal design
131
+ css = """
132
+ .gradio-container {
133
+ background: linear-gradient(180deg, #0f0f0f 0%, #1a1a2e 100%);
134
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
135
+ min-height: 100vh;
136
+ }
137
+ .header-container {
138
+ background: linear-gradient(135deg, #ffd93d 0%, #ffb347 100%);
139
+ padding: 2rem;
140
+ border-radius: 20px;
141
+ margin-bottom: 2rem;
142
+ box-shadow: 0 20px 40px rgba(255, 217, 61, 0.3);
143
+ }
144
+ .logo-text {
145
+ font-size: 3.5rem;
146
+ font-weight: 900;
147
+ color: #1a1a2e;
148
+ text-align: center;
149
+ margin: 0;
150
+ letter-spacing: -2px;
151
+ }
152
+ .subtitle {
153
+ color: #2a2a3e;
154
+ text-align: center;
155
+ font-size: 1rem;
156
+ margin-top: 0.5rem;
157
+ opacity: 0.9;
158
+ }
159
+ .main-content {
160
+ background: rgba(255, 255, 255, 0.05);
161
+ backdrop-filter: blur(10px);
162
+ border-radius: 20px;
163
+ padding: 2rem;
164
+ border: 1px solid rgba(255, 255, 255, 0.1);
165
+ }
166
+ .gr-button-primary {
167
+ background: linear-gradient(135deg, #ffd93d 0%, #ffb347 100%) !important;
168
+ border: none !important;
169
+ color: #1a1a2e !important;
170
+ font-weight: 700 !important;
171
+ font-size: 1.1rem !important;
172
+ padding: 1rem 2rem !important;
173
+ border-radius: 12px !important;
174
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
175
+ text-transform: uppercase;
176
+ letter-spacing: 1px;
177
+ }
178
+ .gr-button-primary:hover {
179
+ transform: translateY(-2px) !important;
180
+ box-shadow: 0 10px 30px rgba(255, 217, 61, 0.4) !important;
181
+ }
182
+ .gr-input, .gr-textarea {
183
+ background: rgba(255, 255, 255, 0.08) !important;
184
+ border: 1px solid rgba(255, 255, 255, 0.15) !important;
185
+ border-radius: 12px !important;
186
+ color: white !important;
187
+ font-size: 1rem !important;
188
+ }
189
+ .gr-input:focus, .gr-textarea:focus {
190
+ border-color: #ffd93d !important;
191
+ box-shadow: 0 0 0 3px rgba(255, 217, 61, 0.1) !important;
192
+ }
193
+ .gr-form {
194
+ background: transparent !important;
195
+ border: none !important;
196
+ }
197
+ .gr-panel {
198
+ background: rgba(255, 255, 255, 0.03) !important;
199
+ border: 1px solid rgba(255, 255, 255, 0.1) !important;
200
+ border-radius: 16px !important;
201
+ padding: 1.5rem !important;
202
+ }
203
+ .gr-box {
204
+ border-radius: 12px !important;
205
+ border-color: rgba(255, 255, 255, 0.1) !important;
206
+ }
207
+ label {
208
+ color: rgba(255, 255, 255, 0.9) !important;
209
+ font-weight: 600 !important;
210
+ font-size: 0.9rem !important;
211
+ text-transform: uppercase;
212
+ letter-spacing: 1px;
213
+ margin-bottom: 0.5rem !important;
214
+ }
215
+ .status-text {
216
+ font-family: 'SF Mono', 'Monaco', monospace;
217
+ color: #ffd93d;
218
+ padding: 0.5rem 1rem;
219
+ background: rgba(255, 217, 61, 0.1);
220
+ border-radius: 8px;
221
+ border: 1px solid rgba(255, 217, 61, 0.3);
222
+ }
223
+ .image-container {
224
+ border-radius: 16px !important;
225
+ overflow: hidden;
226
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
227
+ }
228
+ footer {
229
+ display: none !important;
230
+ }
231
+ """
232
+
233
+ # Build the interface
234
+ with gr.Blocks(css=css, theme=gr.themes.Base()) as demo:
235
+ with gr.Column(elem_classes="header-container"):
236
+ gr.HTML("""
237
+ <h1 class="logo-text">🍌 Free Nano Banana</h1>
238
+ <p class="subtitle">AI-Powered Image Style Transfer</p>
239
+ """)
240
+
241
+ with gr.Column(elem_classes="main-content"):
242
+ with gr.Row():
243
+ with gr.Column(scale=1):
244
+ prompt = gr.Textbox(
245
+ label="Prompt",
246
+ placeholder="Describe your style...",
247
+ lines=2,
248
+ value="Make the sheets in the style of the logo. Make the scene natural."
249
+ )
250
+
251
+ with gr.Row():
252
+ image1 = gr.Image(
253
+ label="Image 1",
254
+ type="pil",
255
+ height=180,
256
+ elem_classes="image-container"
257
+ )
258
+ image2 = gr.Image(
259
+ label="Image 2 (Optional)",
260
+ type="pil",
261
+ height=180,
262
+ elem_classes="image-container"
263
+ )
264
+
265
+ generate_btn = gr.Button(
266
+ "Generate",
267
+ variant="primary",
268
+ size="lg"
269
+ )
270
+
271
+ with gr.Column(scale=1):
272
+ output_image = gr.Image(
273
+ label="Result",
274
+ type="pil",
275
+ height=400,
276
+ elem_classes="image-container"
277
+ )
278
+
279
+ status = gr.Textbox(
280
+ label="Status",
281
+ interactive=False,
282
+ lines=1,
283
+ elem_classes="status-text"
284
+ )
285
+
286
+ # Event handler
287
+ generate_btn.click(
288
+ fn=process_images,
289
+ inputs=[prompt, image1, image2],
290
+ outputs=[output_image, status]
291
+ )
292
+
293
+ # Launch
294
+ if __name__ == "__main__":
295
+ demo.launch(
296
+ share=True,
297
+ server_name="0.0.0.0",
298
+ server_port=7860
299
+ )