Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
@@ -163,15 +163,20 @@ def gd_detect(img: Image.Image, prompt: str) -> BoundingBox | None:
|
|
163 |
return bbox_union(bboxes.numpy().tolist())
|
164 |
|
165 |
def apply_mask(img: Image.Image, mask_img: Image.Image, defringe: bool = True) -> Image.Image:
|
|
|
166 |
assert img.size == mask_img.size
|
167 |
img = img.convert("RGB")
|
168 |
mask_img = mask_img.convert("L")
|
|
|
169 |
if defringe:
|
170 |
rgb, alpha = np.asarray(img) / 255.0, np.asarray(mask_img) / 255.0
|
171 |
foreground = cast(np.ndarray[Any, np.dtype[np.uint8]], estimate_foreground_ml(rgb, alpha))
|
172 |
img = Image.fromarray((foreground * 255).astype("uint8"))
|
173 |
-
|
|
|
|
|
174 |
result.paste(img, (0, 0), mask_img)
|
|
|
175 |
return result
|
176 |
|
177 |
|
@@ -270,13 +275,27 @@ def combine_with_background(foreground: Image.Image, background: Image.Image,
|
|
270 |
"""전경과 배경 합성 함수"""
|
271 |
print(f"Combining with position: {position}, scale: {scale_percent}")
|
272 |
|
|
|
273 |
result = background.convert('RGBA')
|
|
|
|
|
|
|
|
|
|
|
|
|
274 |
scaled_foreground = resize_object(foreground, scale_percent)
|
275 |
|
|
|
276 |
x, y = calculate_object_position(position, result.size, scaled_foreground.size)
|
277 |
print(f"Calculated position coordinates: ({x}, {y})")
|
278 |
|
279 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
280 |
return result
|
281 |
|
282 |
@spaces.GPU(duration=30) # 120초에서 30초로 감소
|
@@ -346,7 +365,7 @@ def on_change_prompt(img: Image.Image | None, prompt: str | None, bg_prompt: str
|
|
346 |
return gr.update(interactive=bool(img and prompt))
|
347 |
|
348 |
|
349 |
-
|
350 |
aspect_ratio: str = "1:1", position: str = "bottom-center",
|
351 |
scale_percent: float = 100) -> tuple[Image.Image, Image.Image]:
|
352 |
try:
|
@@ -364,6 +383,9 @@ def process_prompt(img: Image.Image, prompt: str, bg_prompt: str | None = None,
|
|
364 |
|
365 |
results, _ = _process(img, prompt, bg_prompt, aspect_ratio)
|
366 |
|
|
|
|
|
|
|
367 |
if bg_prompt:
|
368 |
try:
|
369 |
print(f"Using position: {position}")
|
@@ -375,18 +397,23 @@ def process_prompt(img: Image.Image, prompt: str, bg_prompt: str | None = None,
|
|
375 |
print(f"Invalid position, using default: {position}")
|
376 |
|
377 |
combined = combine_with_background(
|
378 |
-
foreground=
|
379 |
background=results[1],
|
380 |
position=position,
|
381 |
scale_percent=scale_percent
|
382 |
)
|
383 |
-
|
384 |
-
|
|
|
385 |
except Exception as e:
|
386 |
print(f"Combination error: {str(e)}")
|
387 |
-
return results[1].convert('RGB'),
|
388 |
|
389 |
-
|
|
|
|
|
|
|
|
|
390 |
except Exception as e:
|
391 |
print(f"Error in process_prompt: {str(e)}")
|
392 |
raise gr.Error(str(e))
|
@@ -888,20 +915,6 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
|
888 |
outputs=position
|
889 |
)
|
890 |
|
891 |
-
process_btn.click(
|
892 |
-
fn=process_prompt,
|
893 |
-
inputs=[
|
894 |
-
input_image,
|
895 |
-
text_prompt,
|
896 |
-
bg_prompt,
|
897 |
-
aspect_ratio,
|
898 |
-
position,
|
899 |
-
scale_slider
|
900 |
-
],
|
901 |
-
outputs=[combined_image, extracted_image],
|
902 |
-
queue=True,
|
903 |
-
show_progress=True
|
904 |
-
)
|
905 |
|
906 |
add_text_btn.click(
|
907 |
fn=add_text_to_image,
|
@@ -931,9 +944,6 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
|
931 |
)
|
932 |
|
933 |
|
934 |
-
|
935 |
-
|
936 |
-
# 이벤트 바인딩
|
937 |
bg_prompt.change(
|
938 |
fn=update_controls,
|
939 |
inputs=bg_prompt,
|
@@ -956,13 +966,35 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
|
956 |
)
|
957 |
|
958 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
959 |
|
960 |
-
|
961 |
-
|
962 |
-
demo.queue(max_size=5)
|
963 |
demo.launch(
|
964 |
server_name="0.0.0.0",
|
965 |
server_port=7860,
|
966 |
share=False,
|
967 |
-
max_threads=
|
968 |
-
|
|
|
|
|
|
|
|
163 |
return bbox_union(bboxes.numpy().tolist())
|
164 |
|
165 |
def apply_mask(img: Image.Image, mask_img: Image.Image, defringe: bool = True) -> Image.Image:
|
166 |
+
"""마스크 적용 함수"""
|
167 |
assert img.size == mask_img.size
|
168 |
img = img.convert("RGB")
|
169 |
mask_img = mask_img.convert("L")
|
170 |
+
|
171 |
if defringe:
|
172 |
rgb, alpha = np.asarray(img) / 255.0, np.asarray(mask_img) / 255.0
|
173 |
foreground = cast(np.ndarray[Any, np.dtype[np.uint8]], estimate_foreground_ml(rgb, alpha))
|
174 |
img = Image.fromarray((foreground * 255).astype("uint8"))
|
175 |
+
|
176 |
+
# 투명한 배경으로 결과 생성
|
177 |
+
result = Image.new("RGBA", img.size, (0, 0, 0, 0))
|
178 |
result.paste(img, (0, 0), mask_img)
|
179 |
+
|
180 |
return result
|
181 |
|
182 |
|
|
|
275 |
"""전경과 배경 합성 함수"""
|
276 |
print(f"Combining with position: {position}, scale: {scale_percent}")
|
277 |
|
278 |
+
# 배경 이미지를 RGBA 모드로 변환
|
279 |
result = background.convert('RGBA')
|
280 |
+
|
281 |
+
# 전경 이미지가 RGBA가 아니면 변환
|
282 |
+
if foreground.mode != 'RGBA':
|
283 |
+
foreground = foreground.convert('RGBA')
|
284 |
+
|
285 |
+
# 스케일 조정
|
286 |
scaled_foreground = resize_object(foreground, scale_percent)
|
287 |
|
288 |
+
# 위치 계산
|
289 |
x, y = calculate_object_position(position, result.size, scaled_foreground.size)
|
290 |
print(f"Calculated position coordinates: ({x}, {y})")
|
291 |
|
292 |
+
# 투명한 배경의 새 이미지 생성
|
293 |
+
temp = Image.new('RGBA', result.size, (0, 0, 0, 0))
|
294 |
+
temp.paste(scaled_foreground, (x, y), scaled_foreground)
|
295 |
+
|
296 |
+
# 최종 합성
|
297 |
+
result = Image.alpha_composite(result, temp)
|
298 |
+
|
299 |
return result
|
300 |
|
301 |
@spaces.GPU(duration=30) # 120초에서 30초로 감소
|
|
|
365 |
return gr.update(interactive=bool(img and prompt))
|
366 |
|
367 |
|
368 |
+
ddef process_prompt(img: Image.Image, prompt: str, bg_prompt: str | None = None,
|
369 |
aspect_ratio: str = "1:1", position: str = "bottom-center",
|
370 |
scale_percent: float = 100) -> tuple[Image.Image, Image.Image]:
|
371 |
try:
|
|
|
383 |
|
384 |
results, _ = _process(img, prompt, bg_prompt, aspect_ratio)
|
385 |
|
386 |
+
# 추출된 이미지를 RGBA로 유지
|
387 |
+
extracted = results[2].convert('RGBA')
|
388 |
+
|
389 |
if bg_prompt:
|
390 |
try:
|
391 |
print(f"Using position: {position}")
|
|
|
397 |
print(f"Invalid position, using default: {position}")
|
398 |
|
399 |
combined = combine_with_background(
|
400 |
+
foreground=extracted,
|
401 |
background=results[1],
|
402 |
position=position,
|
403 |
scale_percent=scale_percent
|
404 |
)
|
405 |
+
|
406 |
+
# 최종 출력을 위해 RGB로 변환
|
407 |
+
return combined.convert('RGB'), extracted.convert('RGB')
|
408 |
except Exception as e:
|
409 |
print(f"Combination error: {str(e)}")
|
410 |
+
return results[1].convert('RGB'), extracted.convert('RGB')
|
411 |
|
412 |
+
# 배경이 없는 경우
|
413 |
+
white_bg = Image.new('RGBA', extracted.size, (255, 255, 255, 255))
|
414 |
+
combined = Image.alpha_composite(white_bg, extracted)
|
415 |
+
return combined.convert('RGB'), extracted.convert('RGB')
|
416 |
+
|
417 |
except Exception as e:
|
418 |
print(f"Error in process_prompt: {str(e)}")
|
419 |
raise gr.Error(str(e))
|
|
|
915 |
outputs=position
|
916 |
)
|
917 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
918 |
|
919 |
add_text_btn.click(
|
920 |
fn=add_text_to_image,
|
|
|
944 |
)
|
945 |
|
946 |
|
|
|
|
|
|
|
947 |
bg_prompt.change(
|
948 |
fn=update_controls,
|
949 |
inputs=bg_prompt,
|
|
|
966 |
)
|
967 |
|
968 |
|
969 |
+
process_btn.click(
|
970 |
+
fn=process_prompt,
|
971 |
+
inputs=[
|
972 |
+
input_image,
|
973 |
+
text_prompt,
|
974 |
+
bg_prompt,
|
975 |
+
aspect_ratio,
|
976 |
+
position,
|
977 |
+
scale_slider
|
978 |
+
],
|
979 |
+
outputs=[combined_image, extracted_image],
|
980 |
+
queue=True,
|
981 |
+
show_progress=True
|
982 |
+
).success(
|
983 |
+
fn=None,
|
984 |
+
_js="""
|
985 |
+
function() {
|
986 |
+
document.querySelector('.output-panel').scrollIntoView({behavior: 'smooth'});
|
987 |
+
}
|
988 |
+
"""
|
989 |
+
)
|
990 |
|
991 |
+
demo.queue(max_size=10, concurrency_count=2)
|
|
|
|
|
992 |
demo.launch(
|
993 |
server_name="0.0.0.0",
|
994 |
server_port=7860,
|
995 |
share=False,
|
996 |
+
max_threads=4,
|
997 |
+
show_error=True,
|
998 |
+
prevent_thread_lock=True
|
999 |
+
)
|
1000 |
+
|