Nymbo's picture
Gradio 6.29; local BiRefNet-lite via rembg (remote ysharma Space is dead); fonts via packages.txt; lazy example cache
63f8661 verified
Raw History Blame Contribute Delete
7.26 kB
import gradio as gr
from PIL import Image, ImageDraw, ImageFont
import numpy as np
import os
from PIL import Image
from rembg import new_session, remove
# The original called the Space ysharma/BiRefNet_for_text_writing, which no longer builds.
# Same model family (BiRefNet lite), run locally on CPU through rembg's ONNX session.
session = new_session("birefnet-general-lite")
def add_text_with_stroke(draw, text, x, y, font, text_color, stroke_width):
"""Helper function to draw text with stroke"""
# Draw the stroke/outline
for adj_x in range(-stroke_width, stroke_width + 1):
for adj_y in range(-stroke_width, stroke_width + 1):
draw.text((x + adj_x, y + adj_y), text, font=font, fill=text_color)
def remove_background(image):
return remove(image.convert("RGB"), session=session)
def superimpose(image_with_text, overlay_image):
# Open image as RGBA to handle transparency
overlay_image = overlay_image.convert("RGBA")
# Paste overlay on the background
image_with_text.paste(overlay_image, (0, 0), overlay_image)
# Save the final image
# image_with_text.save("output_image.png")
return image_with_text
def add_text_to_image(
input_image,
text,
font_size,
color,
opacity,
x_position,
y_position,
thickness
):
"""
Add text to an image with customizable properties
"""
# Convert gradio image (numpy array) to PIL Image
if input_image is None:
return None
image = Image.fromarray(input_image).convert("RGB")
# remove background
overlay_image = remove_background(image)
# Create a transparent overlay for the text
txt_overlay = Image.new('RGBA', image.size, (255, 255, 255, 0))
draw = ImageDraw.Draw(txt_overlay)
# Create a font with specified size
try:
font = ImageFont.truetype("DejaVuSans.ttf", int(font_size))
except:
# If DejaVu font is not found, try to use Arial or default
try:
font = ImageFont.truetype("arial.ttf", int(font_size))
except:
print("Using default font as system fonts not found")
font = ImageFont.load_default()
# Convert color name to RGB
color_map = {
'White': (255, 255, 255),
'Black': (0, 0, 0),
'Red': (255, 0, 0),
'Green': (0, 255, 0),
'Blue': (0, 0, 255),
'Yellow': (255, 255, 0),
'Purple': (128, 0, 128)
}
rgb_color = color_map.get(color, (255, 255, 255))
# Get text size for positioning
text_bbox = draw.textbbox((0, 0), text, font=font)
text_width = text_bbox[2] - text_bbox[0]
text_height = text_bbox[3] - text_bbox[1]
# Calculate actual x and y positions based on percentages
actual_x = int((image.width - text_width) * (x_position / 100))
actual_y = int((image.height - text_height) * (y_position / 100))
# Create final color with opacity
text_color = (*rgb_color, int(opacity))
# Draw the text with stroke for thickness
add_text_with_stroke(
draw,
text,
actual_x,
actual_y,
font,
text_color,
int(thickness)
)
# Combine the original image with the text overlay
if image.mode != 'RGBA':
image = image.convert('RGBA')
output_image = Image.alpha_composite(image, txt_overlay)
# Convert back to RGB for display
output_image = output_image.convert('RGB')
# superimpose images
output_image = superimpose(output_image, overlay_image)
# Convert PIL image back to numpy array for Gradio
return np.array(output_image)
# Create the Gradio interface
def create_interface():
with gr.Blocks() as app:
gr.Markdown("# Add Text Behind Image")
gr.Markdown("Upload an image and customize text properties to add text overlay.")
with gr.Row():
with gr.Column():
# Input components
input_image = gr.Image(label="Upload Image", type="numpy")
text_input = gr.Textbox(label="Enter Text", placeholder="Type your text here...")
font_size = gr.Slider(minimum=10, maximum=800, value=400, step=10,
label="Font Size")
thickness = gr.Slider(minimum=0, maximum=20, value=0, step=1,
label="Text Thickness")
color_dropdown = gr.Dropdown(
choices=["White", "Black", "Red", "Green", "Blue", "Yellow", "Purple"],
value="White",
label="Text Color"
)
opacity_slider = gr.Slider(minimum=0, maximum=255, value=255, step=1,
label="Opacity")
x_position = gr.Slider(minimum=0, maximum=100, value=50, step=1,
label="X Position (%)")
y_position = gr.Slider(minimum=0, maximum=100, value=50, step=1,
label="Y Position (%)")
with gr.Column():
# Output image
output_image = gr.Image(label="Output Image")
# Process button
process_btn = gr.Button("Add Text to Image")
# Connect the input components to the processing function
process_btn.click(
fn=add_text_to_image,
inputs=[
input_image,
text_input,
font_size,
color_dropdown,
opacity_slider,
x_position,
y_position,
thickness
],
outputs=output_image
)
# Add example inputs
gr.Examples(
examples=[
[
"pink_convertible.webp",
"EPIC",
420,
"Purple",
150,
50,
21,
9
],
[
"pear.jpg",
"PEAR",
350,
"Black",
100,
50,
2,
5
],
[
"sample_text_image.jpeg",
"LIFE",
400,
"Black",
150,
50,
2,
8
],
],
inputs=[
input_image,
text_input,
font_size,
color_dropdown,
opacity_slider,
x_position,
y_position,
thickness
],
outputs=output_image,
fn=add_text_to_image,
cache_examples=True,
cache_mode="lazy",
)
return app
# Launch the app
if __name__ == "__main__":
# DejaVu fonts come from packages.txt (fonts-dejavu-core); runtime apt-get can't run as non-root.
# Create and launch the interface
app = create_interface()
app.launch()