Spaces:
Paused
Paused
Download app.py from Nymbo/Text_Behind_Image: direct link, hf CLI and curl.
- Browser
- Download file 7.26 kB
-
https://huggingface.co/spaces/Nymbo/Text_Behind_Image/resolve/main/app.py
- Command line
-
hf download hf://spaces/Nymbo/Text_Behind_Image/app.py
-
curl -L -o app.py https://huggingface.co/spaces/Nymbo/Text_Behind_Image/resolve/main/app.py
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() |