longshiine
feat: single mode
80bf838
raw
history blame
9.56 kB
import requests
import gradio as gr
import zipfile
from PIL import Image
from io import BytesIO
from rembg import remove
def download_image(image_url: str) -> Image.Image:
# ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ
response = requests.get(image_url)
if response.status_code == 200:
original_image = Image.open(BytesIO(response.content))
return original_image
else:
raise Exception(f"Failed to download image. Status code: {response.status_code}")
def remove_background(image: Image.Image) -> Image.Image:
# ์ด๋ฏธ์ง€ ๋ˆ„๋ผ๋”ฐ๊ธฐ
try:
removebg_image = remove(
image,
post_process_mask=True,
alpha_matting=True,
alpha_matting_foreground_threshold=270,
alpha_matting_background_threshold=30,
alpha_matting_erode_size=15)
return removebg_image
except Exception as e:
print(f"Failed to remove background: {e}")
return None
def crop_image(image: Image.Image) -> Image.Image:
# ์ด๋ฏธ์ง€ ํฌ๋กญ
try:
# ์•ŒํŒŒ ์ฑ„๋„์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฏธ์ง€์˜ ๊ฒฝ๊ณ„ ์˜์—ญ ์ฐพ๊ธฐ
bbox = image.getbbox()
if bbox:
# ๊ฒฝ๊ณ„ ์ƒ์ž๋กœ ์ด๋ฏธ์ง€ ํฌ๋กญ
cropped_image = image.crop(bbox)
return cropped_image
else:
print("No bounding box found.")
return image
except Exception as e:
print(f"Failed to crop image: {e}")
return None
def resize_image(image: Image.Image, max_size: int) -> Image.Image:
# ์ด๋ฏธ์ง€ ํฌ๊ธฐ ์กฐ์ •
try:
# ์ด๋ฏธ์ง€์˜ ํ˜„์žฌ ๋„ˆ๋น„์™€ ๋†’์ด ๊ฐ€์ ธ์˜ค๊ธฐ
width, height = image.size
# ๋„ˆ๋น„์™€ ๋†’์ด ์ค‘ ๋” ํฐ ์ชฝ์˜ ๋น„์œจ์— ๋งž์ถฐ ํฌ๊ธฐ๋ฅผ ์กฐ์ •
if width > height:
new_width = max_size
new_height = int((max_size / width) * height)
else:
new_height = max_size
new_width = int((max_size / height) * width)
resized_image = image.resize((new_width, new_height))
return resized_image
except Exception as e:
print(f"Failed to resize image: {e}")
return None
def paste_to_background_type_a(background: Image.Image, product: Image.Image) -> Image.Image:
# ๋ฐฐ๊ฒฝ์— ์ œํ’ˆ ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ
try:
# ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ํฌ๊ธฐ ๊ฐ€์ ธ์˜ค๊ธฐ
bg_width, bg_height = background.size
# ์ œํ’ˆ ์ด๋ฏธ์ง€ ํฌ๊ธฐ ๊ฐ€์ ธ์˜ค๊ธฐ
product_width, product_height = product.size
# ์ œํ’ˆ ์ด๋ฏธ์ง€๋ฅผ ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ์ค‘์•™์— ์œ„์น˜์‹œํ‚ค๊ธฐ
offset = ((bg_width - product_width) // 2, (bg_height - product_height) // 2)
# ์•ŒํŒŒ ์ฑ„๋„์„ ๊ณ ๋ คํ•˜์—ฌ ํ•ฉ์„ฑ
background.paste(product, offset, mask=product)
return background
except Exception as e:
print(f"Failed to paste product image to background: {e}")
return None
def paste_to_background_type_b(background: Image.Image, product: Image.Image, margin: int = 10) -> Image.Image:
try:
# ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ํฌ๊ธฐ ๊ฐ€์ ธ์˜ค๊ธฐ
bg_width, bg_height = background.size
# ์ œํ’ˆ ์ด๋ฏธ์ง€ ํฌ๊ธฐ ๊ฐ€์ ธ์˜ค๊ธฐ
product_width, product_height = product.size
# ๋‘ ์ œํ’ˆ ์ด๋ฏธ์ง€๋ฅผ ์œ„ํ•œ ์ „์ฒด ๋„ˆ๋น„ ๊ณ„์‚ฐ (์ œํ’ˆ ์ด๋ฏธ์ง€ ๋‘ ๊ฐœ + ๋งˆ์ง„)
total_width = (product_width * 2) + margin
if total_width > bg_width:
raise ValueError("Background is too narrow to fit two product images with the specified margin.")
# ์ฒซ ๋ฒˆ์งธ ์ œํ’ˆ ์ด๋ฏธ์ง€์˜ ์™ผ์ชฝ ์ƒ๋‹จ ์ขŒํ‘œ ๊ณ„์‚ฐ
left_offset = (bg_width - total_width) // 2
top_offset = (bg_height - product_height) // 2
# ๋‘ ๋ฒˆ์งธ ์ œํ’ˆ ์ด๋ฏธ์ง€์˜ ์™ผ์ชฝ ์ƒ๋‹จ ์ขŒํ‘œ ๊ณ„์‚ฐ
right_offset = left_offset + product_width + margin
# ์ฒซ ๋ฒˆ์งธ ์ œํ’ˆ ์ด๋ฏธ์ง€ ๋ฐฐ๊ฒฝ์— ํ•ฉ์„ฑ
background.paste(product, (left_offset, top_offset), mask=product)
# ๋‘ ๋ฒˆ์งธ ์ œํ’ˆ ์ด๋ฏธ์ง€ ๋ฐฐ๊ฒฝ์— ํ•ฉ์„ฑ
background.paste(product, (right_offset, top_offset), mask=product)
return background
except Exception as e:
print(f"Failed to paste product images to background: {e}")
return None
def paste_to_background_type_c(background: Image.Image, product: Image.Image, margin: int = 10, top_margin: int = 15) -> Image.Image:
try:
# ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ํฌ๊ธฐ ๊ฐ€์ ธ์˜ค๊ธฐ
bg_width, bg_height = background.size
# ์ œํ’ˆ ์ด๋ฏธ์ง€ ํฌ๊ธฐ ๊ฐ€์ ธ์˜ค๊ธฐ
product_width, product_height = product.size
# ๋จผ์ € type_b ํ˜•ํƒœ๋กœ ์•„๋ž˜ ๋‘ ์ œํ’ˆ ์ด๋ฏธ์ง€๋ฅผ ๋ฐฐ์น˜
background_with_two_products = paste_to_background_type_b(background, product, margin)
# ์ค‘์•™ ์ƒ๋‹จ์— ์œ„์น˜ํ•  ์ œํ’ˆ ์ด๋ฏธ์ง€์˜ ์˜คํ”„์…‹ ๊ณ„์‚ฐ
center_offset_x = (bg_width - product_width) // 2
center_offset_y = ((bg_height - product_height) // 2) + top_margin
if center_offset_y < 0:
raise ValueError("Background is too small to fit three product images with the specified top margin.")
# ์„ธ ๋ฒˆ์งธ ์ œํ’ˆ ์ด๋ฏธ์ง€ ๋ฐฐ๊ฒฝ์— ํ•ฉ์„ฑ (์ค‘์•™ ์ƒ๋‹จ)
background_with_two_products.paste(product, (center_offset_x, center_offset_y), mask=product)
# ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€ ์ €์žฅ ๋ฐ ๋ฐ˜ํ™˜
return background_with_two_products
except Exception as e:
print(f"Failed to paste product images to background: {e}")
return None
def create_zip_file(images: list, zip_filename: str = "images.zip") -> bytes:
# ๋ฉ”๋ชจ๋ฆฌ ๋‚ด์—์„œ ZIP ํŒŒ์ผ ์ƒ์„ฑ
zip_buffer = BytesIO()
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zipf:
for i, image in enumerate(images):
# ์ด๋ฏธ์ง€๋ฅผ ๋ฉ”๋ชจ๋ฆฌ ๋ฒ„ํผ์— ์ €์žฅ
image_buffer = BytesIO()
image.save(image_buffer, format="PNG")
image_buffer.seek(0)
# ๋ฒ„ํผ ๋‚ด์šฉ์„ ZIP ํŒŒ์ผ์— ์ถ”๊ฐ€
zipf.writestr(f"image_{i + 1}.png", image_buffer.getvalue())
# ZIP ํŒŒ์ผ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜
zip_buffer.seek(0)
return zip_buffer.getvalue()
def image_processing_single(background_image: Image.Image, product_image_url: str):
# ๊ฐ์ข… ์„ค์ • ๊ฐ’
product_image_size = 650 # ๋ฐฐ๊ฒฝ์ด ์ œ๊ฑฐ๋œ ์ œํ’ˆ ์ด๋ฏธ์ง€์˜ ํฌ๊ธฐ
type_b_margin = 15
type_c_margin_1, type_c_margin_2 = 15, 45 # (1=๊ฐ€์šด๋ฐ ๋‘๊ฐœ์˜ ๋งˆ์ง„, 2= ์ค‘์•™ ํ•œ๊ฐœ์˜ top ๋งˆ์ง„)
# ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ
original_image = download_image(product_image_url)
# ์ด๋ฏธ์ง€ ๋ˆ„๋ผ๋”ฐ๊ธฐ
removebg_image = remove_background(original_image)
# ์ด๋ฏธ์ง€ ํฌ๋กญ
cropped_image = crop_image(removebg_image)
# ํฌ๋กญ๋œ ์ด๋ฏธ์ง€ ์›ํ•˜๋Š” ์‚ฌ์ด์ฆˆ๋กœ resize
resized_image = resize_image(cropped_image, product_image_size)
# type_a ํ•ฉ์„ฑ
type_a_image = paste_to_background_type_a(background_image.copy(), resized_image)
type_b_image = paste_to_background_type_b(background_image.copy(), resized_image, type_b_margin)
type_c_image = paste_to_background_type_c(background_image.copy(), resized_image, type_c_margin_1, type_c_margin_2)
# ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€ ๋ฐ˜ํ™˜
image_list = [type_a_image, type_b_image, type_c_image]
image_zip_file = create_zip_file(image_list)
return image_list #, image_zip_file
def image_processing_multi(background_image: Image.Image, product_image_url_list: list):
# ๊ฐ์ข… ์„ค์ • ๊ฐ’
product_image_size = 650 # ๋ฐฐ๊ฒฝ์ด ์ œ๊ฑฐ๋œ ์ œํ’ˆ ์ด๋ฏธ์ง€์˜ ํฌ๊ธฐ
type_b_margin = 15
type_c_margin_1, type_c_margin_2 = 15, 45 # (1=๊ฐ€์šด๋ฐ ๋‘๊ฐœ์˜ ๋งˆ์ง„, 2= ์ค‘์•™ ํ•œ๊ฐœ์˜ top ๋งˆ์ง„)
for idx, product_image_url in enumerate(product_image_url_list):
# ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ
original_image = download_image(product_image_url)
# ์ด๋ฏธ์ง€ ๋ˆ„๋ผ๋”ฐ๊ธฐ
removebg_image = remove_background(original_image)
# ์ด๋ฏธ์ง€ ํฌ๋กญ
cropped_image = crop_image(removebg_image)
# ํฌ๋กญ๋œ ์ด๋ฏธ์ง€ ์›ํ•˜๋Š” ์‚ฌ์ด์ฆˆ๋กœ resize
resized_image = resize_image(cropped_image, product_image_size)
# type_a ํ•ฉ์„ฑ
type_a_image = paste_to_background_type_a(background_image.copy(), resized_image)
type_b_image = paste_to_background_type_b(background_image.copy(), resized_image, type_b_margin)
type_c_image = paste_to_background_type_c(background_image.copy(), resized_image, type_c_margin_1, type_c_margin_2)
with gr.Blocks(theme=gr.themes.Monochrome()) as demo:
with gr.Row():
background_image = gr.Image(label="Background Image", type="pil")
with gr.Tab("Single"):
link = gr.Textbox(label="Image URL")
btn_single = gr.Button("Generate Images")
with gr.Tab("Multi"):
links = gr.File(label="Image URLs (.txt file)")
btn_multi = gr.Button("Generate Images")
with gr.Row():
preview = gr.Gallery(label="Generated images", show_label=False, elem_id="gallery", columns=[3], rows=[1], object_fit="contain", height="auto")
output_zip = gr.File(label="Download Result Zip File")
# evnets
btn_single.click(fn=image_processing_single, inputs=[background_image, link], outputs=[preview], api_name="image_processing")
demo.launch()