Delete app-BACKUP.py
Browse files- app-BACKUP.py +0 -378
app-BACKUP.py
DELETED
@@ -1,378 +0,0 @@
|
|
1 |
-
import gradio as gr
|
2 |
-
import replicate
|
3 |
-
import requests
|
4 |
-
import os
|
5 |
-
import json
|
6 |
-
import asyncio
|
7 |
-
import concurrent.futures
|
8 |
-
from io import BytesIO
|
9 |
-
from PIL import Image
|
10 |
-
from typing import List, Tuple, Dict
|
11 |
-
|
12 |
-
# 환경 변수에서 토큰 가져오기
|
13 |
-
REPLICATE_API_TOKEN = os.getenv("RAPI_TOKEN")
|
14 |
-
FRIENDLI_TOKEN = os.getenv("FRIENDLI_TOKEN")
|
15 |
-
|
16 |
-
# 스타일 정의
|
17 |
-
STYLE_TEMPLATES = {
|
18 |
-
"3D Style (Pixar-like)": {
|
19 |
-
"name": "3D Style",
|
20 |
-
"description": "Pixar-esque 3D render with volumetric lighting",
|
21 |
-
"example": "A fluffy ginger cat wearing a tiny spacesuit, floating amidst a vibrant nebula in a 3D render. The cat is gazing curiously at a swirling planet with rings made of candy. Background is filled with sparkling stars and colorful gas clouds, lit with soft, volumetric lighting. Style: Pixar-esque, highly detailed, playful. Colors: Deep blues, purples, oranges, and pinks. Rendered in Octane, 8k resolution."
|
22 |
-
},
|
23 |
-
"Elegant SWOT Quadrant": {
|
24 |
-
"name": "SWOT Analysis",
|
25 |
-
"description": "Flat-design 4-grid layout with minimal shadows",
|
26 |
-
"example": "Elegant SWOT quadrant: flat-design 4-grid on matte-white backdrop, thin pastel separators, top-left 'Strengths' panel shows glowing shield icon and subtle motif, top-right 'Weaknesses' panel with cracked chain icon in soft crimson, bottom-left 'Opportunities' panel with sunrise-over-horizon icon in optimistic teal, bottom-right 'Threats' panel with storm-cloud & lightning icon in deep indigo, minimal shadows, no text, no watermark, 16:9, 4K"
|
27 |
-
},
|
28 |
-
"Colorful Mind Map": {
|
29 |
-
"name": "Mind Map",
|
30 |
-
"description": "Hand-drawn educational style with vibrant colors",
|
31 |
-
"example": "A handrawn colorful mind map diagram: educational style, vibrant colors, clear hierarchy, golden ratio layout. Central concept with branching sub-topics, each branch with unique color coding, organic flowing connections, doodle-style icons for each node"
|
32 |
-
},
|
33 |
-
"Business Workflow": {
|
34 |
-
"name": "Business Process",
|
35 |
-
"description": "End-to-end business workflow with clear phases",
|
36 |
-
"example": "A detailed hand-drawn diagram illustrating an end-to-end business workflow with Market Analysis, Strategy Development, Product Design, Implementation, and Post-Launch Review phases. Clear directional arrows, iconography for each component, vibrant educational yet professional style"
|
37 |
-
},
|
38 |
-
"Industrial Design": {
|
39 |
-
"name": "Product Design",
|
40 |
-
"description": "Sleek industrial design concept sketch",
|
41 |
-
"example": "A sleek industrial design concept: Curved metallic body with minimal bezel, Touchscreen panel for settings, Modern matte black finish, Hand-drawn concept sketch style with annotations and dimension lines"
|
42 |
-
},
|
43 |
-
"3D Bubble Chart": {
|
44 |
-
"name": "Bubble Chart",
|
45 |
-
"description": "Clean 3D bubble visualization",
|
46 |
-
"example": "3-D bubble chart on clean white 2×2 grid, quadrant titles hidden, four translucent spheres in lime, azure, amber, magenta, gentle depth-of-field, modern consulting aesthetic, no text, 4K"
|
47 |
-
},
|
48 |
-
"Timeline Ribbon": {
|
49 |
-
"name": "Timeline",
|
50 |
-
"description": "Horizontal ribbon timeline with cyber-futuristic vibe",
|
51 |
-
"example": "Horizontal ribbon timeline, milestone pins glowing hot pink on charcoal, year markers as circles, faint motion streaks, cyber-futuristic vibe, no text, 1920×1080"
|
52 |
-
},
|
53 |
-
"Risk Heat Map": {
|
54 |
-
"name": "Heat Map",
|
55 |
-
"description": "Risk assessment heat map with gradient colors",
|
56 |
-
"example": "Risk Heat Map: square grid, smooth gradient from mint to fire-red, cells beveled, simple legend strip hidden, long subtle shadow, sterile white frame, no text"
|
57 |
-
},
|
58 |
-
"Pyramid/Funnel": {
|
59 |
-
"name": "Funnel Chart",
|
60 |
-
"description": "Multi-layer gradient funnel visualization",
|
61 |
-
"example": "Pyramid / Funnel: 5-layer gradient funnel narrowing downwards, top vivid sky-blue, mid mint-green, bottom sunset-orange, glass reflection, minimal background, no text"
|
62 |
-
},
|
63 |
-
"KPI Dashboard": {
|
64 |
-
"name": "Dashboard",
|
65 |
-
"description": "Dark-mode analytics dashboard with sci-fi interface",
|
66 |
-
"example": "KPI Dashboard: Dark-mode analytic dashboard, three glass speedometers glowing neon lime, two sparkline charts under, black glass background, sci-fi interface, no text, 4K"
|
67 |
-
},
|
68 |
-
"Value Chain": {
|
69 |
-
"name": "Value Chain",
|
70 |
-
"description": "Horizontal value chain with industrial look",
|
71 |
-
"example": "Value Chain Diagram: Horizontal value chain blocks, steel-blue gradient bars with subtle bevel, small gear icons above each segment, sleek industrial look, shadow cast, no text"
|
72 |
-
},
|
73 |
-
"Gantt Chart": {
|
74 |
-
"name": "Gantt Chart",
|
75 |
-
"description": "Hand-drawn style Gantt chart with playful colors",
|
76 |
-
"example": "Gantt Chart: Hand-drawn style Gantt bars sketched with vibrant markers on dotted grid notebook page, sticky-note color palette, playful yet organized, perspective tilt, no text"
|
77 |
-
},
|
78 |
-
"Mobile App Mockup": {
|
79 |
-
"name": "App Mockup",
|
80 |
-
"description": "Clean wireframe for mobile app design",
|
81 |
-
"example": "MOCKUP DESIGN: A clean hand-drawn style wireframe for a mobile app with Title screen, Login screen, Dashboard with sections, Bottom navigation bar, minimalist design with annotations"
|
82 |
-
},
|
83 |
-
"Flowchart": {
|
84 |
-
"name": "Flowchart",
|
85 |
-
"description": "Vibrant flowchart with minimalistic icons",
|
86 |
-
"example": "FLOWCHART DESIGN: A hand-drawn style flowchart, vibrant colors, minimalistic icons showing process flow from START to END with decision points, branches, and clear directional arrows"
|
87 |
-
}
|
88 |
-
}
|
89 |
-
|
90 |
-
def generate_prompt_with_llm(topic: str, style_example: str = None) -> str:
|
91 |
-
"""주제와 스타일 예제를 받아서 LLM을 사용해 이미지 프롬프트를 생성"""
|
92 |
-
url = "https://api.friendli.ai/dedicated/v1/chat/completions"
|
93 |
-
headers = {
|
94 |
-
"Authorization": f"Bearer {FRIENDLI_TOKEN}",
|
95 |
-
"Content-Type": "application/json"
|
96 |
-
}
|
97 |
-
|
98 |
-
# 강화된 시스템 프롬프트
|
99 |
-
system_prompt = """You are an expert image prompt engineer specializing in creating detailed, visually rich prompts for AI image generation.
|
100 |
-
|
101 |
-
Your task is to create prompts that:
|
102 |
-
1. Are highly specific and visual, describing exact details, colors, lighting, and composition
|
103 |
-
2. Include style references (e.g., "3D render", "hand-drawn", "flat design", "industrial sketch")
|
104 |
-
3. Specify technical details like resolution, aspect ratio, or rendering style when appropriate
|
105 |
-
4. Use descriptive adjectives for materials, textures, and atmospheres
|
106 |
-
5. Avoid abstract concepts - focus on concrete visual elements
|
107 |
-
|
108 |
-
Important guidelines:
|
109 |
-
- If given a style example, adapt the topic to match that specific visual style
|
110 |
-
- Maintain the technical vocabulary and visual descriptors from the style example
|
111 |
-
- Always output ONLY the prompt without any explanation or additional text
|
112 |
-
- Make prompts between 50-150 words for optimal results"""
|
113 |
-
|
114 |
-
user_message = f"Topic: {topic}"
|
115 |
-
if style_example:
|
116 |
-
user_message += f"\n\nStyle reference to follow:\n{style_example}\n\nAdapt the topic to match this exact visual style."
|
117 |
-
|
118 |
-
payload = {
|
119 |
-
"model": "dep89a2fld32mcm",
|
120 |
-
"messages": [
|
121 |
-
{
|
122 |
-
"role": "system",
|
123 |
-
"content": system_prompt
|
124 |
-
},
|
125 |
-
{
|
126 |
-
"role": "user",
|
127 |
-
"content": user_message
|
128 |
-
}
|
129 |
-
],
|
130 |
-
"max_tokens": 300,
|
131 |
-
"top_p": 0.8,
|
132 |
-
"temperature": 0.7,
|
133 |
-
"stream": False
|
134 |
-
}
|
135 |
-
|
136 |
-
try:
|
137 |
-
response = requests.post(url, json=payload, headers=headers)
|
138 |
-
if response.status_code == 200:
|
139 |
-
result = response.json()
|
140 |
-
return result['choices'][0]['message']['content'].strip()
|
141 |
-
else:
|
142 |
-
return f"프롬프트 생성 실패: {response.status_code}"
|
143 |
-
except Exception as e:
|
144 |
-
return f"프롬프트 생성 중 오류 발생: {str(e)}"
|
145 |
-
|
146 |
-
def translate_to_english(text: str) -> str:
|
147 |
-
"""한글 텍스트를 영어로 번역 (LLM 사용)"""
|
148 |
-
if not any(ord('가') <= ord(char) <= ord('힣') for char in text):
|
149 |
-
return text
|
150 |
-
|
151 |
-
url = "https://api.friendli.ai/dedicated/v1/chat/completions"
|
152 |
-
headers = {
|
153 |
-
"Authorization": f"Bearer {FRIENDLI_TOKEN}",
|
154 |
-
"Content-Type": "application/json"
|
155 |
-
}
|
156 |
-
|
157 |
-
payload = {
|
158 |
-
"model": "dep89a2fld32mcm",
|
159 |
-
"messages": [
|
160 |
-
{
|
161 |
-
"role": "system",
|
162 |
-
"content": "You are a translator. Translate the given Korean text to English. Only return the translation without any explanation."
|
163 |
-
},
|
164 |
-
{
|
165 |
-
"role": "user",
|
166 |
-
"content": text
|
167 |
-
}
|
168 |
-
],
|
169 |
-
"max_tokens": 500,
|
170 |
-
"top_p": 0.8,
|
171 |
-
"stream": False
|
172 |
-
}
|
173 |
-
|
174 |
-
try:
|
175 |
-
response = requests.post(url, json=payload, headers=headers)
|
176 |
-
if response.status_code == 200:
|
177 |
-
result = response.json()
|
178 |
-
return result['choices'][0]['message']['content'].strip()
|
179 |
-
else:
|
180 |
-
return text
|
181 |
-
except Exception as e:
|
182 |
-
return text
|
183 |
-
|
184 |
-
def generate_image(prompt: str, seed: int = 10) -> Tuple[Image.Image, str]:
|
185 |
-
"""Replicate API를 사용해 이미지 생성"""
|
186 |
-
try:
|
187 |
-
english_prompt = translate_to_english(prompt)
|
188 |
-
|
189 |
-
if not REPLICATE_API_TOKEN:
|
190 |
-
return None, "RAPI_TOKEN 환경변수가 설정되지 않았습니다."
|
191 |
-
|
192 |
-
client = replicate.Client(api_token=REPLICATE_API_TOKEN)
|
193 |
-
|
194 |
-
input_params = {
|
195 |
-
"seed": seed,
|
196 |
-
"prompt": english_prompt,
|
197 |
-
"speed_mode": "Extra Juiced 🚀 (even more speed)",
|
198 |
-
"output_quality": 80
|
199 |
-
}
|
200 |
-
|
201 |
-
output = client.run(
|
202 |
-
"prunaai/hidream-l1-fast:17c237d753218fed0ed477cb553902b6b75735f48c128537ab829096ef3d3645",
|
203 |
-
input=input_params
|
204 |
-
)
|
205 |
-
|
206 |
-
if output:
|
207 |
-
if isinstance(output, str) and output.startswith('http'):
|
208 |
-
response = requests.get(output)
|
209 |
-
img = Image.open(BytesIO(response.content))
|
210 |
-
return img, english_prompt
|
211 |
-
else:
|
212 |
-
img = Image.open(BytesIO(output.read()))
|
213 |
-
return img, english_prompt
|
214 |
-
else:
|
215 |
-
return None, "이미지 생성 실패"
|
216 |
-
|
217 |
-
except Exception as e:
|
218 |
-
return None, f"오류: {str(e)}"
|
219 |
-
|
220 |
-
def generate_images_parallel(topic: str, selected_styles: List[str], seed: int) -> List[Dict]:
|
221 |
-
"""선택된 스타일들에 대해 병렬로 이미지 생성"""
|
222 |
-
results = []
|
223 |
-
|
224 |
-
# 각 스타일에 대한 프롬프트 생성
|
225 |
-
prompts = []
|
226 |
-
for style_name in selected_styles:
|
227 |
-
if style_name in STYLE_TEMPLATES:
|
228 |
-
style_info = STYLE_TEMPLATES[style_name]
|
229 |
-
prompt = generate_prompt_with_llm(topic, style_info["example"])
|
230 |
-
prompts.append({
|
231 |
-
"style": style_name,
|
232 |
-
"prompt": prompt,
|
233 |
-
"style_info": style_info
|
234 |
-
})
|
235 |
-
|
236 |
-
# 병렬로 이미지 생성
|
237 |
-
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
|
238 |
-
future_to_style = {}
|
239 |
-
for prompt_data in prompts:
|
240 |
-
future = executor.submit(generate_image, prompt_data["prompt"], seed)
|
241 |
-
future_to_style[future] = prompt_data
|
242 |
-
|
243 |
-
for future in concurrent.futures.as_completed(future_to_style):
|
244 |
-
prompt_data = future_to_style[future]
|
245 |
-
try:
|
246 |
-
img, used_prompt = future.result()
|
247 |
-
results.append({
|
248 |
-
"style": prompt_data["style"],
|
249 |
-
"image": img,
|
250 |
-
"prompt": prompt_data["prompt"],
|
251 |
-
"used_prompt": used_prompt,
|
252 |
-
"success": img is not None
|
253 |
-
})
|
254 |
-
except Exception as e:
|
255 |
-
results.append({
|
256 |
-
"style": prompt_data["style"],
|
257 |
-
"image": None,
|
258 |
-
"prompt": prompt_data["prompt"],
|
259 |
-
"used_prompt": str(e),
|
260 |
-
"success": False
|
261 |
-
})
|
262 |
-
|
263 |
-
return results
|
264 |
-
|
265 |
-
def process_multiple_styles(topic: str, selected_styles: List[str], seed: int):
|
266 |
-
"""여러 스타일로 이미지 생성 처리"""
|
267 |
-
if not topic.strip():
|
268 |
-
return [], "주제를 입력해주세요."
|
269 |
-
|
270 |
-
if not selected_styles:
|
271 |
-
return [], "최소 하나의 스타일을 선택해주세요."
|
272 |
-
|
273 |
-
status = f"선택된 {len(selected_styles)}개 스타일로 이미지 생성 중..."
|
274 |
-
|
275 |
-
# 병렬로 이미지 생성
|
276 |
-
results = generate_images_parallel(topic, selected_styles, seed)
|
277 |
-
|
278 |
-
# 결과 정리
|
279 |
-
images = []
|
280 |
-
prompts_info = []
|
281 |
-
|
282 |
-
for result in results:
|
283 |
-
if result["success"]:
|
284 |
-
images.append((result["image"], result["style"]))
|
285 |
-
prompts_info.append(f"**{result['style']}**\n프롬프트: {result['prompt']}\n")
|
286 |
-
else:
|
287 |
-
prompts_info.append(f"**{result['style']}** - 생성 실패: {result['used_prompt']}\n")
|
288 |
-
|
289 |
-
final_status = f"총 {len(images)}개 이미지 생성 완료\n\n" + "\n".join(prompts_info)
|
290 |
-
|
291 |
-
return images, final_status
|
292 |
-
|
293 |
-
# Gradio 인터페이스 생성
|
294 |
-
with gr.Blocks(title="AI 멀티스타일 이미지 생성기", theme=gr.themes.Soft()) as demo:
|
295 |
-
gr.Markdown("""
|
296 |
-
# 🎨 AI 멀티스타일 이미지 생성기
|
297 |
-
|
298 |
-
주제를 입력하고 원하는 스타일을 선택하면, 각 스타일에 맞는 이미지를 동시에 생성합니다.
|
299 |
-
""")
|
300 |
-
|
301 |
-
with gr.Row():
|
302 |
-
with gr.Column(scale=1):
|
303 |
-
topic_input = gr.Textbox(
|
304 |
-
label="주제 입력",
|
305 |
-
placeholder="예: 우주를 여행하는 고양이, 미래의 도시, 혁신적인 제품 디자인",
|
306 |
-
lines=2
|
307 |
-
)
|
308 |
-
|
309 |
-
gr.Markdown("### 스타일 선택 (복수 선택 가능)")
|
310 |
-
|
311 |
-
# 스타일 체크박스 그룹
|
312 |
-
style_checkboxes = gr.CheckboxGroup(
|
313 |
-
choices=list(STYLE_TEMPLATES.keys()),
|
314 |
-
label="생성할 스타일",
|
315 |
-
value=["3D Style (Pixar-like)"],
|
316 |
-
info="각 스타일은 고유한 시각적 특성을 가지고 있습니다"
|
317 |
-
)
|
318 |
-
|
319 |
-
seed_input = gr.Slider(
|
320 |
-
minimum=1,
|
321 |
-
maximum=100,
|
322 |
-
value=10,
|
323 |
-
step=1,
|
324 |
-
label="시드 값 (동일한 시드는 동일한 이미지 생성)"
|
325 |
-
)
|
326 |
-
|
327 |
-
generate_btn = gr.Button("🚀 선택한 스타일로 이미지 생성", variant="primary", size="lg")
|
328 |
-
|
329 |
-
with gr.Column(scale=2):
|
330 |
-
# 갤러리로 여러 이미지 표시
|
331 |
-
output_gallery = gr.Gallery(
|
332 |
-
label="생성된 이미지들",
|
333 |
-
show_label=True,
|
334 |
-
elem_id="gallery",
|
335 |
-
columns=2,
|
336 |
-
rows=3,
|
337 |
-
object_fit="contain",
|
338 |
-
height="auto"
|
339 |
-
)
|
340 |
-
|
341 |
-
status_text = gr.Markdown(
|
342 |
-
value="생성 상태 및 프롬프트 정보가 여기에 표시됩니다."
|
343 |
-
)
|
344 |
-
|
345 |
-
# 스타일 설명 섹션
|
346 |
-
with gr.Accordion("📚 스타일 가이드", open=False):
|
347 |
-
style_guide_text = "### 사용 가능한 스타일:\n\n"
|
348 |
-
for style_name, style_info in STYLE_TEMPLATES.items():
|
349 |
-
style_guide_text += f"**{style_name}**: {style_info['description']}\n\n"
|
350 |
-
gr.Markdown(style_guide_text)
|
351 |
-
|
352 |
-
# 이미지 생성 이벤트
|
353 |
-
generate_btn.click(
|
354 |
-
fn=process_multiple_styles,
|
355 |
-
inputs=[topic_input, style_checkboxes, seed_input],
|
356 |
-
outputs=[output_gallery, status_text]
|
357 |
-
)
|
358 |
-
|
359 |
-
gr.Markdown("""
|
360 |
-
---
|
361 |
-
### 💡 사용 팁:
|
362 |
-
- **주제**: 구체적일수록 좋은 결과를 얻을 수 있습니다
|
363 |
-
- **스타일 조합**: 여러 스타일을 선택하면 다양한 시각적 표현을 비교할 수 있습니다
|
364 |
-
- **병렬 처리**: 선택한 모든 스타일의 이미지가 동시에 생성됩니다
|
365 |
-
- **시드 값**: 동일한 시드로 재현 가능한 결과를 얻을 수 있습니다
|
366 |
-
|
367 |
-
각 스타일은 전문적으로 큐레이션된 예제를 기반으로 프롬프트를 생성합니다.
|
368 |
-
""")
|
369 |
-
|
370 |
-
# 앱 실행
|
371 |
-
if __name__ == "__main__":
|
372 |
-
# 환경 변수 확인
|
373 |
-
if not REPLICATE_API_TOKEN:
|
374 |
-
print("경고: RAPI_TOKEN 환경 변수가 설정되지 않았습니다.")
|
375 |
-
if not FRIENDLI_TOKEN:
|
376 |
-
print("경고: FRIENDLI_TOKEN 환경 변수가 설정되지 않았습니다.")
|
377 |
-
|
378 |
-
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|