Update app.py
Browse files
app.py
CHANGED
@@ -9,9 +9,8 @@ import base64
|
|
9 |
import mimetypes
|
10 |
import logging
|
11 |
|
12 |
-
|
13 |
-
|
14 |
-
from google.generativeai import types
|
15 |
|
16 |
# .env 파일에 저장된 환경변수 로드 (python-dotenv 설치 필요: pip install python-dotenv)
|
17 |
from dotenv import load_dotenv
|
@@ -22,14 +21,17 @@ logging.basicConfig(level=logging.DEBUG,
|
|
22 |
format='%(asctime)s - %(levelname)s - %(message)s')
|
23 |
logger = logging.getLogger(__name__)
|
24 |
|
|
|
25 |
def save_binary_file(file_name, data):
|
26 |
logger.debug(f"파일에 이진 데이터 저장 중: {file_name}")
|
27 |
with open(file_name, "wb") as f:
|
28 |
f.write(data)
|
29 |
logger.debug(f"파일 저장 완료: {file_name}")
|
30 |
|
31 |
-
|
|
|
32 |
logger.debug(f"generate 함수 시작 - 텍스트: '{text}', 파일명: '{file_name}', 모델: '{model}'")
|
|
|
33 |
try:
|
34 |
# API 키는 환경변수에서 불러옴
|
35 |
effective_api_key = os.environ.get("GEMINI_API_KEY")
|
@@ -42,53 +44,23 @@ def generate(text, file_name, background_file=None, style_file=None, model="gemi
|
|
42 |
client = genai.Client(api_key=effective_api_key)
|
43 |
logger.debug("Gemini 클라이언트 초기화 완료.")
|
44 |
|
45 |
-
#
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
if background_file is not None:
|
51 |
-
bg_file = client.files.upload(file=background_file)
|
52 |
-
uploaded_files.append(bg_file)
|
53 |
-
logger.debug(f"배경 파일 업로드 완료. URI: {bg_file.uri}, MIME 타입: {bg_file.mime_type}")
|
54 |
-
if style_file is not None:
|
55 |
-
style_uploaded = client.files.upload(file=style_file)
|
56 |
-
uploaded_files.append(style_uploaded)
|
57 |
-
logger.debug(f"스타일 파일 업로드 완료. URI: {style_uploaded.uri}, MIME 타입: {style_uploaded.mime_type}")
|
58 |
-
|
59 |
-
# 컨텐츠 객체 생성: 업로드한 이미지들을 순서대로(원본, 배경, 스타일) 추가하고 마지막에 텍스트 프롬프트 추가
|
60 |
-
parts = []
|
61 |
-
# 원본 이미지 파트
|
62 |
-
parts.append(
|
63 |
-
types.Part.from_uri(
|
64 |
-
file_uri=uploaded_files[0].uri,
|
65 |
-
mime_type=uploaded_files[0].mime_type,
|
66 |
-
)
|
67 |
-
)
|
68 |
-
# 배경 이미지 파트 (존재 시)
|
69 |
-
if background_file is not None:
|
70 |
-
parts.append(
|
71 |
-
types.Part.from_uri(
|
72 |
-
file_uri=uploaded_files[1].uri,
|
73 |
-
mime_type=uploaded_files[1].mime_type,
|
74 |
-
)
|
75 |
-
)
|
76 |
-
# 스타일 이미지 파트 (존재 시)
|
77 |
-
if style_file is not None:
|
78 |
-
style_index = 2 if background_file is not None else 1
|
79 |
-
parts.append(
|
80 |
-
types.Part.from_uri(
|
81 |
-
file_uri=uploaded_files[style_index].uri,
|
82 |
-
mime_type=uploaded_files[style_index].mime_type,
|
83 |
-
)
|
84 |
-
)
|
85 |
-
# 마지막으로 텍스트 파트 추가
|
86 |
-
parts.append(types.Part.from_text(text=text))
|
87 |
|
|
|
88 |
contents = [
|
89 |
types.Content(
|
90 |
role="user",
|
91 |
-
parts=
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
),
|
93 |
]
|
94 |
logger.debug(f"컨텐츠 객체 생성 완료: {contents}")
|
@@ -132,47 +104,28 @@ def generate(text, file_name, background_file=None, style_file=None, model="gemi
|
|
132 |
|
133 |
logger.debug(f"Raw chunk: {chunk}")
|
134 |
|
135 |
-
del
|
136 |
logger.debug("업로드된 파일 정보 삭제 완료.")
|
137 |
return temp_path
|
138 |
|
139 |
except Exception as e:
|
140 |
logger.exception("이미지 생성 중 오류 발생:")
|
141 |
-
return None
|
|
|
142 |
|
143 |
-
def process_image_and_prompt(
|
144 |
logger.debug(f"process_image_and_prompt 함수 시작 - 프롬프트: '{prompt}'")
|
145 |
try:
|
146 |
-
# 원본 이미지 저장
|
147 |
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
|
148 |
-
|
149 |
-
|
150 |
-
logger.debug(f"
|
151 |
-
|
152 |
-
# 배경 이미지 저장 (제공된 경우)
|
153 |
-
background_path = None
|
154 |
-
if background_pil is not None:
|
155 |
-
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_bg:
|
156 |
-
background_path = tmp_bg.name
|
157 |
-
background_pil.save(background_path)
|
158 |
-
logger.debug(f"배경 이미지 저장 완료: {background_path}")
|
159 |
-
|
160 |
-
# 스타일 이미지 저장 (제공된 경우)
|
161 |
-
style_path = None
|
162 |
-
if style_pil is not None:
|
163 |
-
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_style:
|
164 |
-
style_path = tmp_style.name
|
165 |
-
style_pil.save(style_path)
|
166 |
-
logger.debug(f"스타일 이미지 저장 완료: {style_path}")
|
167 |
|
|
|
168 |
input_text = prompt
|
169 |
model = "gemini-2.0-flash-exp-image-generation"
|
170 |
|
171 |
-
gemma_edited_image_path = generate(text=input_text,
|
172 |
-
file_name=original_path,
|
173 |
-
background_file=background_path,
|
174 |
-
style_file=style_path,
|
175 |
-
model=model)
|
176 |
|
177 |
if gemma_edited_image_path:
|
178 |
logger.debug(f"이미지 생성 완료. 경로: {gemma_edited_image_path}")
|
@@ -182,10 +135,12 @@ def process_image_and_prompt(original_pil, prompt, background_pil=None, style_pi
|
|
182 |
return [result_img]
|
183 |
else:
|
184 |
logger.error("generate 함수에서 None 반환됨.")
|
185 |
-
return []
|
|
|
186 |
except Exception as e:
|
187 |
logger.exception("process_image_and_prompt 함수에서 오류 발생:")
|
188 |
-
return []
|
|
|
189 |
|
190 |
# --- Gradio 인터페이스 구성 ---
|
191 |
with gr.Blocks() as demo:
|
@@ -202,13 +157,11 @@ with gr.Blocks() as demo:
|
|
202 |
</div>
|
203 |
"""
|
204 |
)
|
205 |
-
gr.Markdown("
|
206 |
|
207 |
with gr.Row():
|
208 |
with gr.Column():
|
209 |
-
|
210 |
-
background_input = gr.Image(type="pil", label="배경 이미지", image_mode="RGBA")
|
211 |
-
style_input = gr.Image(type="pil", label="스타일 이미지", image_mode="RGBA")
|
212 |
prompt_input = gr.Textbox(
|
213 |
lines=2,
|
214 |
placeholder="편집할 내용을 입력하세요...",
|
@@ -220,18 +173,17 @@ with gr.Blocks() as demo:
|
|
220 |
|
221 |
submit_btn.click(
|
222 |
fn=process_image_and_prompt,
|
223 |
-
inputs=[
|
224 |
outputs=output_gallery,
|
225 |
)
|
226 |
|
227 |
# --- 테스트 코드 ---
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
dummy_prompt = "이미지 편집: 원본은 빨강, 배경은 초록, 스타일은 파랑"
|
232 |
|
233 |
logger.info("process_image_and_prompt 함수를 직접 호출합니다...")
|
234 |
-
result = process_image_and_prompt(
|
235 |
|
236 |
if result:
|
237 |
logger.info(f"직접 호출 성공. 결과: {result}")
|
|
|
9 |
import mimetypes
|
10 |
import logging
|
11 |
|
12 |
+
from google import genai
|
13 |
+
from google.genai import types
|
|
|
14 |
|
15 |
# .env 파일에 저장된 환경변수 로드 (python-dotenv 설치 필요: pip install python-dotenv)
|
16 |
from dotenv import load_dotenv
|
|
|
21 |
format='%(asctime)s - %(levelname)s - %(message)s')
|
22 |
logger = logging.getLogger(__name__)
|
23 |
|
24 |
+
|
25 |
def save_binary_file(file_name, data):
|
26 |
logger.debug(f"파일에 이진 데이터 저장 중: {file_name}")
|
27 |
with open(file_name, "wb") as f:
|
28 |
f.write(data)
|
29 |
logger.debug(f"파일 저장 완료: {file_name}")
|
30 |
|
31 |
+
|
32 |
+
def generate(text, file_name, model="gemini-2.0-flash-exp-image-generation"):
|
33 |
logger.debug(f"generate 함수 시작 - 텍스트: '{text}', 파일명: '{file_name}', 모델: '{model}'")
|
34 |
+
|
35 |
try:
|
36 |
# API 키는 환경변수에서 불러옴
|
37 |
effective_api_key = os.environ.get("GEMINI_API_KEY")
|
|
|
44 |
client = genai.Client(api_key=effective_api_key)
|
45 |
logger.debug("Gemini 클라이언트 초기화 완료.")
|
46 |
|
47 |
+
# 파일 업로드
|
48 |
+
files = [
|
49 |
+
client.files.upload(file=file_name),
|
50 |
+
]
|
51 |
+
logger.debug(f"파일 업로드 완료. URI: {files[0].uri}, MIME 타입: {files[0].mime_type}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
|
53 |
+
# 컨텐츠 객체 생성: 파일 URI와 텍스트 프롬프트를 함께 포함
|
54 |
contents = [
|
55 |
types.Content(
|
56 |
role="user",
|
57 |
+
parts=[
|
58 |
+
types.Part.from_uri(
|
59 |
+
file_uri=files[0].uri,
|
60 |
+
mime_type=files[0].mime_type,
|
61 |
+
),
|
62 |
+
types.Part.from_text(text=text),
|
63 |
+
],
|
64 |
),
|
65 |
]
|
66 |
logger.debug(f"컨텐츠 객체 생성 완료: {contents}")
|
|
|
104 |
|
105 |
logger.debug(f"Raw chunk: {chunk}")
|
106 |
|
107 |
+
del files
|
108 |
logger.debug("업로드된 파일 정보 삭제 완료.")
|
109 |
return temp_path
|
110 |
|
111 |
except Exception as e:
|
112 |
logger.exception("이미지 생성 중 오류 발생:")
|
113 |
+
return None # 오류 발생 시 None 반환
|
114 |
+
|
115 |
|
116 |
+
def process_image_and_prompt(composite_pil, prompt):
|
117 |
logger.debug(f"process_image_and_prompt 함수 시작 - 프롬프트: '{prompt}'")
|
118 |
try:
|
|
|
119 |
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
|
120 |
+
composite_path = tmp.name
|
121 |
+
composite_pil.save(composite_path)
|
122 |
+
logger.debug(f"합성 이미지 저장 완료: {composite_path}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
|
124 |
+
file_name = composite_path
|
125 |
input_text = prompt
|
126 |
model = "gemini-2.0-flash-exp-image-generation"
|
127 |
|
128 |
+
gemma_edited_image_path = generate(text=input_text, file_name=file_name, model=model)
|
|
|
|
|
|
|
|
|
129 |
|
130 |
if gemma_edited_image_path:
|
131 |
logger.debug(f"이미지 생성 완료. 경로: {gemma_edited_image_path}")
|
|
|
135 |
return [result_img]
|
136 |
else:
|
137 |
logger.error("generate 함수에서 None 반환됨.")
|
138 |
+
return [] # 오류 시 빈 리스트 반환
|
139 |
+
|
140 |
except Exception as e:
|
141 |
logger.exception("process_image_and_prompt 함수에서 오류 발생:")
|
142 |
+
return [] # 오류 시 빈 리스트 반환
|
143 |
+
|
144 |
|
145 |
# --- Gradio 인터페이스 구성 ---
|
146 |
with gr.Blocks() as demo:
|
|
|
157 |
</div>
|
158 |
"""
|
159 |
)
|
160 |
+
gr.Markdown("이미지를 업로드하고, 편집할 내용을 입력하세요.")
|
161 |
|
162 |
with gr.Row():
|
163 |
with gr.Column():
|
164 |
+
image_input = gr.Image(type="pil", label="이미지 업로드", image_mode="RGBA")
|
|
|
|
|
165 |
prompt_input = gr.Textbox(
|
166 |
lines=2,
|
167 |
placeholder="편집할 내용을 입력하세요...",
|
|
|
173 |
|
174 |
submit_btn.click(
|
175 |
fn=process_image_and_prompt,
|
176 |
+
inputs=[image_input, prompt_input],
|
177 |
outputs=output_gallery,
|
178 |
)
|
179 |
|
180 |
# --- 테스트 코드 ---
|
181 |
+
# 테스트용 더미 이미지 (실제 이미지로 대체 가능)
|
182 |
+
dummy_image = Image.new("RGBA", (100, 100), color="red")
|
183 |
+
dummy_prompt = "이미지를 파란색으로 변경해줘"
|
|
|
184 |
|
185 |
logger.info("process_image_and_prompt 함수를 직접 호출합니다...")
|
186 |
+
result = process_image_and_prompt(dummy_image, dummy_prompt)
|
187 |
|
188 |
if result:
|
189 |
logger.info(f"직접 호출 성공. 결과: {result}")
|