longshiine commited on
Commit
a68b775
·
1 Parent(s): 17d34e6

feat: miku theme

Browse files

Signed-off-by: longshiine <[email protected]>

Files changed (3) hide show
  1. .gitignore +1 -0
  2. app.py +2 -252
  3. infra.py +250 -0
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ __pycache__/
app.py CHANGED
@@ -1,257 +1,7 @@
1
- import requests
2
  import gradio as gr
3
- import zipfile
4
- import os
5
- import tempfile
6
- from PIL import Image
7
- from io import BytesIO
8
- from rembg import remove
9
 
10
- def download_image(image_url: str) -> Image.Image:
11
- # 이미지 다운로드
12
- response = requests.get(image_url)
13
-
14
- if response.status_code == 200:
15
- original_image = Image.open(BytesIO(response.content))
16
- return original_image
17
- else:
18
- raise Exception(f"Failed to download image. Status code: {response.status_code}")
19
-
20
- def remove_background(image: Image.Image) -> Image.Image:
21
- # 이미지 누끼따기
22
- try:
23
- removebg_image = remove(
24
- image,
25
- post_process_mask=True,
26
- alpha_matting=True,
27
- alpha_matting_foreground_threshold=270,
28
- alpha_matting_background_threshold=30,
29
- alpha_matting_erode_size=15)
30
- return removebg_image
31
-
32
- except Exception as e:
33
- print(f"Failed to remove background: {e}")
34
- return None
35
-
36
- def crop_image(image: Image.Image) -> Image.Image:
37
- # 이미지 크롭
38
- try:
39
- # 알파 채널을 사용하여 이미지의 경계 영역 찾기
40
- bbox = image.getbbox()
41
- if bbox:
42
- # 경계 상자로 이미지 크롭
43
- cropped_image = image.crop(bbox)
44
- return cropped_image
45
- else:
46
- print("No bounding box found.")
47
- return image
48
-
49
- except Exception as e:
50
- print(f"Failed to crop image: {e}")
51
- return None
52
-
53
- def resize_image(image: Image.Image, max_size: int) -> Image.Image:
54
- # 이미지 크기 조정
55
- try:
56
- # 이미지의 현재 너비와 높이 가져오기
57
- width, height = image.size
58
-
59
- # 너비와 높이 중 더 큰 쪽의 비율에 맞춰 크기를 조정
60
- if width > height:
61
- new_width = max_size
62
- new_height = int((max_size / width) * height)
63
- else:
64
- new_height = max_size
65
- new_width = int((max_size / height) * width)
66
-
67
- resized_image = image.resize((new_width, new_height))
68
- return resized_image
69
-
70
- except Exception as e:
71
- print(f"Failed to resize image: {e}")
72
- return None
73
-
74
- def paste_to_background_type_a(background: Image.Image, product: Image.Image) -> Image.Image:
75
- # 배경에 제품 이미지 합성
76
- try:
77
- # 배경 이미지 크기 가져오기
78
- bg_width, bg_height = background.size
79
-
80
- # 제품 이미지 크기 가져오기
81
- product_width, product_height = product.size
82
-
83
- # 제품 이미지를 배경 이미지 중앙에 위치시키기
84
- offset = ((bg_width - product_width) // 2, (bg_height - product_height) // 2)
85
-
86
- # 알파 채널을 고려하여 합성
87
- background.paste(product, offset, mask=product)
88
- return background
89
-
90
- except Exception as e:
91
- print(f"Failed to paste product image to background: {e}")
92
- return None
93
-
94
- def paste_to_background_type_b(background: Image.Image, product: Image.Image, margin: int = 10) -> Image.Image:
95
- try:
96
- # 배경 이미지 크기 가져오기
97
- bg_width, bg_height = background.size
98
-
99
- # 제품 이미지 크기 가져오기
100
- product_width, product_height = product.size
101
-
102
- # 두 제품 이미지를 위한 전체 너비 계산 (제품 이미지 두 개 + 마진)
103
- total_width = (product_width * 2) + margin
104
-
105
- if total_width > bg_width:
106
- raise ValueError("Background is too narrow to fit two product images with the specified margin.")
107
-
108
- # 첫 번째 제품 이미지의 왼쪽 상단 좌표 계산
109
- left_offset = (bg_width - total_width) // 2
110
- top_offset = (bg_height - product_height) // 2
111
-
112
- # 두 번째 제품 이미지의 왼쪽 상단 좌표 계산
113
- right_offset = left_offset + product_width + margin
114
-
115
- # 첫 번째 제품 이미지 배경에 합성
116
- background.paste(product, (left_offset, top_offset), mask=product)
117
-
118
- # 두 번째 제품 이미지 배경에 합성
119
- background.paste(product, (right_offset, top_offset), mask=product)
120
- return background
121
-
122
- except Exception as e:
123
- print(f"Failed to paste product images to background: {e}")
124
- return None
125
-
126
- def paste_to_background_type_c(background: Image.Image, product: Image.Image, margin: int = 10, top_margin: int = 15) -> Image.Image:
127
- try:
128
- # 배경 이미지 크기 가져오기
129
- bg_width, bg_height = background.size
130
-
131
- # 제품 이미지 크기 가져오기
132
- product_width, product_height = product.size
133
-
134
- # 먼저 type_b 형태로 아래 두 제품 이미지를 배치
135
- background_with_two_products = paste_to_background_type_b(background, product, margin)
136
-
137
- # 중앙 상단에 위치할 제품 이미지의 오프셋 계산
138
- center_offset_x = (bg_width - product_width) // 2
139
- center_offset_y = ((bg_height - product_height) // 2) + top_margin
140
-
141
- if center_offset_y < 0:
142
- raise ValueError("Background is too small to fit three product images with the specified top margin.")
143
-
144
- # 세 번째 제품 이미지 배경에 합성 (중앙 상단)
145
- background_with_two_products.paste(product, (center_offset_x, center_offset_y), mask=product)
146
-
147
- # 결과 이미지 저장 및 반환
148
- return background_with_two_products
149
-
150
- except Exception as e:
151
- print(f"Failed to paste product images to background: {e}")
152
- return None
153
-
154
- def create_zip_file(images: list) -> str:
155
- # 임시 파일 생성
156
- with tempfile.NamedTemporaryFile(delete=False, suffix=".zip") as temp_zip:
157
- with zipfile.ZipFile(temp_zip, 'w', zipfile.ZIP_DEFLATED) as zipf:
158
- for i, image in enumerate(images):
159
- # 이미지를 메모리 버퍼에 저장
160
- image_buffer = BytesIO()
161
- image.save(image_buffer, format="PNG")
162
- image_buffer.seek(0)
163
-
164
- # 버퍼 내용을 ZIP 파일에 추가
165
- zipf.writestr(f"image_{i + 1}.png", image_buffer.getvalue())
166
-
167
- # 임시 파일의 경로를 반환
168
- temp_zip_path = temp_zip.name
169
-
170
- return temp_zip_path
171
-
172
- def image_processing_single(background_image: Image.Image, product_image_url: str):
173
- # 각종 설정 값
174
- background_image_size = 1024 # 배경 이미지의 크기
175
- product_image_size = 650 # 배경이 제거된 제품 이미지의 크기
176
- type_b_margin = 15
177
- type_c_margin_1, type_c_margin_2 = 15, 45 # (1=가운데 두개의 마진, 2= 중앙 한개의 top 마진)
178
-
179
- # 배경 이미지 크기 조정
180
- background_image = background_image.resize((background_image_size, background_image_size))
181
-
182
- # 이미지 다운로드
183
- original_image = download_image(product_image_url)
184
-
185
- # 이미지 누끼따기
186
- removebg_image = remove_background(original_image)
187
-
188
- # 이미지 크롭
189
- cropped_image = crop_image(removebg_image)
190
-
191
- # 크롭된 이미지 원하는 사이즈로 resize
192
- resized_image = resize_image(cropped_image, product_image_size)
193
-
194
- # type_a 합성
195
- type_a_image = paste_to_background_type_a(background_image.copy(), resized_image)
196
- type_b_image = paste_to_background_type_b(background_image.copy(), resized_image, type_b_margin)
197
- type_c_image = paste_to_background_type_c(background_image.copy(), resized_image, type_c_margin_1, type_c_margin_2)
198
-
199
- # 결과 이미지 반환
200
- image_list = [type_a_image, type_b_image, type_c_image]
201
- zip_file_path = create_zip_file(image_list)
202
-
203
- return image_list, zip_file_path
204
-
205
- def image_processing_batch(background_image: Image.Image, product_image_url_file_path: str):
206
- # 각종 설정 값
207
- product_image_size = 650 # 배경이 제거된 제품 이미지의 크기
208
- type_b_margin = 15
209
- type_c_margin_1, type_c_margin_2 = 15, 45 # (1=가운데 두개의 마진, 2= 중앙 한개의 top 마진)
210
-
211
- # file to url
212
- def file_to_list(file_path: str) -> list:
213
- # 파일을 열고 읽기
214
- with open(file_path, "r") as f:
215
- # 파일 내용을 읽어서 줄바꿈(엔터)로 구분된 각 URL을 리스트로 변환
216
- content = f.read()
217
- # 줄바꿈으로 URL 분리
218
- url_list = [url.strip() for url in content.splitlines() if url.strip()]
219
-
220
- return url_list
221
-
222
- product_image_url_list = file_to_list(product_image_url_file_path)
223
-
224
- preview_image_list, image_list = [], []
225
- for idx, product_image_url in enumerate(product_image_url_list):
226
- # 이미지 다운로드
227
- original_image = download_image(product_image_url)
228
-
229
- # 이미지 누끼따기
230
- removebg_image = remove_background(original_image)
231
-
232
- # 이미지 크롭
233
- cropped_image = crop_image(removebg_image)
234
-
235
- # 크롭된 이미지 원하는 사이즈로 resize
236
- resized_image = resize_image(cropped_image, product_image_size)
237
-
238
- # type_a 합성
239
- type_a_image = paste_to_background_type_a(background_image.copy(), resized_image)
240
- type_b_image = paste_to_background_type_b(background_image.copy(), resized_image, type_b_margin)
241
- type_c_image = paste_to_background_type_c(background_image.copy(), resized_image, type_c_margin_1, type_c_margin_2)
242
-
243
- image_list.append(type_a_image)
244
- image_list.append(type_b_image)
245
- image_list.append(type_c_image)
246
- if idx == 0:
247
- preview_image_list = [type_a_image, type_b_image, type_c_image]
248
-
249
- zip_file_path = create_zip_file(image_list)
250
-
251
- return preview_image_list, zip_file_path
252
-
253
-
254
- with gr.Blocks(theme=gr.themes.Monochrome()) as demo:
255
  gr.HTML("""
256
  <h1>Product Image Generator</h1>
257
  <p>1. 배경 이미지를 업로드해주세요.<p>
 
 
1
  import gradio as gr
2
+ from infra import image_processing_single, image_processing_batch
 
 
 
 
 
3
 
4
+ with gr.Blocks(theme='NoCrypt/miku', title='Image Generator') as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  gr.HTML("""
6
  <h1>Product Image Generator</h1>
7
  <p>1. 배경 이미지를 업로드해주세요.<p>
infra.py ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import zipfile
3
+ import tempfile
4
+ from PIL import Image
5
+ from io import BytesIO
6
+ from rembg import remove
7
+
8
+ # 각종 설정 값
9
+ BACKGROUND_IMAGE_MAX_SIZE = 1024 # 배경 이미지의 크기
10
+ PRODUCT_IMAGE_MAX_SIZE = 650 # 배경이 제거된 제품 이미지의 크기
11
+ TYPD_B_MARGIN = 15
12
+ TYPE_C_MARGIN_1, TYPE_C_MARGIN_2 = 15, 45 # (1=가운데 두개의 마진, 2= 중앙 한개의 top 마진)
13
+
14
+
15
+
16
+ def download_image(image_url: str) -> Image.Image:
17
+ # 이미지 다운로드
18
+ response = requests.get(image_url)
19
+
20
+ if response.status_code == 200:
21
+ original_image = Image.open(BytesIO(response.content))
22
+ return original_image
23
+ else:
24
+ raise Exception(f"Failed to download image. Status code: {response.status_code}")
25
+
26
+ def remove_background(image: Image.Image) -> Image.Image:
27
+ # 이미지 누끼따기
28
+ try:
29
+ removebg_image = remove(
30
+ image,
31
+ post_process_mask=True,
32
+ alpha_matting=True,
33
+ alpha_matting_foreground_threshold=270,
34
+ alpha_matting_background_threshold=30,
35
+ alpha_matting_erode_size=15)
36
+ return removebg_image
37
+
38
+ except Exception as e:
39
+ print(f"Failed to remove background: {e}")
40
+ return None
41
+
42
+ def crop_image(image: Image.Image) -> Image.Image:
43
+ # 이미지 크롭
44
+ try:
45
+ # 알파 채널을 사용하여 이미지의 경계 영역 찾기
46
+ bbox = image.getbbox()
47
+ if bbox:
48
+ # 경계 상자로 이미지 크롭
49
+ cropped_image = image.crop(bbox)
50
+ return cropped_image
51
+ else:
52
+ print("No bounding box found.")
53
+ return image
54
+
55
+ except Exception as e:
56
+ print(f"Failed to crop image: {e}")
57
+ return None
58
+
59
+ def resize_image(image: Image.Image, max_size: int) -> Image.Image:
60
+ # 이미지 크기 조정
61
+ try:
62
+ # 이미지의 현재 너비와 높이 가져오기
63
+ width, height = image.size
64
+
65
+ # 너비와 높이 중 더 큰 쪽의 비율에 맞춰 크기를 조정
66
+ if width > height:
67
+ new_width = max_size
68
+ new_height = int((max_size / width) * height)
69
+ else:
70
+ new_height = max_size
71
+ new_width = int((max_size / height) * width)
72
+
73
+ resized_image = image.resize((new_width, new_height))
74
+ return resized_image
75
+
76
+ except Exception as e:
77
+ print(f"Failed to resize image: {e}")
78
+ return None
79
+
80
+ def paste_to_background_type_a(background: Image.Image, product: Image.Image) -> Image.Image:
81
+ # 배경에 제품 이미지 합성
82
+ try:
83
+ # 배경 이미지 크기 가져오기
84
+ bg_width, bg_height = background.size
85
+
86
+ # 제품 이미지 크기 가져오기
87
+ product_width, product_height = product.size
88
+
89
+ # 제품 이미지를 배경 이미지 중앙에 위치시키기
90
+ offset = ((bg_width - product_width) // 2, (bg_height - product_height) // 2)
91
+
92
+ # 알파 채널을 고려하여 합성
93
+ background.paste(product, offset, mask=product)
94
+ return background
95
+
96
+ except Exception as e:
97
+ print(f"Failed to paste product image to background: {e}")
98
+ return None
99
+
100
+ def paste_to_background_type_b(background: Image.Image, product: Image.Image, margin: int = 10) -> Image.Image:
101
+ try:
102
+ # 배경 이미지 크기 가져오기
103
+ bg_width, bg_height = background.size
104
+
105
+ # 제품 이미지 크기 가져오기
106
+ product_width, product_height = product.size
107
+
108
+ # 두 제품 이미지를 위한 전체 너비 계산 (제품 이미지 두 개 + 마진)
109
+ total_width = (product_width * 2) + margin
110
+
111
+ if total_width > bg_width:
112
+ raise ValueError("Background is too narrow to fit two product images with the specified margin.")
113
+
114
+ # 첫 번째 제품 이미지의 왼쪽 상단 좌표 계산
115
+ left_offset = (bg_width - total_width) // 2
116
+ top_offset = (bg_height - product_height) // 2
117
+
118
+ # 두 번째 제품 이미지의 왼쪽 상단 좌표 계산
119
+ right_offset = left_offset + product_width + margin
120
+
121
+ # 첫 번째 제품 이미지 배경에 합성
122
+ background.paste(product, (left_offset, top_offset), mask=product)
123
+
124
+ # 두 번째 제품 이미지 배경에 합성
125
+ background.paste(product, (right_offset, top_offset), mask=product)
126
+ return background
127
+
128
+ except Exception as e:
129
+ print(f"Failed to paste product images to background: {e}")
130
+ return None
131
+
132
+ def paste_to_background_type_c(background: Image.Image, product: Image.Image, margin: int = 10, top_margin: int = 15) -> Image.Image:
133
+ try:
134
+ # 배경 이미지 크기 가져오기
135
+ bg_width, bg_height = background.size
136
+
137
+ # 제품 이미지 크기 가져오기
138
+ product_width, product_height = product.size
139
+
140
+ # 먼저 type_b 형태로 아래 두 제품 이미지를 배치
141
+ background_with_two_products = paste_to_background_type_b(background, product, margin)
142
+
143
+ # 중앙 상단에 위치할 제품 이미지의 오프셋 계산
144
+ center_offset_x = (bg_width - product_width) // 2
145
+ center_offset_y = ((bg_height - product_height) // 2) + top_margin
146
+
147
+ if center_offset_y < 0:
148
+ raise ValueError("Background is too small to fit three product images with the specified top margin.")
149
+
150
+ # 세 번째 제품 이미지 배경에 합성 (중앙 상단)
151
+ background_with_two_products.paste(product, (center_offset_x, center_offset_y), mask=product)
152
+
153
+ # 결과 이미지 저장 및 반환
154
+ return background_with_two_products
155
+
156
+ except Exception as e:
157
+ print(f"Failed to paste product images to background: {e}")
158
+ return None
159
+
160
+ def create_zip_file(images: list) -> str:
161
+ # 임시 파일 생성
162
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".zip") as temp_zip:
163
+ with zipfile.ZipFile(temp_zip, 'w', zipfile.ZIP_DEFLATED) as zipf:
164
+ for i, image in enumerate(images):
165
+ # 이미지를 메모리 버퍼에 저장
166
+ image_buffer = BytesIO()
167
+ image.save(image_buffer, format="PNG")
168
+ image_buffer.seek(0)
169
+
170
+ # 버퍼 내용을 ZIP 파일에 추가
171
+ zipf.writestr(f"image_{i + 1}.png", image_buffer.getvalue())
172
+
173
+ # 임시 파일의 경로를 반환
174
+ temp_zip_path = temp_zip.name
175
+
176
+ return temp_zip_path
177
+
178
+ def image_processing_single(background_image: Image.Image, product_image_url: str):
179
+ # 배경 이미지 크기 조정
180
+ background_image = background_image.resize((BACKGROUND_IMAGE_MAX_SIZE, BACKGROUND_IMAGE_MAX_SIZE))
181
+
182
+ # 이미지 다운로드
183
+ original_image = download_image(product_image_url)
184
+
185
+ # 이미지 누끼따기
186
+ removebg_image = remove_background(original_image)
187
+
188
+ # 이미지 크롭
189
+ cropped_image = crop_image(removebg_image)
190
+
191
+ # 크롭된 이미지 원하는 사이즈로 resize
192
+ resized_image = resize_image(cropped_image, PRODUCT_IMAGE_MAX_SIZE)
193
+
194
+ # type_a 합성
195
+ type_a_image = paste_to_background_type_a(background_image.copy(), resized_image)
196
+ type_b_image = paste_to_background_type_b(background_image.copy(), resized_image, TYPD_B_MARGIN)
197
+ type_c_image = paste_to_background_type_c(background_image.copy(), resized_image, TYPE_C_MARGIN_1, TYPE_C_MARGIN_2)
198
+
199
+ # 결과 이미지 반환
200
+ image_list = [type_a_image, type_b_image, type_c_image]
201
+ zip_file_path = create_zip_file(image_list)
202
+
203
+ return image_list, zip_file_path
204
+
205
+ def image_processing_batch(background_image: Image.Image, product_image_url_file_path: str):
206
+ # 배경 이미지 크기 조정
207
+ background_image = background_image.resize((BACKGROUND_IMAGE_MAX_SIZE, BACKGROUND_IMAGE_MAX_SIZE))
208
+
209
+ # file to url
210
+ def file_to_list(file_path: str) -> list:
211
+ # 파일을 열고 읽기
212
+ with open(file_path, "r") as f:
213
+ # 파일 내용을 읽어서 줄바꿈(엔터)로 구분된 각 URL을 리스트로 변환
214
+ content = f.read()
215
+ # 줄바꿈으로 URL 분리
216
+ url_list = [url.strip() for url in content.splitlines() if url.strip()]
217
+
218
+ return url_list
219
+
220
+ product_image_url_list = file_to_list(product_image_url_file_path)
221
+
222
+ preview_image_list, image_list = [], []
223
+ for idx, product_image_url in enumerate(product_image_url_list):
224
+ # 이미지 다운로드
225
+ original_image = download_image(product_image_url)
226
+
227
+ # 이미지 누끼따기
228
+ removebg_image = remove_background(original_image)
229
+
230
+ # 이미지 크롭
231
+ cropped_image = crop_image(removebg_image)
232
+
233
+ # 크롭된 이미지 원하는 사이즈로 resize
234
+ resized_image = resize_image(cropped_image, PRODUCT_IMAGE_MAX_SIZE)
235
+
236
+ # type_a 합성
237
+ type_a_image = paste_to_background_type_a(background_image.copy(), resized_image)
238
+ type_b_image = paste_to_background_type_b(background_image.copy(), resized_image, TYPD_B_MARGIN)
239
+ type_c_image = paste_to_background_type_c(background_image.copy(), resized_image, TYPE_C_MARGIN_1, TYPE_C_MARGIN_2)
240
+
241
+ image_list.append(type_a_image)
242
+ image_list.append(type_b_image)
243
+ image_list.append(type_c_image)
244
+ if idx == 0:
245
+ preview_image_list = [type_a_image, type_b_image, type_c_image]
246
+
247
+ zip_file_path = create_zip_file(image_list)
248
+
249
+ return preview_image_list, zip_file_path
250
+