DHEIVER commited on
Commit
67867a6
·
verified ·
1 Parent(s): cf838f4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +169 -186
app.py CHANGED
@@ -1,186 +1,169 @@
1
- /* Paleta de cores */
2
- :root {
3
- --primary-color: #4A90E2; /* Azul suave */
4
- --secondary-color: #50C878; /* Verde esmeralda */
5
- --background-color: #F7F9FC; /* Cinza claro */
6
- --text-color: #333333; /* Preto suave */
7
- --accent-color: #F5A623; /* Laranja vibrante */
8
- --border-color: #DDE4E9; /* Cinza claro */
9
- --hover-color: #357ABD; /* Azul mais escuro */
10
- }
11
-
12
- /* Estilo geral */
13
- body {
14
- background-color: var(--background-color);
15
- color: var(--text-color);
16
- font-family: 'Arial', sans-serif;
17
- padding: 20px;
18
- }
19
-
20
- /* Cabeçalho */
21
- .header-container {
22
- display: flex;
23
- align-items: center;
24
- padding: 15px 25px;
25
- background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
26
- color: white;
27
- border-radius: 10px;
28
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
29
- margin-bottom: 20px;
30
- }
31
-
32
- .logo img {
33
- width: 50px;
34
- margin-right: 20px;
35
- }
36
-
37
- .header-text h1 {
38
- margin: 0;
39
- font-size: 28px;
40
- font-weight: bold;
41
- }
42
-
43
- .header-text p {
44
- margin: 5px 0 0;
45
- font-size: 14px;
46
- }
47
-
48
- .header-text a {
49
- color: var(--accent-color);
50
- text-decoration: none;
51
- transition: color 0.3s;
52
- }
53
-
54
- .header-text a:hover {
55
- color: white;
56
- }
57
-
58
- /* Abas */
59
- .gr-tabs {
60
- background-color: white;
61
- border-radius: 10px;
62
- padding: 15px;
63
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
64
- }
65
-
66
- /* Acordeões */
67
- .config-accordion, .instructions-accordion {
68
- border: 1px solid var(--border-color);
69
- border-radius: 8px;
70
- padding: 10px;
71
- background-color: #fff;
72
- }
73
-
74
- .config-accordion h3, .instructions-accordion h3 {
75
- color: var(--primary-color);
76
- font-size: 18px;
77
- }
78
-
79
- /* Entrada e Saída */
80
- .upload-box {
81
- border: 2px dashed var(--primary-color);
82
- border-radius: 8px;
83
- padding: 15px;
84
- background-color: #fafafa;
85
- transition: border-color 0.3s;
86
- }
87
-
88
- .upload-box:hover {
89
- border-color: var(--hover-color);
90
- }
91
-
92
- .api-key-input, .prompt-input {
93
- border: 1px solid var(--border-color);
94
- border-radius: 6px;
95
- padding: 10px;
96
- font-size: 14px;
97
- transition: border-color 0.3s;
98
- }
99
-
100
- .api-key-input:focus, .prompt-input:focus {
101
- border-color: var(--primary-color);
102
- outline: none;
103
- }
104
-
105
- .generate-btn {
106
- background-color: var(--secondary-color);
107
- color: white;
108
- border: none;
109
- padding: 12px 25px;
110
- border-radius: 6px;
111
- font-weight: bold;
112
- cursor: pointer;
113
- transition: background-color 0.3s;
114
- }
115
-
116
- .generate-btn:hover {
117
- background-color: #45b065;
118
- }
119
-
120
- .gr-button.secondary {
121
- background-color: #e0e0e0;
122
- color: var(--text-color);
123
- padding: 12px 25px;
124
- border-radius: 6px;
125
- }
126
-
127
- .gr-button.secondary:hover {
128
- background-color: #d0d0d0;
129
- }
130
-
131
- /* Galeria e texto de saída */
132
- .output-gallery {
133
- border: 1px solid var(--border-color);
134
- border-radius: 8px;
135
- padding: 10px;
136
- background-color: #fff;
137
- min-height: 200px;
138
- }
139
-
140
- .output-text {
141
- border: 1px solid var(--border-color);
142
- border-radius: 6px;
143
- padding: 10px;
144
- min-height: 100px;
145
- font-size: 14px;
146
- background-color: #fafafa;
147
- }
148
-
149
- /* Exemplos */
150
- .gr-examples-header {
151
- color: var(--primary-color);
152
- font-size: 22px;
153
- margin: 20px 0 10px;
154
- text-align: center;
155
- }
156
-
157
- #examples-grid {
158
- display: grid;
159
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
160
- gap: 15px;
161
- padding: 10px;
162
- background-color: white;
163
- border-radius: 8px;
164
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
165
- }
166
-
167
- /* Tooltips (personalizado) */
168
- .gr-tooltip {
169
- position: relative;
170
- display: inline-block;
171
- }
172
-
173
- .gr-tooltip:hover::after {
174
- content: attr(tooltips);
175
- position: absolute;
176
- bottom: 100%;
177
- left: 50%;
178
- transform: translateX(-50%);
179
- background-color: #333;
180
- color: white;
181
- padding: 5px 10px;
182
- border-radius: 4px;
183
- font-size: 12px;
184
- white-space: nowrap;
185
- z-index: 10;
186
- }
 
