Spaces:
Running
Running
import cv2 | |
import numpy as np | |
from PIL import Image, ImageEnhance, ImageFilter | |
import gradio as gr | |
from io import BytesIO | |
import tempfile | |
import logging | |
# 로깅 설정 - INFO 레벨로 변경 | |
logging.basicConfig(level=logging.INFO) | |
logger = logging.getLogger(__name__) | |
def adjust_brightness(image, value): | |
"""이미지 밝기 조절""" | |
value = float(value - 1) * 100 # 0-2 범위를 -100에서 +100으로 변환 | |
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) | |
h, s, v = cv2.split(hsv) | |
v = cv2.add(v, value) | |
v = np.clip(v, 0, 255) | |
final_hsv = cv2.merge((h, s, v)) | |
return cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR) | |
def adjust_contrast(image, value): | |
"""이미지 대비 조절""" | |
value = float(value) | |
return np.clip(image * value, 0, 255).astype(np.uint8) | |
def adjust_saturation(image, value): | |
"""이미지 채도 조절""" | |
value = float(value - 1) * 100 # 0-2 범위를 -100에서 +100으로 변환 | |
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) | |
h, s, v = cv2.split(hsv) | |
s = cv2.add(s, value) | |
s = np.clip(s, 0, 255) | |
final_hsv = cv2.merge((h, s, v)) | |
return cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR) | |
def adjust_temperature(image, value): | |
"""이미지 색온도 조절 (색상 밸런스)""" | |
value = float(value) * 30 # 효과 스케일 조절 | |
b, g, r = cv2.split(image) | |
if value > 0: # 따뜻하게 | |
r = cv2.add(r, value) | |
b = cv2.subtract(b, value) | |
else: # 차갑게 | |
r = cv2.add(r, value) | |
b = cv2.subtract(b, value) | |
r = np.clip(r, 0, 255) | |
b = np.clip(b, 0, 255) | |
return cv2.merge([b, g, r]) | |
def adjust_tint(image, value): | |
"""이미지 색조 조절""" | |
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) | |
h, s, v = cv2.split(hsv_image) | |
h = cv2.add(h, int(value)) | |
h = np.clip(h, 0, 179) # Hue 값은 0-179 범위 | |
final_hsv = cv2.merge((h, s, v)) | |
return cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR) | |
def adjust_exposure(image, value): | |
"""이미지 노출 조절""" | |
enhancer = ImageEnhance.Brightness(Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))) | |
img_enhanced = enhancer.enhance(1 + float(value) / 5.0) | |
return cv2.cvtColor(np.array(img_enhanced), cv2.COLOR_RGB2BGR) | |
def adjust_vibrance(image, value): | |
"""이미지 활기 조절""" | |
img = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) | |
converter = ImageEnhance.Color(img) | |
factor = 1 + (float(value) / 100.0) | |
img = converter.enhance(factor) | |
return cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) | |
def adjust_color_mixer_blues(image, value): | |
"""이미지 컬러 믹서 (블루) 조절""" | |
b, g, r = cv2.split(image) | |
b = cv2.add(b, float(value)) | |
b = np.clip(b, 0, 255) | |
return cv2.merge([b, g, r]) | |
def adjust_shadows(image, value): | |
"""이미지 그림자 조절""" | |
pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) | |
enhancer = ImageEnhance.Brightness(pil_image) | |
factor = 1 + (float(value) / 100.0) | |
pil_image = enhancer.enhance(factor) | |
return cv2.cvtColor(np.array(pil_image), cv2.COLOR_BGR2RGB) | |
def process_image(image, brightness, contrast, saturation, temperature, tint, exposure, vibrance, color_mixer_blues, shadows): | |
"""모든 조정 사항을 이미지에 적용""" | |
if image is None: | |
return None | |
# PIL 이미지를 OpenCV 형식으로 변환 | |
image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR) | |
# 조정 사항 순차 적용 | |
image = adjust_brightness(image, brightness) | |
image = adjust_contrast(image, contrast) | |
image = adjust_saturation(image, saturation) | |
image = adjust_temperature(image, temperature) | |
image = adjust_tint(image, tint) | |
image = adjust_exposure(image, exposure) | |
image = adjust_vibrance(image, vibrance) | |
image = adjust_color_mixer_blues(image, color_mixer_blues) | |
image = adjust_shadows(image, shadows) | |
# PIL 이미지로 다시 변환 | |
return Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) | |
def download_image(image, input_image_name): | |
"""이미지를 JPG 형식으로 저장하고 경로 반환""" | |
if image is None: | |
return None | |
# 한국 시간 타임스탬프 생성 함수 | |
from datetime import datetime, timedelta | |
def get_korean_timestamp(): | |
korea_time = datetime.utcnow() + timedelta(hours=9) | |
return korea_time.strftime('%Y%m%d_%H%M%S') | |
timestamp = get_korean_timestamp() | |
if input_image_name and hasattr(input_image_name, 'name'): | |
base_name = input_image_name.name.split('.')[0] # 파일 객체에서 이름 추출 | |
else: | |
base_name = "이미지" | |
file_name = f"[끝장AI]끝장필터_{base_name}_{timestamp}.jpg" | |
# 파일 저장 | |
temp_file_path = tempfile.gettempdir() + "/" + file_name | |
image.save(temp_file_path, format="JPEG") | |
return temp_file_path | |
def create_interface(): | |
css = """ | |
footer { | |
visibility: hidden; | |
} | |
.download-button, .download-output { | |
width: 100%; | |
} | |
.download-container { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
width: 100%; | |
} | |
#gradio-app { | |
margin: 0 !important; /* 모든 방향 여백 제거 */ | |
text-align: left !important; /* 왼쪽 정렬 강제 */ | |
padding: 20px !important; /* 패딩 추가 */ | |
} | |
.gradio-container { | |
max-width: 100% !important; /* 가로 폭 전체 사용 */ | |
margin-left: 0 !important; /* 왼쪽 정렬 */ | |
padding: 20px !important; /* 패딩 추가 */ | |
} | |
.download-button { | |
background-color: black !important; | |
color: white !important; | |
border: none !important; | |
padding: 10px !important; | |
font-size: 16px !important; | |
} | |
""" | |
with gr.Blocks(theme=gr.themes.Soft( | |
primary_hue=gr.themes.Color( | |
c50="#FFF7ED", # 가장 밝은 주황 | |
c100="#FFEDD5", | |
c200="#FED7AA", | |
c300="#FDBA74", | |
c400="#FB923C", | |
c500="#F97316", # 기본 주황 | |
c600="#EA580C", | |
c700="#C2410C", | |
c800="#9A3412", | |
c900="#7C2D12", # 가장 어두운 주황 | |
c950="#431407", | |
), | |
secondary_hue="zinc", # 모던한 느낌의 회색 계열 | |
neutral_hue="zinc", | |
font=("Pretendard", "sans-serif") | |
), css=css) as interface: | |
with gr.Row(): | |
# 왼쪽 열: 비율 3 | |
with gr.Column(scale=3): | |
# 이미지 업로드 | |
input_image = gr.Image(type="pil", label="이미지 업로드") | |
# 조정 슬라이더 | |
brightness_slider = gr.Slider(0.0, 2.0, value=1.0, step=0.1, label="밝기 조절") | |
contrast_slider = gr.Slider(0.5, 1.5, value=1.0, step=0.1, label="대비 조절") | |
saturation_slider = gr.Slider(0.0, 2.0, value=1.0, step=0.1, label="채도 조절") | |
temperature_slider = gr.Slider(-1.0, 1.0, value=0.0, step=0.1, label="색온도 조절") | |
tint_slider = gr.Slider(-100, 100, value=0, step=1, label="색조 조절") | |
exposure_slider = gr.Slider(-5.0, 5.0, value=0.0, step=0.1, label="노출 조절") | |
vibrance_slider = gr.Slider(-100.0, 100.0, value=0.0, step=1.0, label="활기 조절") | |
color_mixer_blues_slider = gr.Slider(-100.0, 100.0, value=0.0, step=1.0, label="컬러 믹서 (블루)") | |
shadows_slider = gr.Slider(-100.0, 100.0, value=0.0, step=1.0, label="그림자 조절") | |
# 오른쪽 열: 비율 7 | |
with gr.Column(scale=7): | |
# 처리된 이미지 출력 | |
output_image = gr.Image(type="pil", label="처리된 이미지") | |
# 변환된 이미지 다운로드 버튼 | |
with gr.Row(elem_classes="download-container"): | |
download_button = gr.Button("JPG로 변환하기", elem_classes="download-button") | |
with gr.Row(elem_classes="download-container"): | |
download_output = gr.File(label="JPG 이미지 다운로드", elem_classes="download-output") | |
# 이미지 처리 함수 연결 | |
inputs = [ | |
input_image, | |
brightness_slider, | |
contrast_slider, | |
saturation_slider, | |
temperature_slider, | |
tint_slider, | |
exposure_slider, | |
vibrance_slider, | |
color_mixer_blues_slider, | |
shadows_slider | |
] | |
input_components = [ | |
brightness_slider, | |
contrast_slider, | |
saturation_slider, | |
temperature_slider, | |
tint_slider, | |
exposure_slider, | |
vibrance_slider, | |
color_mixer_blues_slider, | |
shadows_slider | |
] | |
for input_component in input_components: | |
input_component.change( | |
fn=process_image, | |
inputs=inputs, | |
outputs=output_image | |
) | |
# 다운로드 버튼 기능 | |
download_button.click( | |
fn=download_image, | |
inputs=[output_image, input_image], | |
outputs=download_output | |
) | |
return interface | |
# 인터페이스 생성 및 실행 | |
if __name__ == "__main__": | |
logger.info("애플리케이션 시작") | |
interface = create_interface() | |
interface.queue() | |
interface.launch() | |