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()