1
+ import json
2
+ import os
3
+ import time
4
+ import uuid
5
+ import tempfile
6
+ from PIL import Image, ImageDraw, ImageFont
7
+ import gradio as gr
8
+ import base64
9
+ import mimetypes
10
+
11
+ from google import genai
12
+ from google.genai import types
13
+
14
+ def save_binary_file(file_name, data):
15
+ with open(file_name, "wb") as f:
16
+ f.write(data)
17
+
18
+ def generate(text, file_name, api_key, model="gemini-2.0-flash-exp"):
19
+ client = genai.Client(api_key=(api_key.strip() if api_key and api_key.strip() != "" else os.environ.get("GEMINI_API_KEY")))
20
+
21
+ files = [client.files.upload(file=file_name)]
22
+
23
+ contents = [
24
+ types.Content(
25
+ role="user",
26
+ parts=[
27
+ types.Part.from_uri(file_uri=files[0].uri, mime_type=files[0].mime_type),
28
+ types.Part.from_text(text=text),
29
+ ],
30
+ ),
31
+ ]
32
+ generate_content_config = types.GenerateContentConfig(
33
+ temperature=1,
34
+ top_p=0.95,
35
+ top_k=40,
36
+ max_output_tokens=8192,
37
+ response_modalities=["image", "text"],
38
+ response_mime_type="text/plain",
39
+ )
40
+
41
+ text_response = ""
42
+ image_path = None
43
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
44
+ temp_path = tmp.name
45
+ for chunk in client.models.generate_content_stream(model=model, contents=contents, config=generate_content_config):
46
+ if not chunk.candidates or not chunk.candidates[0].content or not chunk.candidates[0].content.parts:
47
+ continue
48
+ candidate = chunk.candidates[0].content.parts[0]
49
+ if candidate.inline_data:
50
+ save_binary_file(temp_path, candidate.inline_data.data)
51
+ print(f"Arquivo de tipo {candidate.inline_data.mime_type} salvo em: {temp_path} com prompt: {text}")
52
+ image_path = temp_path
53
+ break
54
+ else:
55
+ text_response += chunk.text + "\n"
56
+
57
+ del files
58
+ return image_path, text_response
59
+
60
+ def process_image_and_prompt(composite_pil, prompt, gemini_api_key):
61
+ try:
62
+ if not composite_pil:
63
+ raise gr.Error("Por favor, carregue uma imagem antes de gerar.", duration=5)
64
+ if not prompt:
65
+ raise gr.Error("Por favor, insira um prompt antes de gerar.", duration=5)
66
+ if not gemini_api_key and not os.environ.get("GEMINI_API_KEY"):
67
+ raise gr.Error("Por favor, forneça uma chave API Gemini ou configure a variável de ambiente GEMINI_API_KEY.", duration=10)
68
+
69
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
70
+ composite_path = tmp.name
71
+ composite_pil.save(composite_path)
72
+
73
+ image_path, text_response = generate(text=prompt, file_name=composite_path, api_key=gemini_api_key)
74
+
75
+ if image_path:
76
+ result_img = Image.open(image_path)
77
+ if result_img.mode == "RGBA":
78
+ result_img = result_img.convert("RGB")
79
+ return [result_img], ""
80
+ else:
81
+ return None, text_response
82
+ except Exception as e:
83
+ raise gr.Error(f"Erro ao processar: {e}", duration=5)
84
+
85
+ # Interface com Gradio
86
+ with gr.Blocks(css="style.css", title="Gemini para Edição de Imagens") as demo:
87
+ gr.HTML(
88
+ """
89
+ <div class="header-container">
90
+ <div class="logo">
91
+ <img src="https://www.gstatic.com/lamda/images/gemini_favicon_f069958c85030456e93de685481c559f160ea06b.png" alt="Logo Gemini">
92
+ </div>
93
+ <div class="header-text">
94
+ <h1>✨ Gemini para Edição de Imagens ✨</h1>
95
+ <p>Desenvolvido com <a href="https://gradio.app/">Gradio</a> ⚡️ |
96
+ <a href="https://huggingface.co/spaces/ameerazam08/Gemini-Image-Edit?duplicate=true">Duplique este Repositório</a> |
97
+ <a href="https://aistudio.google.com/apikey">Obtenha uma Chave API</a> |
98
+ Siga-me no LinkedIn: <a href="https://www.linkedin.com/in/dheiver-santos/">dheiver-santos</a></p>
99
+ </div>
100
+ </div>
101
+ """
102
+ )
103
+
104
+ with gr.Tabs():
105
+ with gr.TabItem("Editar Imagem"):
106
+ with gr.Row():
107
+ with gr.Column(scale=1, min_width=300):
108
+ gr.Markdown("### 📤 Entrada")
109
+ image_input = gr.Image(type="pil", label="Carregar Imagem (PNG)", image_mode="RGBA", elem_classes="upload-box")
110
+ gemini_api_key = gr.Textbox(lines=1, placeholder="Insira a Chave API Gemini", label="Chave API Gemini",
111
+ elem_classes="api-key-input", type="password")
112
+ prompt_input = gr.Textbox(lines=3, placeholder="Digite seu prompt aqui...", label="Prompt de Edição",
113
+ elem_classes="prompt-input")
114
+ with gr.Row():
115
+ submit_btn = gr.Button("Gerar Imagem", variant="primary", elem_classes="generate-btn")
116
+ clear_btn = gr.Button("Limpar", variant="secondary")
117
+
118
+ with gr.Column(scale=2, min_width=400):
119
+ gr.Markdown("### 📷 Resultado")
120
+ output_gallery = gr.Gallery(label="Imagem Gerada", elem_classes="output-gallery", preview=True)
121
+ output_text = gr.Textbox(label="Saída de Texto (se aplicável)", placeholder="Se nenhuma imagem for gerada, o texto aparecerá aqui.",
122
+ elem_classes="output-text", interactive=False)
123
+
124
+ with gr.TabItem("Instruções e Configuração"):
125
+ with gr.Row():
126
+ with gr.Column():
127
+ with gr.Accordion("⚠️ Configuração da API", open=False, elem_classes="config-accordion"):
128
+ gr.Markdown("""
129
+ - **Problema:** ❗ Às vezes, o modelo retorna texto em vez de uma imagem.
130
+ ### 🔧 Solução:
131
+ 1. **🛠️ Duplique o Repositório**
132
+ - Crie uma cópia separada para modificações.
133
+ 2. **🔑 Use Sua Própria Chave API Gemini**
134
+ - É **obrigatório** configurar sua chave Gemini para geração!
135
+ 3. **🌐 Verifique sua conexão**
136
+ - Certifique-se de que a API está acessível.
137
+ """)
138
+ with gr.Column():
139
+ with gr.Accordion("📌 Instruções de Uso", open=False, elem_classes="instructions-accordion"):
140
+ gr.Markdown("""
141
+ ### 📌 Como Usar
142
+ - Faça upload de uma imagem PNG no campo à esquerda.
143
+ - Insira um prompt em inglês descrevendo a edição desejada.
144
+ - Clique em "Gerar Imagem" para processar.
145
+ - Veja o resultado na galeria ou texto abaixo.
146
+ - **Dica:** Use prompts claros como "add lipstick on lips" ou "remove the spoon".
147
+ - ❌ **Não use imagens NSFW!**
148
+ """)
149
+
150
+ gr.Markdown("### 🎨 Exemplos Práticos", elem_classes="gr-examples-header")
151
+ examples = [
152
+ ["data/1.webp", "change text to 'AMEER'", ""],
153
+ ["data/2.webp", "remove the spoon from hand only", ""],
154
+ ["data/3.webp", "change text to 'Make it'", ""],
155
+ ["data/1.jpg", "add joker style only on face", ""],
156
+ ["data/1777043.jpg", "add joker style only on face", ""],
157
+ ["data/2807615.jpg", "add lipstick on lip only", ""],
158
+ ["data/76860.jpg", "add lipstick on lip only", ""],
159
+ ["data/2807615.jpg", "make it happy looking face only", ""],
160
+ ]
161
+ gr.Examples(examples=examples, inputs=[image_input, prompt_input, gemini_api_key], outputs=[output_gallery, output_text],
162
+ fn=process_image_and_prompt, cache_examples=False, elem_id="examples-grid")
163
+
164
+ # Eventos
165
+ submit_btn.click(fn=process_image_and_prompt, inputs=[image_input, prompt_input, gemini_api_key],
166
+ outputs=[output_gallery, output_text])
167
+ clear_btn.click(fn=lambda: (None, "", "", None, ""), inputs=[], outputs=[image_input, prompt_input, gemini_api_key, output_gallery, output_text])
168
+
169
+ demo.queue(max_size=50).launch()