Spaces:
Runtime error
Runtime error
Delete app.py
Browse files
app.py
DELETED
@@ -1,650 +0,0 @@
|
|
1 |
-
import os
|
2 |
-
import gradio as gr
|
3 |
-
import random
|
4 |
-
import time
|
5 |
-
import logging
|
6 |
-
import google.generativeai as genai
|
7 |
-
|
8 |
-
import torch
|
9 |
-
import numpy as np
|
10 |
-
from diffusers import DiffusionPipeline
|
11 |
-
from transformers import pipeline as hf_pipeline
|
12 |
-
import re
|
13 |
-
|
14 |
-
##############################################################################
|
15 |
-
# 1) ZeroGPU Environment Setup + Device and Dtype Configuration
|
16 |
-
##############################################################################
|
17 |
-
try:
|
18 |
-
import zerogpu
|
19 |
-
zerogpu.init()
|
20 |
-
print("ZeroGPU initialized successfully")
|
21 |
-
device = "cuda" if torch.cuda.is_available() else "cpu"
|
22 |
-
except ImportError:
|
23 |
-
print("ZeroGPU package not installed, continuing without it")
|
24 |
-
if os.getenv("ZERO_GPU"):
|
25 |
-
print("ZeroGPU environment variable is set but zerogpu package is not installed.")
|
26 |
-
device = "cuda" if torch.cuda.is_available() else "cpu"
|
27 |
-
except Exception as e:
|
28 |
-
print(f"Error initializing ZeroGPU: {e}")
|
29 |
-
print("Continuing without ZeroGPU")
|
30 |
-
device = "cuda" if torch.cuda.is_available() else "cpu"
|
31 |
-
|
32 |
-
dtype = torch.bfloat16 if device == "cuda" else torch.float32
|
33 |
-
|
34 |
-
print(f"Using device: {device}, dtype: {dtype}")
|
35 |
-
|
36 |
-
##############################################################################
|
37 |
-
# 2) Load Models: Translation Model, Diffusion Pipeline
|
38 |
-
##############################################################################
|
39 |
-
try:
|
40 |
-
translator = hf_pipeline(
|
41 |
-
"translation",
|
42 |
-
model="Helsinki-NLP/opus-mt-ko-en",
|
43 |
-
device=0 if device == "cuda" else -1
|
44 |
-
)
|
45 |
-
|
46 |
-
pipe = DiffusionPipeline.from_pretrained(
|
47 |
-
"black-forest-labs/FLUX.1-schnell",
|
48 |
-
torch_dtype=dtype
|
49 |
-
).to(device)
|
50 |
-
|
51 |
-
print("Models loaded successfully")
|
52 |
-
except Exception as e:
|
53 |
-
print(f"Error loading models: {e}")
|
54 |
-
def dummy_translator(text):
|
55 |
-
return [{'translation_text': text}]
|
56 |
-
|
57 |
-
class DummyPipe:
|
58 |
-
def __call__(self, **kwargs):
|
59 |
-
from PIL import Image
|
60 |
-
import numpy as np
|
61 |
-
dummy_img = Image.fromarray(np.zeros((512, 512, 3), dtype=np.uint8))
|
62 |
-
class DummyResult:
|
63 |
-
def __init__(self, img):
|
64 |
-
self.images = [img]
|
65 |
-
return DummyResult(dummy_img)
|
66 |
-
|
67 |
-
translator = dummy_translator
|
68 |
-
pipe = DummyPipe()
|
69 |
-
|
70 |
-
MAX_SEED = np.iinfo(np.int32).max
|
71 |
-
MAX_IMAGE_SIZE = 2048
|
72 |
-
|
73 |
-
##############################################################################
|
74 |
-
# Korean detection and input text cleaning functions
|
75 |
-
##############################################################################
|
76 |
-
def contains_korean(text):
|
77 |
-
for char in text:
|
78 |
-
if ord('가') <= ord(char) <= ord('힣'):
|
79 |
-
return True
|
80 |
-
return False
|
81 |
-
|
82 |
-
def clean_input_text(text):
|
83 |
-
"""
|
84 |
-
Allows only Korean, English, numbers, whitespace and common punctuation marks.
|
85 |
-
Adjust allowed characters as needed.
|
86 |
-
"""
|
87 |
-
allowed = re.compile(r'[^ㄱ-ㅎ가-힣a-zA-Z0-9\s\.\,\!\?\-\:\;\'\"]')
|
88 |
-
cleaned_text = allowed.sub('', text)
|
89 |
-
return cleaned_text
|
90 |
-
|
91 |
-
def log_unexpected_characters(text):
|
92 |
-
allowed = re.compile(r'[ㄱ-ㅎ가-힣a-zA-Z0-9\s\.\,\!\?\-\:\;\'\"]')
|
93 |
-
unexpected_chars = [char for char in text if not allowed.match(char)]
|
94 |
-
if unexpected_chars:
|
95 |
-
print("Unexpected characters found:", set(unexpected_chars))
|
96 |
-
else:
|
97 |
-
print("No unexpected characters in the input text.")
|
98 |
-
|
99 |
-
##############################################################################
|
100 |
-
# Image Generation Function
|
101 |
-
##############################################################################
|
102 |
-
def generate_design_image(prompt, seed=42, randomize_seed=True, width=1024, height=1024, num_inference_steps=4):
|
103 |
-
original_prompt = prompt
|
104 |
-
translated = False
|
105 |
-
|
106 |
-
# Clean the input text
|
107 |
-
prompt = clean_input_text(prompt)
|
108 |
-
|
109 |
-
# Pre-process: if input is too long, trim to 1000 characters
|
110 |
-
if len(prompt) > 1000:
|
111 |
-
prompt = prompt[:1000]
|
112 |
-
|
113 |
-
if contains_korean(prompt):
|
114 |
-
# When calling translation, add max_length and truncation options to avoid length issues
|
115 |
-
translation = translator(prompt, max_length=400, truncation=True)
|
116 |
-
prompt = translation[0]['translation_text']
|
117 |
-
translated = True
|
118 |
-
|
119 |
-
if randomize_seed:
|
120 |
-
seed = random.randint(0, MAX_SEED)
|
121 |
-
|
122 |
-
generator = torch.Generator(device=device).manual_seed(seed)
|
123 |
-
|
124 |
-
image = pipe(
|
125 |
-
prompt=prompt,
|
126 |
-
width=width,
|
127 |
-
height=height,
|
128 |
-
num_inference_steps=num_inference_steps,
|
129 |
-
generator=generator,
|
130 |
-
guidance_scale=0.0
|
131 |
-
).images[0]
|
132 |
-
|
133 |
-
return image
|
134 |
-
|
135 |
-
##############################################################################
|
136 |
-
# Logging Setup
|
137 |
-
##############################################################################
|
138 |
-
logging.basicConfig(
|
139 |
-
level=logging.INFO,
|
140 |
-
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
141 |
-
handlers=[
|
142 |
-
logging.FileHandler("api_debug.log"),
|
143 |
-
logging.StreamHandler()
|
144 |
-
]
|
145 |
-
)
|
146 |
-
logger = logging.getLogger("idea_generator")
|
147 |
-
|
148 |
-
##############################################################################
|
149 |
-
# Gemini API Key
|
150 |
-
##############################################################################
|
151 |
-
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
|
152 |
-
genai.configure(api_key=GEMINI_API_KEY)
|
153 |
-
|
154 |
-
##############################################################################
|
155 |
-
# Optional Transformation Choice Function
|
156 |
-
##############################################################################
|
157 |
-
def choose_alternative(transformation):
|
158 |
-
if "/" not in transformation:
|
159 |
-
return transformation
|
160 |
-
parts = transformation.split("/")
|
161 |
-
if len(parts) != 2:
|
162 |
-
return random.choice([part.strip() for part in parts])
|
163 |
-
left = parts[0].strip()
|
164 |
-
right = parts[1].strip()
|
165 |
-
if " " in left:
|
166 |
-
tokens = left.split(" ", 1)
|
167 |
-
prefix = tokens[0]
|
168 |
-
if not right.startswith(prefix):
|
169 |
-
option1 = left
|
170 |
-
option2 = prefix + " " + right
|
171 |
-
else:
|
172 |
-
option1 = left
|
173 |
-
option2 = right
|
174 |
-
return random.choice([option1, option2])
|
175 |
-
else:
|
176 |
-
return random.choice([left, right])
|
177 |
-
|
178 |
-
##############################################################################
|
179 |
-
# Transformation Categories Dictionaries
|
180 |
-
##############################################################################
|
181 |
-
# Korean version
|
182 |
-
physical_transformation_categories = {
|
183 |
-
"센서 기능": [
|
184 |
-
"시각 센서/감지", "청각 센서/감지", "촉각 센서/감지", "미각 센서/감지", "후각 센서/감지",
|
185 |
-
"온도 센서/감지", "습도 센서/감지", "압력 센서/감지", "가속도 센서/감지", "회전 센서/감지",
|
186 |
-
"근접 센서/감지", "위치 센서/감지", "운동 센서/감지", "가스 센서/감지", "적외선 센서/감지",
|
187 |
-
"자외선 센서/감지", "방사선 센서/감지", "자기장 센서/감지", "전기장 센서/감지", "화학물질 센서/감지",
|
188 |
-
"생체신호 센서/감지", "진동 센서/감지", "소음 센서/감지", "빛 세기 센서/감지", "빛 파장 센서/감지",
|
189 |
-
"기울기 센서/감지", "pH 센서/감지", "전류 센서/감지", "전압 센서/감지", "이미지 센서/감지",
|
190 |
-
"거리 센서/감지", "깊이 센서/감지", "중력 센서/감지", "속도 센서/감지", "흐름 센서/감지",
|
191 |
-
"수위 센서/감지", "탁도 센서/감지", "염도 센서/감지", "금속 감지", "압전 센서/감지",
|
192 |
-
"광전 센서/감지", "열전대 센서/감지", "홀 효과 센서/감지", "초음파 센서/감지", "레이더 센서/감지",
|
193 |
-
"라이다 센서/감지", "터치 센서/감지", "제스처 센서/감지", "심박 센서/감지", "혈압 센서/감지"
|
194 |
-
],
|
195 |
-
"크기와 형태 변화": [
|
196 |
-
"부피 늘어남/줄어듦", "길이 늘어남/줄어듦", "너비 늘어남/줄어듦", "높이 늘어남/줄어듦",
|
197 |
-
"밀도 변화", "무게 증가/감소", "모양 변형", "상태 변화", "불균등 변형",
|
198 |
-
"복잡한 형태 변형", "비틀림/꼬임", "불균일한 확장/축소", "모서리 둥글게/날카롭게",
|
199 |
-
"깨짐/갈라짐", "여러 조각 나눠짐", "물 저항", "먼지 저항", "찌그러짐/복원",
|
200 |
-
"접힘/펼쳐짐", "압착/팽창", "늘어남/수축", "구겨짐/평평해짐", "뭉개짐/단단해짐",
|
201 |
-
"말림/펴짐", "꺾임/구부러짐"
|
202 |
-
],
|
203 |
-
"표면 및 외관 변화": [
|
204 |
-
"색상 변화", "질감 변화", "투명/불투명 변화", "반짝임/무광 변화",
|
205 |
-
"빛 반사 정도 변화", "무늬 변화", "각도에 따른 색상 변화", "빛에 따른 색상 변화",
|
206 |
-
"온도에 따른 색상 변화", "홀로그램 효과", "표면 각도별 빛 반사", "표면 모양 변형",
|
207 |
-
"초미세 표면 구조 변화", "자가 세정 효과", "얼룩/패턴 생성", "흐림/선명함 변화",
|
208 |
-
"광택/윤기 변화", "색조/채도 변화", "발광/형광", "빛 산란 효과",
|
209 |
-
"빛 흡수 변화", "반투명 효과", "그림자 효과 변화", "자외선 반응 변화",
|
210 |
-
"야광 효과"
|
211 |
-
],
|
212 |
-
"물질의 상태 변화": [
|
213 |
-
"고체/액체/기체 전환", "결정화/용해", "산화/부식", "딱딱해짐/부드러워짐",
|
214 |
-
"특수 상태 전환", "무정형/결정형 전환", "성분 분리", "미세 입자 형성/분해",
|
215 |
-
"젤 형성/풀어짐", "준안정 상태 변화", "분자 자가 정렬/분해", "상태변화 지연 현상",
|
216 |
-
"녹음", "굳음", "증발/응축", "승화/증착", "침전/부유", "분산/응집",
|
217 |
-
"건조/습윤", "팽윤/수축", "동결/해동", "풍화/침식", "충전/방전",
|
218 |
-
"결합/분리", "발효/부패"
|
219 |
-
],
|
220 |
-
"움직임 특성 변화": [
|
221 |
-
"가속/감속", "일정 속도 유지", "진동/진동 감소", "부딪힘/튕김",
|
222 |
-
"회전 속도 증가/감소", "회전 방향 변화", "불규칙 움직임", "멈췄다 미끄러지는 현상",
|
223 |
-
"공진/반공진", "유체 속 저항/양�� 변화", "움직임 저항 변화", "복합 진동 움직임",
|
224 |
-
"특수 유체 속 움직임", "회전-이동 연계 움직임", "관성 정지", "충격 흡수",
|
225 |
-
"충격 전달", "운동량 보존", "마찰력 변화", "관성 탈출", "불안정 균형",
|
226 |
-
"동적 안정성", "흔들림 감쇠", "경로 예측성", "회피 움직임"
|
227 |
-
],
|
228 |
-
"구조적 변화": [
|
229 |
-
"부품 추가/제거", "조립/분해", "접기/펴기", "변형/원상복구", "최적 구조 변화",
|
230 |
-
"자가 재배열", "자연 패턴 형성/소멸", "규칙적 패턴 변화", "모듈식 변형",
|
231 |
-
"복잡성 증가 구조", "원래 모양 기억 효과", "시간에 따른 형태 변화", "부분 제거",
|
232 |
-
"부분 교체", "결합", "분리", "분할/통합", "중첩/겹침", "내부 구조 변화",
|
233 |
-
"외부 구조 변화", "중심축 이동", "균형점 변화", "계층 구조 변화", "지지 구조 변화",
|
234 |
-
"응력 분산 구조", "충격 흡수 구조", "그리드/매트릭스 구조 변화", "상호 연결성 변화"
|
235 |
-
],
|
236 |
-
"공간 이동": [
|
237 |
-
"앞/뒤 이동", "좌/우 이동", "위/아래 이동", "세로축 회전(고개 끄덕임)",
|
238 |
-
"가로축 회전(고개 젓기)", "길이축 회전(옆으로 기울임)", "원 운동", "나선형 이동",
|
239 |
-
"관성에 의한 미끄러짐", "회전축 변화", "불규칙 회전", "흔들림 운동", "포물선 이동",
|
240 |
-
"무중력 부유", "수면 위 부유", "점프/도약", "슬라이딩", "롤링", "자유 낙하",
|
241 |
-
"왕복 운동", "탄성 튕김", "관통", "회피 움직임", "지그재그 이동", "스윙 운동"
|
242 |
-
],
|
243 |
-
"시간 관련 변화": [
|
244 |
-
"노화/풍화", "마모/부식", "색 바램/변색", "손상/회복", "수명 주기 변화",
|
245 |
-
"사용자 상호작용에 따른 적응", "학습 기반 형태 최적화", "시간에 따른 물성 변화",
|
246 |
-
"집단 기억 효과", "문화적 의미 변화", "지연 반응", "이전 상태 의존 변화",
|
247 |
-
"점진적 시간 변화", "진화적 변화", "주기적 재생", "계절 변화 적응",
|
248 |
-
"생체리듬 변화", "생애 주기 단계", "성장/퇴화", "자기 복구/재생",
|
249 |
-
"자연 순환 적응", "지속성/일시성", "기억 효과", "지연된 작용", "누적 효과"
|
250 |
-
],
|
251 |
-
"빛과 시각 효과": [
|
252 |
-
"발광/소등", "빛 투과/차단", "빛 산란/집중", "색상 스펙트럼 변화", "빛 회절",
|
253 |
-
"빛 간섭", "홀로그램 생성", "레이저 효과", "빛 편광", "형광/인광",
|
254 |
-
"자외선/적외선 발광", "광학적 착시", "빛 굴절", "그림자 생성/제거",
|
255 |
-
"색수차 효과", "무지개 효과", "글로우 효과", "플래시 효과", "조명 패턴",
|
256 |
-
"빔 효과", "광 필터 효과", "빛의 방향성 변화", "투영 효과", "빛 감지/반응",
|
257 |
-
"광도 변화"
|
258 |
-
],
|
259 |
-
"소리와 진동 효과": [
|
260 |
-
"소리 발생/소멸", "소리 높낮이 변화", "소리 크기 변화", "음색 변화",
|
261 |
-
"공명/반공명", "음향 진동", "초음파/저음파 발생", "음향 집중/분산",
|
262 |
-
"음향 반사/흡수", "음향 도플러 효과", "음파 간섭", "음향 공진",
|
263 |
-
"진동 패턴 변화", "타악 효과", "음향 피드백", "음향 차폐/증폭",
|
264 |
-
"소리 지향성", "음향 왜곡", "비트 생성", "하모닉스 생성", "주파수 변조",
|
265 |
-
"음향 충격파", "음향 필터링"
|
266 |
-
]
|
267 |
-
}
|
268 |
-
|
269 |
-
##############################################################################
|
270 |
-
# Gemini API Call Function (Language Independent)
|
271 |
-
##############################################################################
|
272 |
-
def query_gemini_api(prompt):
|
273 |
-
try:
|
274 |
-
model = genai.GenerativeModel('gemini-2.0-flash-thinking-exp-01-21')
|
275 |
-
response = model.generate_content(prompt)
|
276 |
-
try:
|
277 |
-
if hasattr(response, 'text'):
|
278 |
-
return response.text
|
279 |
-
if hasattr(response, 'candidates') and response.candidates:
|
280 |
-
candidate = response.candidates[0]
|
281 |
-
if hasattr(candidate, 'content'):
|
282 |
-
content = candidate.content
|
283 |
-
if hasattr(content, 'parts') and content.parts:
|
284 |
-
if len(content.parts) > 0:
|
285 |
-
return content.parts[0].text
|
286 |
-
if hasattr(response, 'parts') and response.parts:
|
287 |
-
if len(response.parts) > 0:
|
288 |
-
return response.parts[0].text
|
289 |
-
return "Unable to generate a response. API response structure is different than expected."
|
290 |
-
except Exception as inner_e:
|
291 |
-
logger.error(f"Error processing response: {inner_e}")
|
292 |
-
return f"An error occurred while processing the response: {str(inner_e)}"
|
293 |
-
except Exception as e:
|
294 |
-
logger.error(f"Error calling Gemini API: {e}")
|
295 |
-
if "API key not valid" in str(e):
|
296 |
-
return "API key is not valid. Please check your GEMINI_API_KEY environment variable."
|
297 |
-
return f"An error occurred while calling the API: {str(e)}"
|
298 |
-
|
299 |
-
##############################################################################
|
300 |
-
# Description Expansion Functions (LLM) - Korean and English Versions
|
301 |
-
##############################################################################
|
302 |
-
def enhance_with_llm(base_description, obj_name, category):
|
303 |
-
prompt = f"""
|
304 |
-
다음은 '{obj_name}'의 '{category}' 관련 간단한 설명입니다:
|
305 |
-
"{base_description}"
|
306 |
-
위 내용을 보다 구체화하여,
|
307 |
-
1) 창의적인 모델/컨셉/형상의 변화에 대한 이해,
|
308 |
-
2) 혁신 포인트와 기능성 등을 중심으로
|
309 |
-
3~4문장의 아이디어로 확장해 주세요.
|
310 |
-
"""
|
311 |
-
return query_gemini_api(prompt)
|
312 |
-
|
313 |
-
def enhance_with_llm_en(base_description, obj_name, category):
|
314 |
-
prompt = f"""
|
315 |
-
Below is a brief description related to '{category}' for '{obj_name}':
|
316 |
-
"{base_description}"
|
317 |
-
Please expand the above content into a more detailed explanation, focusing on:
|
318 |
-
1) Creative transformation of the model/concept/shape,
|
319 |
-
2) Innovative aspects and functionality,
|
320 |
-
in 3-4 sentences.
|
321 |
-
"""
|
322 |
-
return query_gemini_api(prompt)
|
323 |
-
|
324 |
-
##############################################################################
|
325 |
-
# Transformation Idea Generation Functions for Both Languages
|
326 |
-
##############################################################################
|
327 |
-
def generate_single_object_transformation_for_category_lang(obj, selected_category, categories_dict, lang="ko"):
|
328 |
-
transformations = categories_dict.get(selected_category)
|
329 |
-
if not transformations:
|
330 |
-
return {}
|
331 |
-
transformation = choose_alternative(random.choice(transformations))
|
332 |
-
if lang == "ko":
|
333 |
-
base_description = f"{obj}이(가) {transformation} 현상을 보인다"
|
334 |
-
else:
|
335 |
-
base_description = f"{obj} exhibits {transformation}"
|
336 |
-
return {selected_category: {"base": base_description, "enhanced": None}}
|
337 |
-
|
338 |
-
def generate_two_objects_interaction_for_category_lang(obj1, obj2, selected_category, categories_dict, lang="ko"):
|
339 |
-
transformations = categories_dict.get(selected_category)
|
340 |
-
if not transformations:
|
341 |
-
return {}
|
342 |
-
transformation = choose_alternative(random.choice(transformations))
|
343 |
-
if lang == "ko":
|
344 |
-
template = random.choice([
|
345 |
-
"{obj1}이(가) {obj2}에 결합하여 {change}가 발생했다",
|
346 |
-
"{obj1}과(와) {obj2}이(가) 충돌하면서 {change}가 일어났다"
|
347 |
-
])
|
348 |
-
else:
|
349 |
-
template = random.choice([
|
350 |
-
"{obj1} combined with {obj2} resulted in {change}",
|
351 |
-
"A collision between {obj1} and {obj2} led to {change}"
|
352 |
-
])
|
353 |
-
base_description = template.format(obj1=obj1, obj2=obj2, change=transformation)
|
354 |
-
return {selected_category: {"base": base_description, "enhanced": None}}
|
355 |
-
|
356 |
-
def generate_three_objects_interaction_for_category_lang(obj1, obj2, obj3, selected_category, categories_dict, lang="ko"):
|
357 |
-
transformations = categories_dict.get(selected_category)
|
358 |
-
if not transformations:
|
359 |
-
return {}
|
360 |
-
transformation = choose_alternative(random.choice(transformations))
|
361 |
-
if lang == "ko":
|
362 |
-
template = random.choice([
|
363 |
-
"{obj1}, {obj2}, {obj3}이(가) 삼각형 구조로 결합하여 {change}가 발생했다",
|
364 |
-
"{obj1}이(가) {obj2}와(과) {obj3} 사이에서 매개체 역할을 하며 {change}를 촉진했다"
|
365 |
-
])
|
366 |
-
else:
|
367 |
-
template = random.choice([
|
368 |
-
"{obj1}, {obj2}, and {obj3} formed a triangular structure resulting in {change}",
|
369 |
-
"{obj1} acted as an intermediary between {obj2} and {obj3}, facilitating {change}"
|
370 |
-
])
|
371 |
-
base_description = template.format(obj1=obj1, obj2=obj2, obj3=obj3, change=transformation)
|
372 |
-
return {selected_category: {"base": base_description, "enhanced": None}}
|
373 |
-
|
374 |
-
def enhance_descriptions_lang(results, objects, lang="ko"):
|
375 |
-
obj_name = " 및 ".join([obj for obj in objects if obj]) if lang=="ko" else " and ".join([obj for obj in objects if obj])
|
376 |
-
for category, result in results.items():
|
377 |
-
if lang == "ko":
|
378 |
-
result["enhanced"] = enhance_with_llm(result["base"], obj_name, category)
|
379 |
-
else:
|
380 |
-
result["enhanced"] = enhance_with_llm_en(result["base"], obj_name, category)
|
381 |
-
return results
|
382 |
-
|
383 |
-
def generate_transformations_lang(text1, text2, text3, selected_category, categories_dict, lang="ko"):
|
384 |
-
if text2 and text3:
|
385 |
-
results = generate_three_objects_interaction_for_category_lang(text1, text2, text3, selected_category, categories_dict, lang)
|
386 |
-
objects = [text1, text2, text3]
|
387 |
-
elif text2:
|
388 |
-
results = generate_two_objects_interaction_for_category_lang(text1, text2, selected_category, categories_dict, lang)
|
389 |
-
objects = [text1, text2]
|
390 |
-
else:
|
391 |
-
results = generate_single_object_transformation_for_category_lang(text1, selected_category, categories_dict, lang)
|
392 |
-
objects = [text1]
|
393 |
-
return enhance_descriptions_lang(results, objects, lang)
|
394 |
-
|
395 |
-
def format_results_lang(results, lang="ko"):
|
396 |
-
formatted = ""
|
397 |
-
if lang == "ko":
|
398 |
-
for category, result in results.items():
|
399 |
-
formatted += f"## {category}\n**기본 아이디어**: {result['base']}\n\n**확장된 아이디어**: {result['enhanced']}\n\n---\n\n"
|
400 |
-
else:
|
401 |
-
for category, result in results.items():
|
402 |
-
formatted += f"## {category}\n**Base Idea**: {result['base']}\n\n**Expanded Idea**: {result['enhanced']}\n\n---\n\n"
|
403 |
-
return formatted
|
404 |
-
|
405 |
-
def process_inputs_lang(text1, text2, text3, selected_category, categories_dict, lang="ko", progress=gr.Progress()):
|
406 |
-
text1 = text1.strip() if text1 else None
|
407 |
-
text2 = text2.strip() if text2 else None
|
408 |
-
text3 = text3.strip() if text3 else None
|
409 |
-
|
410 |
-
if not text1:
|
411 |
-
return "오류: 최소 하나의 키워드를 입력해주세요." if lang=="ko" else "Error: Please enter at least one keyword."
|
412 |
-
|
413 |
-
if lang == "ko":
|
414 |
-
progress(0.05, desc="아이디어 생성 준비 중...")
|
415 |
-
time.sleep(0.3)
|
416 |
-
progress(0.1, desc="창의적인 아이디어 생성 시작...")
|
417 |
-
else:
|
418 |
-
progress(0.05, desc="Preparing idea generation...")
|
419 |
-
time.sleep(0.3)
|
420 |
-
progress(0.1, desc="Generating creative idea...")
|
421 |
-
|
422 |
-
results = generate_transformations_lang(text1, text2, text3, selected_category, categories_dict, lang)
|
423 |
-
|
424 |
-
if lang == "ko":
|
425 |
-
progress(0.8, desc="결과 포맷팅 중...")
|
426 |
-
formatted = format_results_lang(results, lang)
|
427 |
-
progress(1.0, desc="완료!")
|
428 |
-
else:
|
429 |
-
progress(0.8, desc="Formatting results...")
|
430 |
-
formatted = format_results_lang(results, lang)
|
431 |
-
progress(1.0, desc="Done!")
|
432 |
-
return formatted
|
433 |
-
|
434 |
-
def process_all_lang(text1, text2, text3, selected_category, categories_dict, lang="ko", progress=gr.Progress()):
|
435 |
-
idea_result = process_inputs_lang(text1, text2, text3, selected_category, categories_dict, lang, progress)
|
436 |
-
image_result = generate_design_image(
|
437 |
-
idea_result,
|
438 |
-
seed=42,
|
439 |
-
randomize_seed=True,
|
440 |
-
width=1024,
|
441 |
-
height=1024,
|
442 |
-
num_inference_steps=4
|
443 |
-
)
|
444 |
-
return idea_result, image_result
|
445 |
-
|
446 |
-
##############################################################################
|
447 |
-
# Warning Message Function for API Key (Language Specific)
|
448 |
-
##############################################################################
|
449 |
-
def get_warning_message_lang(lang="ko"):
|
450 |
-
if not GEMINI_API_KEY:
|
451 |
-
return "⚠️ 환경 변수 GEMINI_API_KEY가 설정되지 않았습니다. Gemini API 키를 설정하세요." if lang=="ko" else "⚠️ The GEMINI_API_KEY environment variable is not set. Please set your Gemini API key."
|
452 |
-
return ""
|
453 |
-
|
454 |
-
##############################################################################
|
455 |
-
# Helper function for caching examples in the English tab
|
456 |
-
##############################################################################
|
457 |
-
def process_all_lang_example(text1, text2, text3, selected_category):
|
458 |
-
# 고정된 state값(physical_transformation_categories_en, "en")으로 호출
|
459 |
-
return process_all_lang(text1, text2, text3, selected_category, physical_transformation_categories_en, "en")
|
460 |
-
|
461 |
-
##############################################################################
|
462 |
-
# Gradio UI with Two Tabs: English (Main Home) and Korean
|
463 |
-
##############################################################################
|
464 |
-
with gr.Blocks(
|
465 |
-
title="Idea Transformer",
|
466 |
-
theme=gr.themes.Soft(primary_hue="teal", secondary_hue="slate", neutral_hue="neutral")
|
467 |
-
) as demo:
|
468 |
-
|
469 |
-
gr.HTML("""
|
470 |
-
<style>
|
471 |
-
body {
|
472 |
-
background: linear-gradient(135deg, #e0eafc, #cfdef3);
|
473 |
-
font-family: 'Arial', sans-serif;
|
474 |
-
}
|
475 |
-
.gradio-container {
|
476 |
-
padding: 20px;
|
477 |
-
}
|
478 |
-
h1, h2 {
|
479 |
-
text-align: center;
|
480 |
-
}
|
481 |
-
h1 {
|
482 |
-
color: #333;
|
483 |
-
}
|
484 |
-
h2 {
|
485 |
-
color: #555;
|
486 |
-
}
|
487 |
-
.output {
|
488 |
-
background-color: #ffffff;
|
489 |
-
padding: 15px;
|
490 |
-
border-radius: 8px;
|
491 |
-
}
|
492 |
-
.gr-button {
|
493 |
-
background-color: #4CAF50;
|
494 |
-
color: white;
|
495 |
-
border: none;
|
496 |
-
border-radius: 4px;
|
497 |
-
padding: 8px 16px;
|
498 |
-
}
|
499 |
-
.progress-message {
|
500 |
-
color: #2196F3;
|
501 |
-
font-weight: bold;
|
502 |
-
margin-top: 10px;
|
503 |
-
}
|
504 |
-
</style>
|
505 |
-
""")
|
506 |
-
|
507 |
-
with gr.Tabs():
|
508 |
-
with gr.Tab(label="English"):
|
509 |
-
gr.Markdown("# 🚀 Idea Transformer")
|
510 |
-
gr.Markdown("Based on up to **three keywords** and a **selected category**, this tool generates a creative transformation idea and a design image using the expanded idea as a prompt.")
|
511 |
-
|
512 |
-
warning_en = gr.Markdown(get_warning_message_lang("en"))
|
513 |
-
|
514 |
-
with gr.Row():
|
515 |
-
with gr.Column(scale=1):
|
516 |
-
text_input1_en = gr.Textbox(label="Keyword 1 (required)", placeholder="e.g., Smartphone")
|
517 |
-
text_input2_en = gr.Textbox(label="Keyword 2 (optional)", placeholder="e.g., Artificial Intelligence")
|
518 |
-
text_input3_en = gr.Textbox(label="Keyword 3 (optional)", placeholder="e.g., Healthcare")
|
519 |
-
category_radio_en = gr.Radio(
|
520 |
-
label="Select Category",
|
521 |
-
choices=list(physical_transformation_categories_en.keys()),
|
522 |
-
value=list(physical_transformation_categories_en.keys())[0],
|
523 |
-
info="Select a category."
|
524 |
-
)
|
525 |
-
status_msg_en = gr.Markdown("💡 Click the 'Generate Idea' button to create an idea and design image based on the selected category.")
|
526 |
-
|
527 |
-
processing_indicator_en = gr.HTML("""
|
528 |
-
<div style="display: flex; justify-content: center; align-items: center; margin: 10px 0;">
|
529 |
-
<div style="border: 5px solid #f3f3f3; border-top: 5px solid #3498db; border-radius: 50%; width: 30px; height: 30px; animation: spin 2s linear infinite;"></div>
|
530 |
-
<p style="margin-left: 10px; font-weight: bold; color: #3498db;">Processing...</p>
|
531 |
-
</div>
|
532 |
-
<style>
|
533 |
-
@keyframes spin {
|
534 |
-
0% { transform: rotate(0deg); }
|
535 |
-
100% { transform: rotate(360deg); }
|
536 |
-
}
|
537 |
-
</style>
|
538 |
-
""", visible=False)
|
539 |
-
|
540 |
-
submit_button_en = gr.Button("Generate Idea", variant="primary")
|
541 |
-
|
542 |
-
with gr.Column(scale=2):
|
543 |
-
idea_output_en = gr.Markdown(label="Idea Output")
|
544 |
-
generated_image_en = gr.Image(label="Generated Design Image", type="pil")
|
545 |
-
|
546 |
-
gr.Examples(
|
547 |
-
examples=[
|
548 |
-
["Smartphone", "", "", "Sensor Functions"],
|
549 |
-
["Car", "", "", "Size and Shape Change"],
|
550 |
-
["Car", "Artificial Intelligence", "", "Surface and Appearance Change"],
|
551 |
-
["Drone", "Artificial Intelligence", "", "Material State Change"],
|
552 |
-
["Sneakers", "Wearable", "Health", "Structural Change"],
|
553 |
-
],
|
554 |
-
inputs=[text_input1_en, text_input2_en, text_input3_en, category_radio_en],
|
555 |
-
fn=process_all_lang_example,
|
556 |
-
outputs=[idea_output_en, generated_image_en],
|
557 |
-
cache_examples=True
|
558 |
-
)
|
559 |
-
|
560 |
-
def show_processing_indicator_en():
|
561 |
-
return gr.update(visible=True)
|
562 |
-
|
563 |
-
def hide_processing_indicator_en():
|
564 |
-
return gr.update(visible=False)
|
565 |
-
|
566 |
-
submit_button_en.click(
|
567 |
-
fn=show_processing_indicator_en,
|
568 |
-
inputs=None,
|
569 |
-
outputs=processing_indicator_en
|
570 |
-
).then(
|
571 |
-
fn=process_all_lang,
|
572 |
-
inputs=[text_input1_en, text_input2_en, text_input3_en, category_radio_en, gr.State(physical_transformation_categories_en), gr.State("en")],
|
573 |
-
outputs=[idea_output_en, generated_image_en]
|
574 |
-
).then(
|
575 |
-
fn=hide_processing_indicator_en,
|
576 |
-
inputs=None,
|
577 |
-
outputs=processing_indicator_en
|
578 |
-
)
|
579 |
-
|
580 |
-
with gr.Tab(label="한국어"):
|
581 |
-
gr.Markdown("# 🚀 아이디어 트랜스포머")
|
582 |
-
gr.Markdown("입력한 **키워드**(최대 3개)와 **카테고리**를 바탕으로, 창의적인 모델/컨셉/형상 변화 아이디어를 생성하고, 해당 확장 아이디어를 프롬프트로 하여 디자인 이미지를 생성합니다.")
|
583 |
-
|
584 |
-
warning_ko = gr.Markdown(get_warning_message_lang("ko"))
|
585 |
-
|
586 |
-
with gr.Row():
|
587 |
-
with gr.Column(scale=1):
|
588 |
-
text_input1_ko = gr.Textbox(label="키워드 1 (필수)", placeholder="예: 스마트폰")
|
589 |
-
text_input2_ko = gr.Textbox(label="키워드 2 (선택)", placeholder="예: 인공지능")
|
590 |
-
text_input3_ko = gr.Textbox(label="키워드 3 (선택)", placeholder="예: 헬스케어")
|
591 |
-
category_radio_ko = gr.Radio(
|
592 |
-
label="카테고리 선택",
|
593 |
-
choices=list(physical_transformation_categories.keys()),
|
594 |
-
value=list(physical_transformation_categories.keys())[0],
|
595 |
-
info="출력할 카테고리를 선택하세요."
|
596 |
-
)
|
597 |
-
status_msg_ko = gr.Markdown("💡 '아이디어 생성하기' 버튼을 클릭하면 선택한 카테고리에 해당하는 아이디어와 디자인 이미지가 생성됩니다.")
|
598 |
-
|
599 |
-
processing_indicator_ko = gr.HTML("""
|
600 |
-
<div style="display: flex; justify-content: center; align-items: center; margin: 10px 0;">
|
601 |
-
<div style="border: 5px solid #f3f3f3; border-top: 5px solid #3498db; border-radius: 50%; width: 30px; height: 30px; animation: spin 2s linear infinite;"></div>
|
602 |
-
<p style="margin-left: 10px; font-weight: bold; color: #3498db;">처리 중입니다...</p>
|
603 |
-
</div>
|
604 |
-
<style>
|
605 |
-
@keyframes spin {
|
606 |
-
0% { transform: rotate(0deg); }
|
607 |
-
100% { transform: rotate(360deg); }
|
608 |
-
}
|
609 |
-
</style>
|
610 |
-
""", visible=False)
|
611 |
-
|
612 |
-
submit_button_ko = gr.Button("아이디어 생성하기", variant="primary")
|
613 |
-
|
614 |
-
with gr.Column(scale=2):
|
615 |
-
idea_output_ko = gr.Markdown(label="아이디어 결과")
|
616 |
-
generated_image_ko = gr.Image(label="생성된 디자인 이미지", type="pil")
|
617 |
-
|
618 |
-
gr.Examples(
|
619 |
-
examples=[
|
620 |
-
["스마트폰", "", "", "센서 기능"],
|
621 |
-
["자동차", "", "", "크기와 형태 변화"],
|
622 |
-
["자동차", "인공지능", "", "표면 및 외관 변화"],
|
623 |
-
["드론", "인공지능", "", "물질의 상태 변화"],
|
624 |
-
["운동화", "웨어러블", "건강", "구조적 변화"],
|
625 |
-
],
|
626 |
-
inputs=[text_input1_ko, text_input2_ko, text_input3_ko, category_radio_ko]
|
627 |
-
)
|
628 |
-
|
629 |
-
def show_processing_indicator_ko():
|
630 |
-
return gr.update(visible=True)
|
631 |
-
|
632 |
-
def hide_processing_indicator_ko():
|
633 |
-
return gr.update(visible=False)
|
634 |
-
|
635 |
-
submit_button_ko.click(
|
636 |
-
fn=show_processing_indicator_ko,
|
637 |
-
inputs=None,
|
638 |
-
outputs=processing_indicator_ko
|
639 |
-
).then(
|
640 |
-
fn=process_all_lang,
|
641 |
-
inputs=[text_input1_ko, text_input2_ko, text_input3_ko, category_radio_ko, gr.State(physical_transformation_categories), gr.State("ko")],
|
642 |
-
outputs=[idea_output_ko, generated_image_ko]
|
643 |
-
).then(
|
644 |
-
fn=hide_processing_indicator_ko,
|
645 |
-
inputs=None,
|
646 |
-
outputs=processing_indicator_ko
|
647 |
-
)
|
648 |
-
|
649 |
-
if __name__ == "__main__":
|
650 |
-
demo.launch(debug=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|