longshiine
feat: 1์ฐจ ๊ตฌํ˜„ ์˜ค๋‚˜๋ฃŒ
17d34e6
raw
history blame
12.8 kB
import requests
import gradio as gr
import zipfile
import os
import tempfile
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) -> str:
# ์ž„์‹œ ํŒŒ์ผ ์ƒ์„ฑ
with tempfile.NamedTemporaryFile(delete=False, suffix=".zip") as temp_zip:
with zipfile.ZipFile(temp_zip, '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())
# ์ž„์‹œ ํŒŒ์ผ์˜ ๊ฒฝ๋กœ๋ฅผ ๋ฐ˜ํ™˜
temp_zip_path = temp_zip.name
return temp_zip_path
def image_processing_single(background_image: Image.Image, product_image_url: str):
# ๊ฐ์ข… ์„ค์ • ๊ฐ’
background_image_size = 1024 # ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€์˜ ํฌ๊ธฐ
product_image_size = 650 # ๋ฐฐ๊ฒฝ์ด ์ œ๊ฑฐ๋œ ์ œํ’ˆ ์ด๋ฏธ์ง€์˜ ํฌ๊ธฐ
type_b_margin = 15
type_c_margin_1, type_c_margin_2 = 15, 45 # (1=๊ฐ€์šด๋ฐ ๋‘๊ฐœ์˜ ๋งˆ์ง„, 2= ์ค‘์•™ ํ•œ๊ฐœ์˜ top ๋งˆ์ง„)
# ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ํฌ๊ธฐ ์กฐ์ •
background_image = background_image.resize((background_image_size, background_image_size))
# ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ
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]
zip_file_path = create_zip_file(image_list)
return image_list, zip_file_path
def image_processing_batch(background_image: Image.Image, product_image_url_file_path: str):
# ๊ฐ์ข… ์„ค์ • ๊ฐ’
product_image_size = 650 # ๋ฐฐ๊ฒฝ์ด ์ œ๊ฑฐ๋œ ์ œํ’ˆ ์ด๋ฏธ์ง€์˜ ํฌ๊ธฐ
type_b_margin = 15
type_c_margin_1, type_c_margin_2 = 15, 45 # (1=๊ฐ€์šด๋ฐ ๋‘๊ฐœ์˜ ๋งˆ์ง„, 2= ์ค‘์•™ ํ•œ๊ฐœ์˜ top ๋งˆ์ง„)
# file to url
def file_to_list(file_path: str) -> list:
# ํŒŒ์ผ์„ ์—ด๊ณ  ์ฝ๊ธฐ
with open(file_path, "r") as f:
# ํŒŒ์ผ ๋‚ด์šฉ์„ ์ฝ์–ด์„œ ์ค„๋ฐ”๊ฟˆ(์—”ํ„ฐ)๋กœ ๊ตฌ๋ถ„๋œ ๊ฐ URL์„ ๋ฆฌ์ŠคํŠธ๋กœ ๋ณ€ํ™˜
content = f.read()
# ์ค„๋ฐ”๊ฟˆ์œผ๋กœ URL ๋ถ„๋ฆฌ
url_list = [url.strip() for url in content.splitlines() if url.strip()]
return url_list
product_image_url_list = file_to_list(product_image_url_file_path)
preview_image_list, image_list = [], []
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)
image_list.append(type_a_image)
image_list.append(type_b_image)
image_list.append(type_c_image)
if idx == 0:
preview_image_list = [type_a_image, type_b_image, type_c_image]
zip_file_path = create_zip_file(image_list)
return preview_image_list, zip_file_path
with gr.Blocks(theme=gr.themes.Monochrome()) as demo:
gr.HTML("""
<h1>Product Image Generator</h1>
<p>1. ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.<p>
<p>2-1. Single ๋ชจ๋“œ์˜ ๊ฒฝ์šฐ, ์ด๋ฏธ์ง€์˜ ๋งํฌ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.<p>
<p>2-2. Batch ๋ชจ๋“œ์˜ ๊ฒฝ์šฐ, ์ด๋ฏธ์ง€ ๋งํฌ๋“ค์ด ํฌํ•จ๋œ .txt ํŒŒ์ผ(ํ˜•์‹ = ๋งํฌ1(์—”ํ„ฐ) ๋งํฌ2(์—”ํ„ฐ) ..)์„ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.<p>
<p>3. Generate Images ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ์ œํ’ˆ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”.<p>
""")
with gr.Row(elem_classes="input_box"):
with gr.Column():
background_image = gr.Image(label="Background Image", type="pil", height=300)
gr.Examples(
examples=[["examples/background_1.png"], ["examples/background_2.png"], ["examples/background_3.png"], ["examples/background_4.png"]], # ์˜ˆ์ œ ์ด๋ฏธ์ง€ ๊ฒฝ๋กœ ๋ฆฌ์ŠคํŠธ
inputs=background_image, # ์˜ˆ์ œ๋ฅผ ํด๋ฆญํ–ˆ์„ ๋•Œ ํ•ด๋‹น ์ด๋ฏธ์ง€๊ฐ€ ์ด input์— ์ ์šฉ๋จ
label="์˜ˆ์ œ ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€")
with gr.Tab("Single Mode"):
with gr.Column():
link = gr.Textbox(label="Image URL")
gr.Examples(
examples=["https://url.kr/yx85be", "https://url.kr/if4wxo"],
inputs=link,
label="์˜ˆ์ œ ์ด๋ฏธ์ง€ ๋งํฌ"
)
btn_single = gr.Button("Generate Images")
with gr.Tab("Batch Mode"):
gr.HTML("<div>๋งํฌ1(์—”ํ„ฐ) ๋งํฌ2(์—”ํ„ฐ) ... ์œผ๋กœ ๊ตฌ์„ฑ๋œ .txt ํŒŒ์ผ์„ ์—…๋กœ๋“œ</div>")
with gr.Column():
links = gr.File(label="Image URLs (.txt file)", height=100)
gr.Examples(
examples=[["examples/links.txt"]],
inputs=links,
label="์˜ˆ์ œ .txt ํŒŒ์ผ"
)
btn_batch = gr.Button("Generate Images")
gr.HTML("""
<br><br>
<h1>์ƒ์„ฑ ๊ฒฐ๊ณผ ํ™•์ธ</h1>
<p>์ƒ์„ฑ์€ 1๊ฐœ ์ด๋ฏธ์ง€ ๋‹น 15~30์ดˆ ์†Œ์š”๋˜๋ฉฐ(๋ฐฐ๊ฒฝ์ œ๊ฑฐ AI ๋ชจ๋“ˆ ์ฒ˜๋ฆฌ์‹œ๊ฐ„), ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.</p>
<p>1. ์ฒซ๋ฒˆ์งธ ์ด๋ฏธ์ง€์— ๋Œ€ํ•œ ํ”„๋ฆฌ๋ทฐ(3๊ฐœ ํƒ€์ž…)</p>
<p>2. ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€ ์ „์ฒด๊ฐ€ ๋‹ด๊ธด ZIP ํŒŒ์ผ(results.zip) ๋‹ค์šด๋กœ๋“œ ๋งํฌ๊ฐ€ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค</p>
""")
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, output_zip], api_name="image_processing_single")
btn_batch.click(fn=image_processing_batch, inputs=[background_image, links], outputs=[preview, output_zip], api_name="image_processing_batch")
demo.launch()