300 lines
12 KiB
Python
Executable File
300 lines
12 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
기존 클라이언트 호환성 테스트 스크립트
|
|
기존 클라이언트 코드가 수정 없이 동작하는지 확인합니다.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import base64
|
|
import cv2
|
|
import numpy as np
|
|
import requests
|
|
import logging
|
|
import time
|
|
from typing import Optional
|
|
|
|
# 프로젝트 루트를 Python 경로에 추가
|
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')))
|
|
|
|
class LegacyClientSimulator:
|
|
"""기존 클라이언트 코드를 시뮬레이션하는 클래스"""
|
|
|
|
def __init__(self, base_url: str = "http://localhost:8008"):
|
|
self.inpaint_base_url = base_url
|
|
self.rembg_base_url = base_url
|
|
self.inpaint_api_url = f"{base_url}/api/v1/inpaint"
|
|
self.rembg_api_url = f"{base_url}/api/v1/remove_bg"
|
|
|
|
# 로거 설정
|
|
self.logger = logging.getLogger(__name__)
|
|
handler = logging.StreamHandler()
|
|
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
handler.setFormatter(formatter)
|
|
self.logger.addHandler(handler)
|
|
self.logger.setLevel(logging.INFO)
|
|
|
|
def is_server_alive(self, base_url: str) -> bool:
|
|
"""서버 상태 확인"""
|
|
try:
|
|
response = requests.get(f"{base_url}/health", timeout=5)
|
|
return response.status_code == 200
|
|
except Exception:
|
|
return False
|
|
|
|
def request_inpaint(self, image: np.ndarray, mask: np.ndarray) -> Optional[np.ndarray]:
|
|
"""기존 인페인팅 클라이언트 코드 시뮬레이션"""
|
|
try:
|
|
# 서버 상태 먼저 확인
|
|
if not self.is_server_alive(self.inpaint_base_url):
|
|
self.logger.log("인페인팅 서버가 비정상입니다. 백업 인페인팅으로 넘어갑니다.", level=logging.WARNING)
|
|
return None
|
|
|
|
image_data = image
|
|
|
|
# 이미지를 base64로 인코딩
|
|
_, img_encoded = cv2.imencode('.png', image_data)
|
|
_, mask_encoded = cv2.imencode('.png', mask)
|
|
img_b64 = base64.b64encode(img_encoded).decode('utf-8')
|
|
mask_b64 = base64.b64encode(mask_encoded).decode('utf-8')
|
|
|
|
# 기존 클라이언트는 model_name을 포함하지 않음
|
|
payload = {
|
|
"image": img_b64,
|
|
"mask": mask_b64
|
|
}
|
|
|
|
# 기존 클라이언트는 바이너리 응답을 기대함
|
|
response = requests.post(self.inpaint_api_url, json=payload, timeout=(5, 45))
|
|
|
|
if response.status_code != 200:
|
|
print("인페인팅 서버 에러:", response.text)
|
|
return None
|
|
|
|
# 응답이 바이너리 PNG 이미지이므로 바로 디코딩
|
|
nparr = np.frombuffer(response.content, np.uint8)
|
|
result = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
|
|
return result
|
|
|
|
except (requests.exceptions.Timeout, requests.exceptions.ConnectionError, requests.exceptions.RequestException) as e:
|
|
self.logger.log(f"인페인팅 서버 요청 실패: {e}, 백업 모듈로 전환", level=logging.WARNING)
|
|
return None # 백업으로 넘어가도록
|
|
except Exception as e:
|
|
self.logger.log(f"인페인팅 서버 에러: {e}", level=logging.ERROR, exc_info=True)
|
|
return None
|
|
|
|
def request_rembg(self, image: np.ndarray) -> Optional[np.ndarray]:
|
|
"""기존 배경 제거 클라이언트 코드 시뮬레이션"""
|
|
try:
|
|
# 서버 상태 먼저 확인
|
|
if not self.is_server_alive(self.rembg_base_url):
|
|
self.logger.log("rembg 서버가 비정상입니다. 백업 내장 rembg 모듈을 사용합니다.", level=logging.WARNING)
|
|
return None
|
|
|
|
# base64 인코딩
|
|
_, img_encoded = cv2.imencode('.png', image)
|
|
img_b64 = base64.b64encode(img_encoded).decode('utf-8')
|
|
|
|
# 기존 클라이언트는 model_name을 포함하지 않음
|
|
payload = {
|
|
"image": img_b64
|
|
}
|
|
|
|
response = requests.post(self.rembg_api_url, json=payload, timeout=(5, 45))
|
|
|
|
if response.status_code != 200:
|
|
self.logger.log(f"rembg 서버 에러: {response.text}. 백업 내장 rembg 모듈을 사용합니다.", level=logging.WARNING)
|
|
return None
|
|
|
|
# PNG 바이너리 → numpy
|
|
nparr = np.frombuffer(response.content, np.uint8)
|
|
result_img = cv2.imdecode(nparr, cv2.IMREAD_UNCHANGED)
|
|
|
|
if result_img is None or result_img.ndim != 3:
|
|
self.logger.log("서버 응답 이미지 디코딩 실패. 백업 내장 rembg 모듈을 사용합니다.", level=logging.WARNING)
|
|
return None
|
|
|
|
return result_img
|
|
|
|
except (requests.exceptions.Timeout, requests.exceptions.ConnectionError, requests.exceptions.RequestException) as e:
|
|
self.logger.log(f"rembg 서버 요청 실패: {e}, 백업 내장 rembg 모듈로 전환", level=logging.WARNING)
|
|
return None
|
|
except Exception as e:
|
|
self.logger.log(f"request_rembg 실행 중 오류: {e}. 백업 내장 rembg 모듈을 사용합니다.", level=logging.WARNING, exc_info=True)
|
|
return None
|
|
|
|
|
|
def create_test_images():
|
|
"""테스트용 이미지와 마스크 생성"""
|
|
# 테스트 이미지 생성 (300x300 컬러 이미지)
|
|
image = np.zeros((300, 300, 3), dtype=np.uint8)
|
|
# 빨간 사각형
|
|
cv2.rectangle(image, (50, 50), (150, 150), (0, 0, 255), -1)
|
|
# 파란 원
|
|
cv2.circle(image, (200, 200), 50, (255, 0, 0), -1)
|
|
|
|
# 테스트 마스크 생성 (빨간 사각형 부분을 제거)
|
|
mask = np.zeros((300, 300), dtype=np.uint8)
|
|
cv2.rectangle(mask, (50, 50), (150, 150), 255, -1)
|
|
|
|
return image, mask
|
|
|
|
|
|
def test_legacy_compatibility():
|
|
"""기존 클라이언트 호환성 테스트"""
|
|
print("🧪 기존 클라이언트 호환성 테스트 시작")
|
|
print("=" * 50)
|
|
|
|
# 클라이언트 시뮬레이터 생성
|
|
client = LegacyClientSimulator()
|
|
|
|
# 테스트 이미지 생성
|
|
test_image, test_mask = create_test_images()
|
|
|
|
# 서버 상태 확인
|
|
print("1. 서버 상태 확인...")
|
|
if not client.is_server_alive("http://localhost:8008"):
|
|
print("❌ 서버가 실행되지 않았습니다. 먼저 서버를 시작하세요.")
|
|
print(" ./scripts/start_server.sh")
|
|
return False
|
|
print("✅ 서버가 정상적으로 실행 중입니다.")
|
|
|
|
success_count = 0
|
|
total_tests = 0
|
|
|
|
# 인페인팅 테스트
|
|
print("\n2. 인페인팅 API 호환성 테스트...")
|
|
total_tests += 1
|
|
try:
|
|
start_time = time.time()
|
|
result = client.request_inpaint(test_image, test_mask)
|
|
end_time = time.time()
|
|
|
|
if result is not None and isinstance(result, np.ndarray):
|
|
print(f"✅ 인페인팅 테스트 성공 (처리 시간: {end_time - start_time:.2f}초)")
|
|
print(f" - 입력 이미지 크기: {test_image.shape}")
|
|
print(f" - 출력 이미지 크기: {result.shape}")
|
|
print(f" - model_name 누락 처리: ✅ (자동으로 'simple-lama' 사용)")
|
|
print(f" - 바이너리 PNG 응답: ✅")
|
|
success_count += 1
|
|
else:
|
|
print("❌ 인페인팅 테스트 실패: 결과가 None이거나 올바르지 않습니다.")
|
|
except Exception as e:
|
|
print(f"❌ 인페인팅 테스트 실패: {e}")
|
|
|
|
# 배경 제거 테스트
|
|
print("\n3. 배경 제거 API 호환성 테스트...")
|
|
total_tests += 1
|
|
try:
|
|
start_time = time.time()
|
|
result = client.request_rembg(test_image)
|
|
end_time = time.time()
|
|
|
|
if result is not None and isinstance(result, np.ndarray):
|
|
print(f"✅ 배경 제거 테스트 성공 (처리 시간: {end_time - start_time:.2f}초)")
|
|
print(f" - 입력 이미지 크기: {test_image.shape}")
|
|
print(f" - 출력 이미지 크기: {result.shape}")
|
|
print(f" - model_name 누락 처리: ✅ (자동으로 'rembg' 사용)")
|
|
print(f" - 바이너리 PNG 응답: ✅")
|
|
success_count += 1
|
|
else:
|
|
print("❌ 배경 제거 테스트 실패: 결과가 None이거나 올바르지 않습니다.")
|
|
except Exception as e:
|
|
print(f"❌ 배경 제거 테스트 실패: {e}")
|
|
|
|
# 결과 출력
|
|
print("\n" + "=" * 50)
|
|
print("🎯 테스트 결과")
|
|
print("=" * 50)
|
|
print(f"성공: {success_count}/{total_tests}")
|
|
print(f"성공률: {success_count/total_tests*100:.1f}%")
|
|
|
|
if success_count == total_tests:
|
|
print("🎉 모든 테스트가 성공했습니다!")
|
|
print(" 기존 클라이언트 코드를 수정 없이 그대로 사용할 수 있습니다.")
|
|
return True
|
|
else:
|
|
print("⚠️ 일부 테스트가 실패했습니다.")
|
|
print(" 서버 로그를 확인하거나 다시 시도해보세요.")
|
|
return False
|
|
|
|
|
|
def test_new_features():
|
|
"""새로운 기능 테스트 (선택사항)"""
|
|
print("\n🚀 새로운 기능 테스트")
|
|
print("=" * 50)
|
|
|
|
test_image, test_mask = create_test_images()
|
|
|
|
# WebP 형식 테스트
|
|
print("1. WebP 형식 응답 테스트...")
|
|
try:
|
|
_, img_encoded = cv2.imencode('.png', test_image)
|
|
_, mask_encoded = cv2.imencode('.png', test_mask)
|
|
img_b64 = base64.b64encode(img_encoded).decode('utf-8')
|
|
mask_b64 = base64.b64encode(mask_encoded).decode('utf-8')
|
|
|
|
payload = {
|
|
"image": img_b64,
|
|
"mask": mask_b64,
|
|
"model_name": "simple-lama"
|
|
}
|
|
|
|
response = requests.post(
|
|
"http://localhost:8008/api/v1/inpaint?response_format=binary&image_format=webp",
|
|
json=payload,
|
|
timeout=(5, 45)
|
|
)
|
|
|
|
if response.status_code == 200 and response.headers.get('content-type') == 'image/webp':
|
|
print("✅ WebP 형식 응답 성공")
|
|
else:
|
|
print(f"❌ WebP 형식 응답 실패: {response.status_code}")
|
|
except Exception as e:
|
|
print(f"❌ WebP 테스트 실패: {e}")
|
|
|
|
# JSON 응답 테스트
|
|
print("\n2. JSON 응답 형식 테스트...")
|
|
try:
|
|
response = requests.post(
|
|
"http://localhost:8008/api/v1/inpaint?response_format=json",
|
|
json=payload,
|
|
timeout=(5, 45)
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
if data.get('success') and data.get('image'):
|
|
print("✅ JSON 응답 형식 성공")
|
|
print(f" - 처리 시간: {data.get('processing_time', 0):.2f}초")
|
|
else:
|
|
print("❌ JSON 응답 형식 실패: 잘못된 응답 구조")
|
|
else:
|
|
print(f"❌ JSON 응답 형식 실패: {response.status_code}")
|
|
except Exception as e:
|
|
print(f"❌ JSON 테스트 실패: {e}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print("🔧 인페인팅 서버 호환성 테스트")
|
|
print("=" * 50)
|
|
print("이 스크립트는 기존 클라이언트 코드가 수정 없이 동작하는지 확인합니다.")
|
|
print("")
|
|
|
|
# 기존 클라이언트 호환성 테스트
|
|
compatibility_success = test_legacy_compatibility()
|
|
|
|
# 새로운 기능 테스트 (선택사항)
|
|
if compatibility_success:
|
|
test_new_features()
|
|
|
|
print("\n" + "=" * 50)
|
|
print("테스트 완료!")
|
|
|
|
if compatibility_success:
|
|
print("🎉 기존 클라이언트와 완벽하게 호환됩니다!")
|
|
sys.exit(0)
|
|
else:
|
|
print("⚠️ 호환성 문제가 발견되었습니다.")
|
|
sys.exit(1)
|