Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
@@ -4,13 +4,25 @@ from PIL import Image, ImageDraw, ImageFont
|
|
4 |
import textwrap
|
5 |
import os
|
6 |
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
|
|
|
7 |
|
8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
try:
|
10 |
-
|
11 |
-
|
|
|
12 |
response.raise_for_status()
|
13 |
-
image_filename = f'generated_image_{index}.jpg'
|
14 |
with open(image_filename, 'wb') as file:
|
15 |
file.write(response.content)
|
16 |
return image_filename
|
@@ -18,45 +30,38 @@ def download_image(prompt, index):
|
|
18 |
print(f"Error downloading image {index}: {e}")
|
19 |
return None
|
20 |
|
21 |
-
def add_text_to_image(image_path, text):
|
22 |
try:
|
23 |
img = Image.open(image_path)
|
24 |
draw = ImageDraw.Draw(img)
|
25 |
|
26 |
-
# Use a default font if custom font is not available
|
27 |
try:
|
28 |
-
font = ImageFont.truetype("arial.ttf",
|
29 |
except IOError:
|
30 |
-
font = ImageFont.load_default().font_variant(size=
|
31 |
|
32 |
-
# Calculate text size and position
|
33 |
img_width, img_height = img.size
|
34 |
-
max_text_width = int(img_width * 0.9)
|
35 |
lines = textwrap.wrap(text, width=40)
|
36 |
|
37 |
-
|
38 |
-
line_height = font.getbbox("hg")[3] + 5 # Add some padding
|
39 |
text_height = line_height * len(lines)
|
40 |
|
41 |
-
# Create semi-transparent background
|
42 |
overlay = Image.new('RGBA', img.size, (0, 0, 0, 0))
|
43 |
overlay_draw = ImageDraw.Draw(overlay)
|
44 |
overlay_draw.rectangle([(0, img_height - text_height - 20), (img_width, img_height)],
|
45 |
fill=(0, 0, 0, 180))
|
46 |
|
47 |
-
# Paste the overlay onto the original image
|
48 |
img = img.convert('RGBA')
|
49 |
img = Image.alpha_composite(img, overlay)
|
50 |
draw = ImageDraw.Draw(img)
|
51 |
|
52 |
-
# Add text
|
53 |
y_text = img_height - text_height - 10
|
54 |
for line in lines:
|
55 |
-
# Use getbbox for line width calculation
|
56 |
bbox = font.getbbox(line)
|
57 |
line_width = bbox[2] - bbox[0]
|
58 |
x_text = (img_width - line_width) / 2
|
59 |
-
draw.text((x_text, y_text), line, font=font, fill=
|
60 |
y_text += line_height
|
61 |
|
62 |
img_with_text = f'text_added_{os.path.basename(image_path)}'
|
@@ -67,25 +72,15 @@ def add_text_to_image(image_path, text):
|
|
67 |
print(f"Error adding text to image: {e}")
|
68 |
return None
|
69 |
|
70 |
-
|
71 |
-
future_to_idx = {executor.submit(process_line, idx, line): idx for idx, line in enumerate(lines)}
|
72 |
-
for future in progress.tqdm(as_completed(future_to_idx), total=len(lines), desc="Processing images"):
|
73 |
-
idx = future_to_idx[future]
|
74 |
-
result = future.result()
|
75 |
-
if result:
|
76 |
-
images_with_text.append((idx, result))
|
77 |
-
|
78 |
-
return [img for _, img in sorted(images_with_text)]
|
79 |
-
|
80 |
-
def process_story_lines(story, progress=gr.Progress()):
|
81 |
lines = [line.strip() for line in story.split('\n') if line.strip()]
|
82 |
images_with_text = []
|
83 |
|
84 |
def process_line(idx, line):
|
85 |
prompt = line.replace(" ", "_")
|
86 |
-
img_file = download_image(prompt, idx)
|
87 |
if img_file:
|
88 |
-
return add_text_to_image(img_file, line)
|
89 |
return None
|
90 |
|
91 |
with ThreadPoolExecutor(max_workers=5) as executor:
|
@@ -98,24 +93,60 @@ def process_story_lines(story, progress=gr.Progress()):
|
|
98 |
|
99 |
return [img for _, img in sorted(images_with_text)]
|
100 |
|
101 |
-
def visualize_story_images(story):
|
102 |
if not story.strip():
|
103 |
return []
|
104 |
-
return process_story_lines(story)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
105 |
|
106 |
-
with gr.Blocks() as iface:
|
107 |
-
gr.Markdown("# Story Visualizer")
|
108 |
|
109 |
with gr.Row():
|
110 |
-
|
111 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
112 |
|
|
|
113 |
gallery_output = gr.Gallery(label="Visualized Story", show_label=False, elem_id="gallery", columns=2, rows=2, height="auto")
|
114 |
|
|
|
|
|
|
|
|
|
|
|
|
|
115 |
visualize_button.click(
|
116 |
fn=visualize_story_images,
|
117 |
-
inputs=[story_input],
|
118 |
outputs=[gallery_output]
|
119 |
)
|
120 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
iface.launch(share=True)
|
|
|
4 |
import textwrap
|
5 |
import os
|
6 |
from concurrent.futures import ThreadPoolExecutor, as_completed
|
7 |
+
import random
|
8 |
+
import time
|
9 |
|
10 |
+
# Constants for image styles
|
11 |
+
IMAGE_STYLES = {
|
12 |
+
"Realistic": "",
|
13 |
+
"Cartoon": "cartoon style",
|
14 |
+
"Watercolor": "watercolor painting style",
|
15 |
+
"Sketch": "pencil sketch style",
|
16 |
+
"Anime": "anime style"
|
17 |
+
}
|
18 |
+
|
19 |
+
def download_image(prompt, index, style):
|
20 |
try:
|
21 |
+
style_prompt = f"{prompt}, {IMAGE_STYLES[style]}" if style != "Realistic" else prompt
|
22 |
+
url = f"https://pollinations.ai/p/{style_prompt.replace(' ', '_')}"
|
23 |
+
response = requests.get(url, timeout=15)
|
24 |
response.raise_for_status()
|
25 |
+
image_filename = f'generated_image_{index}_{int(time.time())}.jpg'
|
26 |
with open(image_filename, 'wb') as file:
|
27 |
file.write(response.content)
|
28 |
return image_filename
|
|
|
30 |
print(f"Error downloading image {index}: {e}")
|
31 |
return None
|
32 |
|
33 |
+
def add_text_to_image(image_path, text, font_size, text_color):
|
34 |
try:
|
35 |
img = Image.open(image_path)
|
36 |
draw = ImageDraw.Draw(img)
|
37 |
|
|
|
38 |
try:
|
39 |
+
font = ImageFont.truetype("arial.ttf", font_size)
|
40 |
except IOError:
|
41 |
+
font = ImageFont.load_default().font_variant(size=font_size)
|
42 |
|
|
|
43 |
img_width, img_height = img.size
|
44 |
+
max_text_width = int(img_width * 0.9)
|
45 |
lines = textwrap.wrap(text, width=40)
|
46 |
|
47 |
+
line_height = font.getbbox("hg")[3] + 5
|
|
|
48 |
text_height = line_height * len(lines)
|
49 |
|
|
|
50 |
overlay = Image.new('RGBA', img.size, (0, 0, 0, 0))
|
51 |
overlay_draw = ImageDraw.Draw(overlay)
|
52 |
overlay_draw.rectangle([(0, img_height - text_height - 20), (img_width, img_height)],
|
53 |
fill=(0, 0, 0, 180))
|
54 |
|
|
|
55 |
img = img.convert('RGBA')
|
56 |
img = Image.alpha_composite(img, overlay)
|
57 |
draw = ImageDraw.Draw(img)
|
58 |
|
|
|
59 |
y_text = img_height - text_height - 10
|
60 |
for line in lines:
|
|
|
61 |
bbox = font.getbbox(line)
|
62 |
line_width = bbox[2] - bbox[0]
|
63 |
x_text = (img_width - line_width) / 2
|
64 |
+
draw.text((x_text, y_text), line, font=font, fill=text_color)
|
65 |
y_text += line_height
|
66 |
|
67 |
img_with_text = f'text_added_{os.path.basename(image_path)}'
|
|
|
72 |
print(f"Error adding text to image: {e}")
|
73 |
return None
|
74 |
|
75 |
+
def process_story_lines(story, style, font_size, text_color, progress=gr.Progress()):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
76 |
lines = [line.strip() for line in story.split('\n') if line.strip()]
|
77 |
images_with_text = []
|
78 |
|
79 |
def process_line(idx, line):
|
80 |
prompt = line.replace(" ", "_")
|
81 |
+
img_file = download_image(prompt, idx, style)
|
82 |
if img_file:
|
83 |
+
return add_text_to_image(img_file, line, font_size, text_color)
|
84 |
return None
|
85 |
|
86 |
with ThreadPoolExecutor(max_workers=5) as executor:
|
|
|
93 |
|
94 |
return [img for _, img in sorted(images_with_text)]
|
95 |
|
96 |
+
def visualize_story_images(story, style, font_size, text_color):
|
97 |
if not story.strip():
|
98 |
return []
|
99 |
+
return process_story_lines(story, style, font_size, text_color)
|
100 |
+
|
101 |
+
def preview_image(story, style, font_size, text_color):
|
102 |
+
if not story.strip():
|
103 |
+
return None
|
104 |
+
lines = [line.strip() for line in story.split('\n') if line.strip()]
|
105 |
+
if not lines:
|
106 |
+
return None
|
107 |
+
preview_line = random.choice(lines)
|
108 |
+
img_file = download_image(preview_line, 0, style)
|
109 |
+
if img_file:
|
110 |
+
return add_text_to_image(img_file, preview_line, font_size, text_color)
|
111 |
+
return None
|
112 |
|
113 |
+
with gr.Blocks(css="#gallery {height: 600px}") as iface:
|
114 |
+
gr.Markdown("# 📚✨ Story Visualizer")
|
115 |
|
116 |
with gr.Row():
|
117 |
+
with gr.Column(scale=2):
|
118 |
+
story_input = gr.Textbox(lines=10, placeholder="Enter your story here...", label="Story Input")
|
119 |
+
with gr.Column(scale=1):
|
120 |
+
style_dropdown = gr.Dropdown(choices=list(IMAGE_STYLES.keys()), value="Realistic", label="Image Style")
|
121 |
+
font_size_slider = gr.Slider(minimum=20, maximum=60, value=30, step=2, label="Font Size")
|
122 |
+
text_color_picker = gr.ColorPicker(value="#FFFFFF", label="Text Color")
|
123 |
+
preview_button = gr.Button("Preview", variant="secondary")
|
124 |
+
visualize_button = gr.Button("Visualize Story", variant="primary")
|
125 |
|
126 |
+
preview_output = gr.Image(label="Preview", height=300)
|
127 |
gallery_output = gr.Gallery(label="Visualized Story", show_label=False, elem_id="gallery", columns=2, rows=2, height="auto")
|
128 |
|
129 |
+
preview_button.click(
|
130 |
+
fn=preview_image,
|
131 |
+
inputs=[story_input, style_dropdown, font_size_slider, text_color_picker],
|
132 |
+
outputs=[preview_output]
|
133 |
+
)
|
134 |
+
|
135 |
visualize_button.click(
|
136 |
fn=visualize_story_images,
|
137 |
+
inputs=[story_input, style_dropdown, font_size_slider, text_color_picker],
|
138 |
outputs=[gallery_output]
|
139 |
)
|
140 |
|
141 |
+
gr.Markdown("""
|
142 |
+
## How to use:
|
143 |
+
1. Enter your story in the text box, with each line representing a scene.
|
144 |
+
2. Choose an image style from the dropdown menu.
|
145 |
+
3. Adjust the font size and text color as desired.
|
146 |
+
4. Click "Preview" to see how a random scene from your story might look.
|
147 |
+
5. When you're happy with the settings, click "Visualize Story" to generate images for all scenes.
|
148 |
+
|
149 |
+
Enjoy bringing your story to life! 🎨📖
|
150 |
+
""")
|
151 |
+
|
152 |
iface.launch(share=True)
|