|
|
import gradio as gr |
|
|
import numpy as np |
|
|
import matplotlib.pyplot as plt |
|
|
from PIL import Image |
|
|
import io |
|
|
from stringart import StringArtGenerator |
|
|
|
|
|
def generate_string_art(image, num_nails, iterations, weight, shape): |
|
|
"""Generate string art from uploaded image""" |
|
|
if image is None: |
|
|
return None |
|
|
|
|
|
try: |
|
|
|
|
|
generator = StringArtGenerator() |
|
|
|
|
|
|
|
|
np_img = np.array(image) |
|
|
generator.image = image |
|
|
generator.data = np.flipud(np_img).transpose() |
|
|
|
|
|
|
|
|
generator.preprocess() |
|
|
generator.set_nails(int(num_nails)) |
|
|
generator.set_iterations(int(iterations)) |
|
|
generator.set_weight(int(weight)) |
|
|
generator.set_shape(shape) |
|
|
generator.set_seed(42) |
|
|
|
|
|
|
|
|
pattern = generator.generate() |
|
|
|
|
|
if len(pattern) == 0: |
|
|
return None |
|
|
|
|
|
|
|
|
lines_x = [] |
|
|
lines_y = [] |
|
|
for i in range(len(pattern)-1): |
|
|
lines_x.extend([pattern[i][0], pattern[i+1][0], None]) |
|
|
lines_y.extend([pattern[i][1], pattern[i+1][1], None]) |
|
|
|
|
|
|
|
|
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 8)) |
|
|
|
|
|
|
|
|
ax1.imshow(generator.image, cmap='gray') |
|
|
ax1.set_title('Original Image (Preprocessed)') |
|
|
ax1.axis('off') |
|
|
|
|
|
|
|
|
xmin, ymin = 0., 0. |
|
|
xmax = generator.data.shape[0] |
|
|
ymax = generator.data.shape[1] |
|
|
|
|
|
ax2.set_xlim([xmin, xmax]) |
|
|
ax2.set_ylim([ymin, ymax]) |
|
|
ax2.set_aspect('equal') |
|
|
ax2.axis('off') |
|
|
ax2.set_title(f'String Art ({len(pattern)} connections)') |
|
|
ax2.set_facecolor('white') |
|
|
|
|
|
|
|
|
ax2.plot(lines_x, lines_y, linewidth=0.15, color='black', alpha=0.8) |
|
|
|
|
|
plt.tight_layout() |
|
|
|
|
|
|
|
|
buf = io.BytesIO() |
|
|
plt.savefig(buf, format='png', bbox_inches='tight', dpi=150, facecolor='white') |
|
|
buf.seek(0) |
|
|
plt.close() |
|
|
|
|
|
|
|
|
result_image = Image.open(buf) |
|
|
return result_image |
|
|
|
|
|
except Exception as e: |
|
|
print(f"Error: {str(e)}") |
|
|
return None |
|
|
|
|
|
|
|
|
with gr.Blocks( |
|
|
title="String Art Generator", |
|
|
theme=gr.themes.Soft(), |
|
|
css=""" |
|
|
.gradio-container { |
|
|
max-width: 1200px !important; |
|
|
} |
|
|
""" |
|
|
) as demo: |
|
|
|
|
|
gr.Markdown(""" |
|
|
# π¨ String Art Generator |
|
|
|
|
|
Transform your images into beautiful string art! Upload an image and watch as an algorithm recreates it using virtual string connections between nails. |
|
|
|
|
|
**How it works:** The algorithm places nails around a circle/rectangle, then iteratively finds the darkest paths between nails to recreate your image. |
|
|
""") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
gr.Markdown("### π€ Input") |
|
|
image_input = gr.Image( |
|
|
type="pil", |
|
|
label="Upload Your Image", |
|
|
sources=["upload"], |
|
|
height=300 |
|
|
) |
|
|
|
|
|
gr.Markdown("### βοΈ Settings") |
|
|
with gr.Row(): |
|
|
num_nails = gr.Slider( |
|
|
minimum=50, |
|
|
maximum=200, |
|
|
value=120, |
|
|
step=10, |
|
|
label="Number of Nails", |
|
|
info="More nails = more detail, but slower processing" |
|
|
) |
|
|
iterations = gr.Slider( |
|
|
minimum=500, |
|
|
maximum=3000, |
|
|
value=1500, |
|
|
step=100, |
|
|
label="Iterations", |
|
|
info="More iterations = better quality, but slower" |
|
|
) |
|
|
|
|
|
with gr.Row(): |
|
|
weight = gr.Slider( |
|
|
minimum=5, |
|
|
maximum=40, |
|
|
value=20, |
|
|
step=5, |
|
|
label="Line Weight", |
|
|
info="Thickness of virtual string" |
|
|
) |
|
|
shape = gr.Dropdown( |
|
|
choices=["circle", "rectangle"], |
|
|
value="circle", |
|
|
label="Nail Arrangement", |
|
|
info="Shape for nail placement" |
|
|
) |
|
|
|
|
|
generate_btn = gr.Button( |
|
|
"π¨ Generate String Art", |
|
|
variant="primary", |
|
|
size="lg" |
|
|
) |
|
|
|
|
|
gr.Markdown(""" |
|
|
### π‘ Tips: |
|
|
- High contrast images work best |
|
|
- Simple compositions are ideal |
|
|
- Processing may take 1-3 minutes |
|
|
- Start with lower settings for faster results |
|
|
""") |
|
|
|
|
|
with gr.Column(scale=1): |
|
|
gr.Markdown("### π― Result") |
|
|
output_image = gr.Image( |
|
|
label="Generated String Art", |
|
|
height=600 |
|
|
) |
|
|
|
|
|
generate_btn.click( |
|
|
fn=generate_string_art, |
|
|
inputs=[image_input, num_nails, iterations, weight, shape], |
|
|
outputs=output_image, |
|
|
show_progress=True |
|
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.launch() |