인페인팅 방법을 MIGAN으로 변경하고 관련 설정 추가. TensorRT 최적화 및 메모리 관리 기능 개선. Dockerfile 및 의존성 파일 업데이트. 전체적인 코드 정리 및 주석 보강.
This commit is contained in:
parent
1b72426779
commit
e3bd4bb50b
|
|
@ -1,5 +1,6 @@
|
|||
# CUDA 11.8 + Ubuntu 22.04 (cudnn 필요 없으면 -runtime- 만 사용)
|
||||
#FROM nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04
|
||||
#FROM nvcr.io/nvidia/tensorrt:23.10-py3
|
||||
|
||||
FROM nvidia/cuda:11.8.0-cudnn8-devel-ubuntu22.04
|
||||
|
||||
# ---------- 시스템 의존성 (Pango/Cairo/HarfBuzz/Fonts 포함) ----------
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
# CUDA 11.8 + Ubuntu 22.04 + cuDNN8 + TensorRT 8.6 + Python3.10
|
||||
FROM nvcr.io/nvidia/tensorrt:23.10-py3
|
||||
|
||||
#FROM nvidia/cuda:11.8.0-cudnn8-devel-ubuntu22.04
|
||||
|
||||
# ---------- 시스템 의존성 (Pango/Cairo/HarfBuzz/Fonts 포함) ----------
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
python3.10 python3-pip python3.10-distutils \
|
||||
# OpenCV 런타임
|
||||
libgl1-mesa-glx libgl1-mesa-dri libglib2.0-0 libgomp1 \
|
||||
libsm6 libxext6 libxrender1 \
|
||||
cuda-cudart-dev-11-8 \
|
||||
# Pango / Cairo / HarfBuzz / GI 바인딩
|
||||
libcairo2 libcairo2-dev \
|
||||
libpango-1.0-0 libpangocairo-1.0-0 libpangoft2-1.0-0 \
|
||||
libharfbuzz0b libharfbuzz-icu0 \
|
||||
gobject-introspection libgirepository1.0-dev \
|
||||
python3-gi python3-gi-cairo \
|
||||
# 글꼴과 폰트 설정
|
||||
fontconfig fonts-nanum fonts-noto-cjk fonts-dejavu-core \
|
||||
# 기타
|
||||
pkg-config \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# ---------- Python 기본 설정 ----------
|
||||
RUN ln -sf /usr/bin/python3.10 /usr/local/bin/python && \
|
||||
python -m pip install --upgrade pip
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# ---------- 파이썬 패키지 ----------
|
||||
COPY requirements_worker.txt .
|
||||
COPY packages_worker/fastdeploy_gpu_python-1.0.7-cp310-cp310-manylinux1_x86_64.whl .
|
||||
# constraints 복사
|
||||
COPY constraints.txt .
|
||||
|
||||
RUN pip install --no-cache-dir --progress-bar off \
|
||||
--index-url https://download.pytorch.org/whl/cu118 \
|
||||
torch==2.3.1 torchvision==0.18.1 torchaudio==2.3.1
|
||||
RUN pip install --no-cache-dir --progress-bar off -r requirements_worker.txt -c constraints.txt
|
||||
RUN pip install --no-cache-dir --progress-bar off fastdeploy_gpu_python-1.0.7-cp310-cp310-manylinux1_x86_64.whl
|
||||
|
||||
# ---------- 메모리 추척 ----------
|
||||
RUN pip install --no-cache-dir --progress-bar off pynvml
|
||||
|
||||
# ---------- 프로젝트 파일 ----------
|
||||
COPY worker /app/worker
|
||||
# COPY fonts /app/fonts
|
||||
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV TEMP_STORAGE=/app/temp_files
|
||||
RUN mkdir -p $TEMP_STORAGE
|
||||
|
||||
# (선택) TensorRT 런타임이 정말 필요하면 NVIDIA 레포 추가 후 libnvinfer8 설치 필요.
|
||||
# 없다면 FastDeploy가 자동으로 Paddle Inference로 폴백하니 생략해도 동작은 함.
|
||||
# 성능 이유로 TRT를 쓰려면 별도 설치 가이드대로 세팅 권장.
|
||||
|
||||
# ---------- Celery 엔트리포인트 ----------
|
||||
# ★ 중요: -A 경로를 실제 celery_app 이 있는 모듈:변수 로 맞추세요.
|
||||
# 예) celery_worker.py 안에 celery_app = Celery(...) 라면:
|
||||
CMD ["celery", "-A", "worker.celery_worker:celery_app", "worker", "-l", "info", "--concurrency=1"]
|
||||
|
||||
|
|
@ -31,10 +31,18 @@ requests==2.32.3
|
|||
# loguru==0.7.2 # 사용시
|
||||
python-dotenv==1.0.1 # .env 파싱시
|
||||
|
||||
# onnxruntime
|
||||
# ONNX Runtime (MIGAN 인페인팅용)
|
||||
onnxruntime-gpu==1.16.3
|
||||
onnx==1.14.0
|
||||
|
||||
# 🔥 MIGAN 인페인팅 추가 패키지
|
||||
# TensorRT (성능 최적화용 - NVIDIA GPU 환경)
|
||||
# tensorrt>=8.6.0 # TensorRT 라이브러리 (별도 설치 필요)
|
||||
|
||||
# 텍스트 렌더링 품질 개선 (Cairo/Pango 경고 해결)
|
||||
pycairo>=1.20.0 # Cairo 그래픽 라이브러리
|
||||
PyGObject>=3.42.0 # GTK/Pango 바인딩
|
||||
|
||||
# Background removal
|
||||
rembg[gpu]==2.0.67
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 951 KiB |
|
|
@ -37,14 +37,24 @@ unwanted_texts = {
|
|||
toggle_states = {"inpaint_method": "lama:cuda", "min_masks_for_lama": 2, 'title': False, 'title_shuffle': False, 'title_trans_type': False, 'collect_method_combo': '쇼핑API', 'ocr': True, 'unwanted_words': {'할인': '', '무료': '', '증정': '', '이벤트': '', '특가': '', '세일': '', '사은품': '', '보증': '', '품절': '', '행사': '', '할인가': '', '무료배송': '', '가격설명': ''}, 'interval': 3.0, 'watingTime': 20, 'memo': False, 'memo_toggle_exposer': False, 'memo_toggle_order': False, 'optionTrnas': True, 'optionTrnas_method': True, 'optionIMGTrans': True, 'optionIMGTrans_type': '자체서버', 'optionAutoSelect': True, 'price': False, 'tag': False, 'tag_ai': False, 'thumb': False, 'thumb_trans_type': 'CPU', 'thumb_nukki': False, 'remove_background_white': True, 'detail_Option': False, 'detail_IMGTrans': True, 'detail_IMGTrans_type': '자체서버', 'debug_mode': True, 'ed_mode': False, 'discord': False, 'is_localServer': False, 'watermark_toggle': False, 'clientID': '', 'clientSecret': '', 'discord_webhook': '', 'watermark_text': '', 'thumb_rmb_count': 3, 'max_option_count': 6, 'opacity_percent': 20, 'group_index': 4, 'remove_overprice': False, 'cat_rec': False, 'fixed_keywords': False, 'fixed_keywords_count': 2, 'title_length_limit': 27, 'base_dir': 'C:\\Program Files\\Edit_PartTimer\\lib\\src', 'TEMP_IMAGE_DIR': 'C:\\Program Files\\Edit_PartTimer\\lib\\src\\temp_images', 'ERROR_SCREENSHOT_DIR': 'C:\\Program Files\\Edit_PartTimer\\lib\\src\\error_screenshots', 'image_font_path': 'C:\\Program Files\\Edit_PartTimer\\lib\\src\\fonts\\HakgyoansimDunggeunmisoTTFB.ttf', 'watermark_font_path': 'C:\\Program Files\\Edit_PartTimer\\lib\\src\\fonts\\HakgyoansimDunggeunmisoTTFB.ttf', 'request_inpainting_server_url': 'http://171.101.232.45:50205', 'request_rembg_server_url': 'http://171.101.232.45:50205', 'request_rembg_server_url_local': 'http://192.168.0.150:35756', 'membership_level': 'premium', 'image_worker_restart_every': 10, 'image_worker_restart_count': 0, 'products_per_context_restart': 19, 'is_admin': False, 'admin_id': 'matia0514@naver.com', 'admin_pw': '', 'user_id': 'dreamm8985', 'user_pw': '112233', 'unwanted_words_button': False, 'font_type': '폰트5', 'cmb_button': False, 'detail_text_button': False, 'watermark': False}
|
||||
|
||||
toggle_states.update({
|
||||
"use_roi_optimized_mask": True, # True: 새 방식, False: 기존 방식
|
||||
"enable_mask_refinement": False, # ROI 마스크 정제 비활성화
|
||||
"context_expansion_ratio": 0.4, # 최소 확장
|
||||
"blend_mode": "simple", # 단순 블렌딩
|
||||
"performance_mode": True, # 빠른 경로 사용
|
||||
"max_image_size": 1280, # 더 작은 크기 제한
|
||||
"roi_area_high": 0.0 # 기본값: 0.60 → 0.0으로 변경 # 풀프레임 인페인팅 강제
|
||||
|
||||
# 🔥 MIGAN 인페인팅 사용 설정
|
||||
"inpaint_method": "migan", # ROI → MIGAN 변경
|
||||
"migan_use_cuda": True, # CUDA 사용
|
||||
"migan_use_tensorrt": True, # TensorRT 최적화
|
||||
"migan_trt_fp16_enable": True, # FP16 활성화
|
||||
"migan_trt_engine_cache_enable": True, # 엔진 캐시 활성화
|
||||
"migan_max_image_size": 1600, # 최대 이미지 크기
|
||||
"migan_output_max_width": 800, # 출력 최대 가로
|
||||
"migan_enable_output_resize": True, # 출력 리사이즈 활성화
|
||||
|
||||
# 기존 ROI 설정들 (MIGAN에서는 사용되지 않지만 호환성 유지)
|
||||
"use_roi_optimized_mask": True, # True: 새 방식, False: 기존 방식
|
||||
"enable_mask_refinement": False, # ROI 마스크 정제 비활성화
|
||||
"context_expansion_ratio": 0.4, # 최소 확장
|
||||
"blend_mode": "simple", # 단순 블렌딩
|
||||
"performance_mode": True, # 빠른 경로 사용
|
||||
"max_image_size": 1280, # 더 작은 크기 제한
|
||||
"roi_area_high": 0.0 # 기본값: 0.60 → 0.0으로 변경 # 풀프레임 인페인팅 강제
|
||||
})
|
||||
|
||||
def call_translate(img_path: pathlib.Path):
|
||||
|
|
@ -58,7 +68,8 @@ def call_translate(img_path: pathlib.Path):
|
|||
"toggle_states": json.dumps(toggle_states, ensure_ascii=False),
|
||||
"unwanted_texts": json.dumps(unwanted_texts, ensure_ascii=False),
|
||||
"ocr_method": "paddleocr",
|
||||
"inpaint_method": "lama",
|
||||
# "inpaint_method": "lama", # ← 이 줄을 제거하거나
|
||||
"inpaint_method": "migan", # ← migan으로 변경
|
||||
"sync": "true",
|
||||
"wait": "90",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,306 @@
|
|||
# MIGAN 인페인팅 모듈 설정 안내
|
||||
|
||||
## 개요
|
||||
MIGAN ONNX 파이프라인을 사용한 전체 이미지 인페인팅 모듈이 추가되었습니다. 기존 ROI 기반 인페인팅과 함께 사용할 수 있으며, `toggle_states`를 통해 선택할 수 있습니다.
|
||||
|
||||
## 모델 파일 설정
|
||||
|
||||
### 1. 모델 디렉토리 생성
|
||||
```bash
|
||||
mkdir -p worker/models
|
||||
```
|
||||
|
||||
### 2. MIGAN 모델 파일 배치
|
||||
`migan_pipeline_v2.onnx` 파일을 다음 경로에 배치하세요:
|
||||
```
|
||||
worker/models/migan_pipeline_v2.onnx
|
||||
```
|
||||
|
||||
### 3. TensorRT 캐시 디렉토리 (선택사항)
|
||||
TensorRT 엔진 캐시를 위한 디렉토리:
|
||||
```bash
|
||||
mkdir -p /app/temp_files/trt_cache
|
||||
```
|
||||
|
||||
## 사용 방법
|
||||
|
||||
### 인페인팅 방법 선택
|
||||
`toggle_states`에서 `inpaint_method` 키를 통해 인페인팅 방법을 선택할 수 있습니다:
|
||||
|
||||
```python
|
||||
# ROI 기반 인페인팅 (기본값)
|
||||
toggle_states = {
|
||||
"inpaint_method": "roi"
|
||||
}
|
||||
|
||||
# MIGAN 전체 이미지 인페인팅
|
||||
toggle_states = {
|
||||
"inpaint_method": "migan"
|
||||
}
|
||||
```
|
||||
|
||||
### MIGAN 설정 옵션
|
||||
|
||||
#### 기본 설정
|
||||
```python
|
||||
toggle_states = {
|
||||
"inpaint_method": "migan",
|
||||
"migan_onnx_path": "/app/worker/models/migan_pipeline_v2.onnx",
|
||||
"migan_use_cuda": True,
|
||||
"migan_use_tensorrt": True,
|
||||
"migan_trt_fp16_enable": True,
|
||||
"migan_trt_engine_cache_enable": True,
|
||||
"migan_max_image_size": 1600,
|
||||
"migan_output_max_width": 800,
|
||||
"migan_enable_output_resize": True
|
||||
}
|
||||
```
|
||||
|
||||
#### 설정 옵션 설명
|
||||
|
||||
**ONNX 모델 설정:**
|
||||
- `migan_onnx_path`: ONNX 모델 파일 경로
|
||||
- `migan_use_cuda`: CUDA 사용 여부 (기본: True)
|
||||
- `migan_intra_threads`: ONNX Runtime 스레드 수 (기본: 0 = 자동)
|
||||
- `migan_inter_threads`: ONNX Runtime 스레드 수 (기본: 0 = 자동)
|
||||
|
||||
**TensorRT 최적화:**
|
||||
- `migan_use_tensorrt`: TensorRT 사용 여부 (기본: True)
|
||||
- `migan_trt_fp16_enable`: FP16 최적화 활성화 (기본: True)
|
||||
- `migan_trt_engine_cache_enable`: 엔진 캐시 활성화 (기본: True)
|
||||
|
||||
**이미지 처리:**
|
||||
- `migan_max_image_size`: 최대 이미지 크기 제한 (긴 변 기준, 기본: 1600px)
|
||||
- `migan_output_max_width`: 출력 최대 가로 크기 (기본: 800px)
|
||||
- `migan_enable_output_resize`: 출력 크기 조정 활성화 (기본: True)
|
||||
|
||||
#### 직접 모듈 사용 (개발/테스트용)
|
||||
```python
|
||||
from worker.migan_inpainting_module import MIGANInpaintingModule
|
||||
|
||||
# ⚠️ 프로덕션에서는 celery_worker의 전역 인스턴스 사용 권장
|
||||
inpainter = MIGANInpaintingModule(config={
|
||||
'use_cuda': True,
|
||||
'max_image_size': 1600
|
||||
})
|
||||
|
||||
# 인페인팅 실행
|
||||
result = inpainter.inpaint_with_migan(image, mask)
|
||||
# cleanup_memory() 호출하지 말 것 - 전역 인스턴스는 계속 유지
|
||||
```
|
||||
|
||||
#### 전역 인스턴스 사용 (권장)
|
||||
```python
|
||||
# celery_worker.py에서 자동으로 처리됨
|
||||
# toggle_states만 설정하면 됨
|
||||
|
||||
toggle_states = {
|
||||
"inpaint_method": "migan",
|
||||
"migan_use_tensorrt": True,
|
||||
"migan_trt_fp16_enable": True,
|
||||
"migan_max_image_size": 2048
|
||||
}
|
||||
|
||||
# worker가 자동으로 전역 인스턴스를 사용하고 설정을 동적 업데이트
|
||||
```
|
||||
|
||||
## 주요 특징
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### MIGAN vs ROI 인페인팅 비교
|
||||
|
||||
| 항목 | MIGAN | ROI |
|
||||
|------|-------|-----|
|
||||
| 처리 방식 | 전체 이미지 | 영역별 분할 |
|
||||
| 전처리 | 모델 내장 | 외부 처리 |
|
||||
| ROI 분석 | 불필요 | 필수 |
|
||||
| 메모리 사용 | 고정적 | 적응적 |
|
||||
| 속도 | 일정 | 영역 수에 따라 변동 |
|
||||
| 품질 | 일관적 | 영역별 최적화 |
|
||||
|
||||
### MIGAN 모듈 장점
|
||||
1. **내장 전처리**: 리사이즈, 정규화 등이 ONNX 모델에 포함
|
||||
2. **TensorRT 최적화**: FP16, 엔진 캐시를 통한 성능 향상
|
||||
3. **세션 재사용**: 메모리 효율성 및 초기화 시간 단축
|
||||
4. **자동 폴백**: MIGAN 실패 시 ROI 방식으로 자동 전환
|
||||
5. **크기 제한**: 메모리 사용량 제어를 위한 이미지 크기 제한
|
||||
6. **🔥 전역 인스턴스**: 모델은 한 번만 로딩되어 메모리에 상주
|
||||
7. **🔥 동적 설정**: 실행 중 설정 변경 가능 (필요시 세션 재생성)
|
||||
|
||||
## 환경 변수
|
||||
|
||||
```bash
|
||||
# TensorRT 엔진 캐시 디렉토리 (선택사항)
|
||||
export TRT_ENGINE_CACHE_DIR="/app/temp_files/trt_cache"
|
||||
|
||||
# 디버그 아티팩트 저장 디렉토리
|
||||
export DEBUG_DUMP_DIR="/app/temp_files/debug"
|
||||
```
|
||||
|
||||
## 로그 분석
|
||||
|
||||
### 정상 처리 로그 예시
|
||||
```
|
||||
[MIGAN] ONNX Runtime 세션 생성 완료: ['TensorrtExecutionProvider', 'CUDAExecutionProvider', 'CPUExecutionProvider']
|
||||
[INPAINT] MIGAN 처리 통계: 마스크 커버리지 15.3%, 최대 크기 제한: 1600px
|
||||
이미지 스케일링: 2000x1500 → 1600x1200 (factor=0.800)
|
||||
MIGAN 인페인팅 완료: 총 1.234s (추론: 0.987s)
|
||||
```
|
||||
|
||||
### 에러 및 폴백 로그 예시
|
||||
```
|
||||
[MIGAN] ONNX 파일을 찾을 수 없습니다: /app/worker/models/migan_pipeline_v2.onnx
|
||||
[INPAINT] MIGAN 인페인팅 실패, ROI로 폴백: FileNotFoundError
|
||||
```
|
||||
|
||||
## 메모리 관리 및 성능
|
||||
|
||||
### 전역 인스턴스 방식
|
||||
- **모델 로딩**: 워커 프로세스 시작 시 한 번만 로딩
|
||||
- **메모리 상주**: 모델이 메모리에 계속 유지되어 초기화 오버헤드 없음
|
||||
- **세션 재사용**: ONNX Runtime 세션이 캐시되어 반복 사용
|
||||
- **동적 설정**: 필요시에만 세션 재생성 (경로 변경 등)
|
||||
|
||||
### 주의사항
|
||||
- `cleanup_memory()` 메소드를 전역 인스턴스에서 호출하지 마세요
|
||||
- 프로덕션 환경에서는 celery_worker의 전역 인스턴스를 사용하세요
|
||||
- 개발/테스트에서만 직접 인스턴스 생성을 사용하세요
|
||||
|
||||
## 성능 최적화 팁
|
||||
|
||||
1. **TensorRT 활용**: GPU가 있는 환경에서는 TensorRT를 활성화하여 성능 향상
|
||||
2. **엔진 캐시**: 첫 실행 후 생성되는 TensorRT 엔진을 캐시하여 재시작 시 빠른 로딩
|
||||
3. **크기 제한**: 메모리 부족 시 `migan_max_image_size`를 조정하여 처리 가능한 크기로 제한
|
||||
4. **출력 크기**: 클라이언트 요구사항에 맞춰 `migan_output_max_width` 조정
|
||||
|
||||
## 문제 해결
|
||||
|
||||
### 일반적인 문제들
|
||||
|
||||
1. **모델 파일 없음**
|
||||
- 에러: `FileNotFoundError: ONNX 파일을 찾을 수 없습니다`
|
||||
- 해결: `worker/models/migan_pipeline_v2.onnx` 파일 확인
|
||||
|
||||
2. **CUDA 메모리 부족**
|
||||
- 에러: GPU 메모리 관련 오류
|
||||
- 해결: `migan_max_image_size` 값을 줄이거나 `migan_use_cuda: False`로 설정
|
||||
|
||||
3. **TensorRT 초기화 실패**
|
||||
- 에러: TensorRT Provider 초기화 실패
|
||||
- 해결: `migan_use_tensorrt: False`로 설정하여 CUDA Provider만 사용
|
||||
|
||||
4. **세션 생성 실패**
|
||||
- 에러: ONNX Runtime 세션 생성 실패
|
||||
- 해결: CPU 모드로 폴백 (`migan_use_cuda: False`)
|
||||
|
||||
### 디버깅
|
||||
|
||||
1. **로그 레벨 조정**: 디버그 로그를 활성화하여 상세 정보 확인
|
||||
2. **아티팩트 저장**: 디버그 이미지를 저장하여 각 단계별 결과 확인
|
||||
3. **성능 모니터링**: GPU 메모리 사용량 및 처리 시간 모니터링
|
||||
|
||||
## 업데이트 및 확장
|
||||
|
||||
새로운 MIGAN 모델 버전을 사용하려면:
|
||||
1. 새 ONNX 파일을 `worker/models/` 디렉토리에 배치
|
||||
2. `toggle_states`에서 `migan_onnx_path` 업데이트
|
||||
3. 필요시 입출력 형태에 맞춰 모듈 코드 수정
|
||||
|
|
@ -48,6 +48,7 @@ from worker.loggerModule import Logger
|
|||
# from worker.inpaint_module import Inpainter, InpaintBackends
|
||||
from worker.utils_debug import save_debug_artifacts, draw_ocr_overlay
|
||||
from worker.roi_inpainting_module import ROIInpaintingModule
|
||||
from worker.migan_inpainting_module import MIGANInpaintingModule
|
||||
|
||||
# from deep_translator import GoogleTranslator
|
||||
|
||||
|
|
@ -117,6 +118,7 @@ _mask: MaskModule | None = None
|
|||
_text: TextRenderingModule | None = None
|
||||
# _inpainter: Inpainter | None = None
|
||||
_roi_inpainter: ROIInpaintingModule | None = None
|
||||
_migan_inpainter: MIGANInpaintingModule | None = None
|
||||
_translator = get_translator() # ✅ 워커 부팅 시 생성 & 재사용
|
||||
|
||||
# def get_lama():
|
||||
|
|
@ -170,6 +172,15 @@ def get_roi_inpainter():
|
|||
_gpu_tracker.log_snapshot(tag="after ROI Inpainter init")
|
||||
return _roi_inpainter
|
||||
|
||||
def get_migan_inpainter():
|
||||
"""MIGAN 인페인팅 모듈 초기화 및 반환"""
|
||||
global _migan_inpainter
|
||||
if _migan_inpainter is None:
|
||||
_migan_inpainter = MIGANInpaintingModule(logger=clogger)
|
||||
# MIGAN 인페인팅 초기화 직후 VRAM 스냅샷
|
||||
_gpu_tracker.log_snapshot(tag="after MIGAN Inpainter init")
|
||||
return _migan_inpainter
|
||||
|
||||
# 번역기 워밍업
|
||||
try:
|
||||
_ = _translator.translate_batch(["测试"], src="zh-CN", dest="ko")
|
||||
|
|
@ -211,6 +222,9 @@ def _warm_up_models(**_):
|
|||
roi_inpainter = get_roi_inpainter()
|
||||
roi_inpainter._get_simple_lama() # SimpleLama 사전 로딩
|
||||
|
||||
# 🔥 MIGAN 인페인팅 모듈 사전 초기화 (옵션)
|
||||
get_migan_inpainter() # 필요 시 주석 해제
|
||||
|
||||
logger.info("✅ 모델 사전 로딩 완료 (성능 최적화 포함)")
|
||||
|
||||
except Exception as e:
|
||||
|
|
@ -251,6 +265,54 @@ def _parse_font_number_from_toggle(toggle_states: Dict[str, Any]) -> int | None:
|
|||
logger.warning(f"[font] font_type 파싱 실패: {e}")
|
||||
return None
|
||||
|
||||
def _parse_inpaint_method(toggle_states: Dict[str, Any]) -> str:
|
||||
"""
|
||||
toggle_states에서 인페인팅 방법을 파싱
|
||||
|
||||
Returns:
|
||||
'roi': ROI 기반 인페인팅 (기본값)
|
||||
'migan': MIGAN 전체 이미지 인페인팅
|
||||
"""
|
||||
method = str(toggle_states.get("inpaint_method", "roi")).lower().strip()
|
||||
|
||||
# MIGAN 방식 키워드들
|
||||
migan_keywords = ["migan", "mi-gan", "full", "full_image", "onnx"]
|
||||
|
||||
if any(keyword in method for keyword in migan_keywords):
|
||||
return "migan"
|
||||
|
||||
# 기본값은 ROI
|
||||
return "roi"
|
||||
|
||||
def _lightweight_memory_cleanup(trace_id: Optional[str] = None):
|
||||
"""
|
||||
경량 메모리 정리 - 모델은 유지하고 중간 처리 데이터만 정리
|
||||
|
||||
Args:
|
||||
trace_id: 트레이스 ID (로깅용)
|
||||
"""
|
||||
try:
|
||||
import gc
|
||||
import torch
|
||||
|
||||
# Python 가비지 컬렉션
|
||||
collected = gc.collect()
|
||||
|
||||
# GPU 캐시 정리 (모델은 유지됨)
|
||||
gpu_freed = 0
|
||||
if torch.cuda.is_available():
|
||||
gpu_freed = torch.cuda.memory_allocated() / 1024 / 1024 # MB
|
||||
torch.cuda.empty_cache()
|
||||
gpu_freed = gpu_freed - (torch.cuda.memory_allocated() / 1024 / 1024)
|
||||
|
||||
clogger.log(
|
||||
f"[MEMORY][{trace_id or ''}] 경량 정리 완료: Python객체={collected}개, GPU캐시={gpu_freed:.1f}MB",
|
||||
level=logging.DEBUG
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
clogger.log(f"[MEMORY][{trace_id or ''}] 메모리 정리 중 오류: {e}", level=logging.WARNING)
|
||||
|
||||
# def _parse_inpaint_backend(
|
||||
# toggle_states: Dict[str, Any],
|
||||
# *,
|
||||
|
|
@ -570,41 +632,97 @@ def translate_task(self, *, image_b64: str, filename: str,
|
|||
|
||||
|
||||
with track_phase("INPAINT", trace_id):
|
||||
# 🔥 ROI 기반 인페인팅 최적화 (모듈화)
|
||||
roi_inpainter = get_roi_inpainter()
|
||||
# 🔥 인페인팅 방법 선택
|
||||
inpaint_method = _parse_inpaint_method(toggle_states)
|
||||
clogger.log(f"[TRACE][{trace_id}] 선택된 인페인팅 방법: {inpaint_method}", level=logging.INFO)
|
||||
|
||||
# ROI 처리 설정 (toggle_states에서 오버라이드 가능)
|
||||
roi_config = {
|
||||
'min_component_area': toggle_states.get('min_component_area', 100),
|
||||
'merge_distance': toggle_states.get('merge_distance', 50),
|
||||
'margin_ratio': toggle_states.get('margin_ratio', 0.15),
|
||||
'large_mask_threshold': toggle_states.get('large_mask_threshold', 0.5),
|
||||
# 🔥 마스크 정제 비활성화 (마스크 모듈에서 이미 최적화됨)
|
||||
'enable_mask_refinement': toggle_states.get('enable_mask_refinement', False),
|
||||
'mask_erosion_kernel': 0, # 비활성화
|
||||
'mask_dilation_kernel': 0, # 비활성화
|
||||
'mask_blur_kernel': 0, # 비활성화
|
||||
'context_expansion_ratio': toggle_states.get('context_expansion_ratio', 0.1), # 줄임
|
||||
'blend_mode': toggle_states.get('blend_mode', 'simple'), # 단순 블렌딩
|
||||
'feather_blend_size': toggle_states.get('feather_blend_size', 5), # 줄임
|
||||
# 🔥 형상 최적화 설정
|
||||
'enable_shape_optimization': toggle_states.get('enable_shape_optimization', True),
|
||||
'performance_tracking': toggle_states.get('performance_tracking', True),
|
||||
}
|
||||
|
||||
# 처리 전 통계 로깅
|
||||
stats = roi_inpainter.get_processing_stats(src_bgr, mask)
|
||||
clogger.log(
|
||||
f"[INPAINT] 처리 통계: {stats['num_components']}개 컴포넌트 → "
|
||||
f"{stats['num_merged_rois']}개 ROI, 메모리 효율성: {stats['memory_efficiency']*100:.1f}%",
|
||||
level=logging.INFO
|
||||
)
|
||||
|
||||
# ROI 기반 인페인팅 실행
|
||||
dst_bgr = roi_inpainter.inpaint_with_roi(src_bgr, mask, config=roi_config)
|
||||
|
||||
# 메모리 정리
|
||||
roi_inpainter.cleanup_memory()
|
||||
if inpaint_method == "migan":
|
||||
# 🔥 MIGAN 전체 이미지 인페인팅
|
||||
try:
|
||||
migan_inpainter = get_migan_inpainter() # 전역 인스턴스 재사용
|
||||
|
||||
# MIGAN 설정 오버라이드 (toggle_states에서)
|
||||
migan_config = {
|
||||
'onnx_path': toggle_states.get('migan_onnx_path', '/app/worker/models/migan_pipeline_v2.onnx'),
|
||||
'use_cuda': toggle_states.get('migan_use_cuda', True),
|
||||
'use_tensorrt': toggle_states.get('migan_use_tensorrt', True),
|
||||
'trt_fp16_enable': toggle_states.get('migan_trt_fp16_enable', True),
|
||||
'trt_engine_cache_enable': toggle_states.get('migan_trt_engine_cache_enable', True),
|
||||
'max_image_size': int(toggle_states.get('migan_max_image_size', 1600)),
|
||||
'output_max_width': int(toggle_states.get('migan_output_max_width', 800)),
|
||||
'enable_output_resize': toggle_states.get('migan_enable_output_resize', True),
|
||||
}
|
||||
|
||||
# 처리 전 통계 로깅
|
||||
stats = migan_inpainter.get_processing_stats(src_bgr, mask)
|
||||
clogger.log(
|
||||
f"[INPAINT] MIGAN 처리 통계: 마스크 커버리지 {stats['mask_coverage_ratio']*100:.1f}%, "
|
||||
f"최대 크기 제한: {stats['max_size_limit']}px",
|
||||
level=logging.INFO
|
||||
)
|
||||
|
||||
# MIGAN 인페인팅 실행 (전역 인스턴스, 메모리 정리 안함)
|
||||
dst_bgr = migan_inpainter.inpaint_with_migan(src_bgr, mask, config=migan_config)
|
||||
|
||||
# 🔥 경량 메모리 정리 - 중간 처리 데이터만 정리, 모델은 유지
|
||||
_lightweight_memory_cleanup(trace_id)
|
||||
|
||||
except Exception as e:
|
||||
clogger.log(f"[INPAINT] MIGAN 인페인팅 실패, ROI로 폴백: {e}", level=logging.WARNING)
|
||||
# MIGAN 실패 시 ROI로 폴백
|
||||
roi_inpainter = get_roi_inpainter()
|
||||
roi_config = {
|
||||
'min_component_area': toggle_states.get('min_component_area', 100),
|
||||
'merge_distance': toggle_states.get('merge_distance', 50),
|
||||
'margin_ratio': toggle_states.get('margin_ratio', 0.15),
|
||||
'large_mask_threshold': toggle_states.get('large_mask_threshold', 0.5),
|
||||
'enable_mask_refinement': toggle_states.get('enable_mask_refinement', False),
|
||||
'context_expansion_ratio': toggle_states.get('context_expansion_ratio', 0.1),
|
||||
'blend_mode': toggle_states.get('blend_mode', 'simple'),
|
||||
'feather_blend_size': toggle_states.get('feather_blend_size', 5),
|
||||
'enable_shape_optimization': toggle_states.get('enable_shape_optimization', True),
|
||||
'performance_tracking': toggle_states.get('performance_tracking', True),
|
||||
}
|
||||
dst_bgr = roi_inpainter.inpaint_with_roi(src_bgr, mask, config=roi_config)
|
||||
# 🔥 경량 메모리 정리 추가
|
||||
_lightweight_memory_cleanup(trace_id)
|
||||
|
||||
else:
|
||||
# 🔥 ROI 기반 인페인팅 (기본값)
|
||||
roi_inpainter = get_roi_inpainter()
|
||||
|
||||
# ROI 처리 설정 (toggle_states에서 오버라이드 가능)
|
||||
roi_config = {
|
||||
'min_component_area': toggle_states.get('min_component_area', 100),
|
||||
'merge_distance': toggle_states.get('merge_distance', 50),
|
||||
'margin_ratio': toggle_states.get('margin_ratio', 0.15),
|
||||
'large_mask_threshold': toggle_states.get('large_mask_threshold', 0.5),
|
||||
# 🔥 마스크 정제 비활성화 (마스크 모듈에서 이미 최적화됨)
|
||||
'enable_mask_refinement': toggle_states.get('enable_mask_refinement', False),
|
||||
'mask_erosion_kernel': 0, # 비활성화
|
||||
'mask_dilation_kernel': 0, # 비활성화
|
||||
'mask_blur_kernel': 0, # 비활성화
|
||||
'context_expansion_ratio': toggle_states.get('context_expansion_ratio', 0.1), # 줄임
|
||||
'blend_mode': toggle_states.get('blend_mode', 'simple'), # 단순 블렌딩
|
||||
'feather_blend_size': toggle_states.get('feather_blend_size', 5), # 줄임
|
||||
# 🔥 형상 최적화 설정
|
||||
'enable_shape_optimization': toggle_states.get('enable_shape_optimization', True),
|
||||
'performance_tracking': toggle_states.get('performance_tracking', True),
|
||||
}
|
||||
|
||||
# 처리 전 통계 로깅
|
||||
stats = roi_inpainter.get_processing_stats(src_bgr, mask)
|
||||
clogger.log(
|
||||
f"[INPAINT] ROI 처리 통계: {stats['num_components']}개 컴포넌트 → "
|
||||
f"{stats['num_merged_rois']}개 ROI, 메모리 효율성: {stats['memory_efficiency']*100:.1f}%",
|
||||
level=logging.INFO
|
||||
)
|
||||
|
||||
# ROI 기반 인페인팅 실행
|
||||
dst_bgr = roi_inpainter.inpaint_with_roi(src_bgr, mask, config=roi_config)
|
||||
|
||||
# 🔥 경량 메모리 정리 - 중간 처리 데이터만 정리, 모델은 유지
|
||||
_lightweight_memory_cleanup(trace_id)
|
||||
|
||||
# 인페인트 후 (렌더 전)
|
||||
save_debug_artifacts(debug_dir, guid, inpaint=dst_bgr, _stage="INPAINT")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,585 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
MIGAN ONNX 파이프라인 인페인팅 모듈
|
||||
- MIGAN ONNX 모델을 사용한 전체 이미지 인페인팅
|
||||
- TensorRT 최적화 지원
|
||||
- 세션 캐시 및 버퍼 재사용
|
||||
- 이미지 크기 제한 및 후처리
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import logging
|
||||
import tempfile
|
||||
from typing import Optional, Dict, Any, Tuple
|
||||
from pathlib import Path
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import onnxruntime as ort
|
||||
|
||||
# OpenCV 내부 최적화 off (호환성)
|
||||
cv2.setUseOptimized(False)
|
||||
|
||||
|
||||
def _np_uint8_2d(arr, name="mask"):
|
||||
"""2D uint8 배열 검증 및 변환"""
|
||||
if arr is None:
|
||||
raise ValueError(f"{name} is None")
|
||||
if not isinstance(arr, np.ndarray):
|
||||
raise TypeError(f"{name} must be np.ndarray, got {type(arr)}")
|
||||
if arr.ndim != 2:
|
||||
raise ValueError(f"{name} must be 2D, got shape={arr.shape}")
|
||||
if arr.dtype != np.uint8:
|
||||
arr = arr.astype(np.uint8, copy=False)
|
||||
return arr
|
||||
|
||||
|
||||
def _ensure_logger(logger: Optional[object]) -> logging.Logger:
|
||||
"""로거 어댑터 생성"""
|
||||
if logger and hasattr(logger, "log"):
|
||||
return logger
|
||||
|
||||
pylogger = logging.getLogger("MIGAN")
|
||||
if not pylogger.handlers:
|
||||
pylogger.setLevel(logging.DEBUG)
|
||||
h = logging.StreamHandler(stream=sys.stdout)
|
||||
h.setFormatter(logging.Formatter("[%(asctime)s][%(levelname)s] %(message)s"))
|
||||
pylogger.addHandler(h)
|
||||
|
||||
class _Adapter:
|
||||
def __init__(self, _lg):
|
||||
self._lg = _lg
|
||||
def log(self, msg, level=logging.INFO, **kwargs):
|
||||
self._lg.log(level, msg)
|
||||
return _Adapter(pylogger)
|
||||
|
||||
|
||||
class MIGANInpaintingModule:
|
||||
"""
|
||||
MIGAN ONNX 파이프라인 인페인팅 모듈
|
||||
- 전체 이미지 처리 (ROI 비활성화)
|
||||
- TensorRT 최적화 지원
|
||||
- 세션 캐시 및 메모리 효율성
|
||||
"""
|
||||
|
||||
_SESSION_CACHE = {} # 세션 캐시
|
||||
_TRT_ENGINE_CACHE_DIR = None # TensorRT 엔진 캐시 디렉토리
|
||||
|
||||
def __init__(self, logger=None, config: Dict[str, Any] = None):
|
||||
"""
|
||||
MIGAN 인페인팅 모듈 초기화
|
||||
|
||||
Args:
|
||||
logger: 로깅 객체
|
||||
config: 설정 딕셔너리
|
||||
"""
|
||||
self.logger = _ensure_logger(logger)
|
||||
|
||||
# 기본 설정
|
||||
self.default_config = {
|
||||
'onnx_path': '/app/worker/models/migan_pipeline_v2.onnx',
|
||||
'use_cuda': True,
|
||||
'use_tensorrt': True,
|
||||
'trt_fp16_enable': True,
|
||||
'trt_engine_cache_enable': True,
|
||||
'intra_threads': 0,
|
||||
'inter_threads': 0,
|
||||
'max_image_size': 1600, # 최대 한 변 제한
|
||||
'output_max_width': 800, # 출력 가로 최대 크기
|
||||
'enable_output_resize': True, # 출력 리사이즈 활성화
|
||||
}
|
||||
|
||||
if config:
|
||||
self.default_config.update(config)
|
||||
|
||||
# TensorRT 엔진 캐시 디렉토리 설정
|
||||
if self.default_config['trt_engine_cache_enable']:
|
||||
self._setup_trt_cache_dir()
|
||||
|
||||
self.session = None
|
||||
self.input_names = None
|
||||
self.output_names = None
|
||||
|
||||
self.logger.log("MIGAN 인페인팅 모듈 초기화 완료", level=logging.INFO)
|
||||
|
||||
def update_config(self, new_config: Dict[str, Any]):
|
||||
"""
|
||||
설정을 동적으로 업데이트
|
||||
|
||||
Args:
|
||||
new_config: 새로운 설정 딕셔너리
|
||||
"""
|
||||
old_config = self.default_config.copy()
|
||||
self.default_config.update(new_config)
|
||||
|
||||
# 세션에 영향을 주는 설정이 변경되었는지 확인
|
||||
session_affecting_keys = {
|
||||
'onnx_path', 'use_cuda', 'use_tensorrt',
|
||||
'trt_fp16_enable', 'intra_threads', 'inter_threads'
|
||||
}
|
||||
|
||||
config_changed = any(
|
||||
old_config.get(key) != self.default_config.get(key)
|
||||
for key in session_affecting_keys
|
||||
)
|
||||
|
||||
if config_changed:
|
||||
self.logger.log("설정 변경으로 인한 세션 재초기화 필요", level=logging.INFO)
|
||||
# 기존 세션 정보 리셋 (새로운 세션이 생성되도록)
|
||||
self.session = None
|
||||
self.input_names = None
|
||||
self.output_names = None
|
||||
|
||||
# TensorRT 캐시 디렉토리 업데이트
|
||||
if self.default_config['trt_engine_cache_enable']:
|
||||
self._setup_trt_cache_dir()
|
||||
|
||||
self.logger.log(f"설정 업데이트 완료: {list(new_config.keys())}", level=logging.INFO)
|
||||
|
||||
def _setup_trt_cache_dir(self):
|
||||
"""TensorRT 엔진 캐시 디렉토리 설정"""
|
||||
if self._TRT_ENGINE_CACHE_DIR is None:
|
||||
cache_dir = os.getenv('TRT_ENGINE_CACHE_DIR', '/app/temp_files/trt_cache')
|
||||
os.makedirs(cache_dir, exist_ok=True)
|
||||
self._TRT_ENGINE_CACHE_DIR = cache_dir
|
||||
self.logger.log(f"TensorRT 엔진 캐시 디렉토리: {cache_dir}", level=logging.INFO)
|
||||
|
||||
def _get_session_key(self, config: Dict[str, Any]) -> tuple:
|
||||
"""세션 캐시 키 생성"""
|
||||
return (
|
||||
config['onnx_path'],
|
||||
config['use_cuda'],
|
||||
config['use_tensorrt'],
|
||||
config['trt_fp16_enable'],
|
||||
config['intra_threads'],
|
||||
config['inter_threads']
|
||||
)
|
||||
|
||||
def _create_session(self, config: Dict[str, Any]) -> ort.InferenceSession:
|
||||
"""ONNX Runtime 세션 생성"""
|
||||
onnx_path = config['onnx_path']
|
||||
|
||||
if not os.path.exists(onnx_path):
|
||||
raise FileNotFoundError(f"ONNX 파일을 찾을 수 없습니다: {onnx_path}")
|
||||
|
||||
# 세션 옵션 설정
|
||||
session_options = ort.SessionOptions()
|
||||
if config['intra_threads'] > 0:
|
||||
session_options.intra_op_num_threads = config['intra_threads']
|
||||
if config['inter_threads'] > 0:
|
||||
session_options.inter_op_num_threads = config['inter_threads']
|
||||
|
||||
# 최적화 레벨 설정
|
||||
session_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
|
||||
|
||||
# Provider 설정
|
||||
providers = []
|
||||
provider_options = []
|
||||
|
||||
if config['use_cuda']:
|
||||
if config['use_tensorrt']:
|
||||
# TensorRT Provider 설정
|
||||
trt_options = {
|
||||
'device_id': 0,
|
||||
'trt_fp16_enable': config['trt_fp16_enable'],
|
||||
'trt_engine_cache_enable': config['trt_engine_cache_enable'],
|
||||
}
|
||||
|
||||
if config['trt_engine_cache_enable'] and self._TRT_ENGINE_CACHE_DIR:
|
||||
trt_options['trt_engine_cache_path'] = self._TRT_ENGINE_CACHE_DIR
|
||||
|
||||
providers.append('TensorrtExecutionProvider')
|
||||
provider_options.append(trt_options)
|
||||
|
||||
self.logger.log(f"TensorRT 설정: FP16={config['trt_fp16_enable']}, 캐시={config['trt_engine_cache_enable']}", level=logging.INFO)
|
||||
|
||||
# CUDA Provider
|
||||
cuda_options = {
|
||||
'device_id': 0,
|
||||
'arena_extend_strategy': 'kNextPowerOfTwo',
|
||||
'gpu_mem_limit': 2 * 1024 * 1024 * 1024, # 2GB
|
||||
'cudnn_conv_algo_search': 'EXHAUSTIVE',
|
||||
'do_copy_in_default_stream': True,
|
||||
}
|
||||
providers.append('CUDAExecutionProvider')
|
||||
provider_options.append(cuda_options)
|
||||
|
||||
# CPU Provider (폴백)
|
||||
providers.append('CPUExecutionProvider')
|
||||
provider_options.append({})
|
||||
|
||||
try:
|
||||
if provider_options:
|
||||
session = ort.InferenceSession(
|
||||
onnx_path,
|
||||
sess_options=session_options,
|
||||
providers=providers,
|
||||
provider_options=provider_options
|
||||
)
|
||||
else:
|
||||
session = ort.InferenceSession(
|
||||
onnx_path,
|
||||
sess_options=session_options,
|
||||
providers=providers
|
||||
)
|
||||
|
||||
actual_providers = session.get_providers()
|
||||
self.logger.log(f"ONNX Runtime 세션 생성 완료: {actual_providers}", level=logging.INFO)
|
||||
|
||||
return session
|
||||
|
||||
except Exception as e:
|
||||
self.logger.log(f"GPU 세션 생성 실패, CPU로 폴백: {e}", level=logging.WARNING)
|
||||
# CPU 전용 폴백
|
||||
session = ort.InferenceSession(
|
||||
onnx_path,
|
||||
sess_options=session_options,
|
||||
providers=['CPUExecutionProvider']
|
||||
)
|
||||
self.logger.log("CPU 세션으로 폴백 완료", level=logging.INFO)
|
||||
return session
|
||||
|
||||
def _get_or_create_session(self, config: Dict[str, Any]) -> ort.InferenceSession:
|
||||
"""세션 가져오기 또는 생성 (캐시 활용)"""
|
||||
cache_key = self._get_session_key(config)
|
||||
|
||||
if cache_key in self._SESSION_CACHE:
|
||||
self.logger.log("캐시된 ONNX 세션 재사용", level=logging.DEBUG)
|
||||
return self._SESSION_CACHE[cache_key]
|
||||
|
||||
session = self._create_session(config)
|
||||
self._SESSION_CACHE[cache_key] = session
|
||||
|
||||
return session
|
||||
|
||||
def _prepare_session(self, config: Dict[str, Any]):
|
||||
"""세션 및 입출력 정보 준비"""
|
||||
if self.session is None:
|
||||
self.session = self._get_or_create_session(config)
|
||||
|
||||
# 입출력 정보 저장
|
||||
inputs = self.session.get_inputs()
|
||||
outputs = self.session.get_outputs()
|
||||
|
||||
if len(inputs) != 2:
|
||||
raise ValueError(f"MIGAN 모델은 2개의 입력이 필요합니다. 현재: {len(inputs)}개")
|
||||
if len(outputs) != 1:
|
||||
raise ValueError(f"MIGAN 모델은 1개의 출력이 필요합니다. 현재: {len(outputs)}개")
|
||||
|
||||
self.input_names = [inp.name for inp in inputs]
|
||||
self.output_names = [out.name for out in outputs]
|
||||
|
||||
# 입출력 형태 로깅
|
||||
for i, inp in enumerate(inputs):
|
||||
self.logger.log(f"입력 {i}: {inp.name}, 형태: {inp.shape}, 타입: {inp.type}", level=logging.DEBUG)
|
||||
for i, out in enumerate(outputs):
|
||||
self.logger.log(f"출력 {i}: {out.name}, 형태: {out.shape}, 타입: {out.type}", level=logging.DEBUG)
|
||||
|
||||
def scale_image_if_needed(self, image: np.ndarray, mask: np.ndarray,
|
||||
max_size: int) -> Tuple[np.ndarray, np.ndarray, Dict]:
|
||||
"""
|
||||
이미지 크기 제한 적용
|
||||
|
||||
Args:
|
||||
image: 입력 이미지
|
||||
mask: 입력 마스크
|
||||
max_size: 최대 크기 (긴 변 기준)
|
||||
|
||||
Returns:
|
||||
(스케일된 이미지, 스케일된 마스크, 스케일 정보)
|
||||
"""
|
||||
h, w = image.shape[:2]
|
||||
max_dimension = max(h, w)
|
||||
|
||||
if max_dimension <= max_size:
|
||||
return image, mask, {'scaled': False, 'original_size': (h, w)}
|
||||
|
||||
# 스케일 계산
|
||||
scale_factor = max_size / max_dimension
|
||||
new_h = int(h * scale_factor)
|
||||
new_w = int(w * scale_factor)
|
||||
|
||||
# 8의 배수로 조정 (ONNX 모델 호환성)
|
||||
new_h = ((new_h + 7) // 8) * 8
|
||||
new_w = ((new_w + 7) // 8) * 8
|
||||
|
||||
# 리사이즈
|
||||
scaled_image = cv2.resize(image, (new_w, new_h), interpolation=cv2.INTER_AREA)
|
||||
scaled_mask = cv2.resize(mask, (new_w, new_h), interpolation=cv2.INTER_NEAREST)
|
||||
|
||||
self.logger.log(
|
||||
f"이미지 스케일링: {w}x{h} → {new_w}x{new_h} (factor={scale_factor:.3f})",
|
||||
level=logging.INFO
|
||||
)
|
||||
|
||||
return scaled_image, scaled_mask, {
|
||||
'scaled': True,
|
||||
'original_size': (h, w),
|
||||
'scale_factor': scale_factor,
|
||||
'scaled_size': (new_h, new_w)
|
||||
}
|
||||
|
||||
def restore_original_scale(self, image: np.ndarray, scale_info: Dict) -> np.ndarray:
|
||||
"""
|
||||
처리된 이미지를 원본 크기로 복원
|
||||
|
||||
Args:
|
||||
image: 처리된 이미지
|
||||
scale_info: 스케일 정보
|
||||
|
||||
Returns:
|
||||
복원된 이미지
|
||||
"""
|
||||
if not scale_info.get('scaled', False):
|
||||
return image
|
||||
|
||||
original_h, original_w = scale_info['original_size']
|
||||
restored = cv2.resize(image, (original_w, original_h), interpolation=cv2.INTER_CUBIC)
|
||||
|
||||
self.logger.log(
|
||||
f"이미지 복원: {image.shape[1]}x{image.shape[0]} → {original_w}x{original_h}",
|
||||
level=logging.INFO
|
||||
)
|
||||
|
||||
return restored
|
||||
|
||||
def resize_output_if_needed(self, image: np.ndarray, max_width: int) -> np.ndarray:
|
||||
"""
|
||||
출력 이미지 크기 조정 (가로 기준)
|
||||
|
||||
Args:
|
||||
image: 출력 이미지
|
||||
max_width: 최대 가로 크기
|
||||
|
||||
Returns:
|
||||
크기 조정된 이미지
|
||||
"""
|
||||
h, w = image.shape[:2]
|
||||
|
||||
if w <= max_width:
|
||||
return image
|
||||
|
||||
# 비율 유지하며 리사이즈
|
||||
scale_factor = max_width / w
|
||||
new_w = max_width
|
||||
new_h = int(h * scale_factor)
|
||||
|
||||
resized = cv2.resize(image, (new_w, new_h), interpolation=cv2.INTER_AREA)
|
||||
|
||||
self.logger.log(
|
||||
f"출력 리사이즈: {w}x{h} → {new_w}x{new_h} (max_width={max_width})",
|
||||
level=logging.INFO
|
||||
)
|
||||
|
||||
return resized
|
||||
|
||||
def inpaint_with_migan(self, image: np.ndarray, mask: np.ndarray,
|
||||
config: Dict[str, Any] = None) -> np.ndarray:
|
||||
"""
|
||||
MIGAN을 사용한 전체 이미지 인페인팅
|
||||
|
||||
Args:
|
||||
image: 입력 이미지 (BGR)
|
||||
mask: 마스크 (0~255, 텍스트영역=255)
|
||||
config: 설정 오버라이드
|
||||
|
||||
Returns:
|
||||
인페인팅된 이미지 (BGR)
|
||||
"""
|
||||
start_time = time.time()
|
||||
|
||||
# 🔥 동적 설정 업데이트 지원
|
||||
if config is not None:
|
||||
self.update_config(config)
|
||||
|
||||
effective_config = self.default_config
|
||||
|
||||
try:
|
||||
# 1. 세션 준비
|
||||
self._prepare_session(effective_config)
|
||||
|
||||
# 2. 이미지 크기 제한
|
||||
max_size = effective_config['max_image_size']
|
||||
scaled_image, scaled_mask, scale_info = self.scale_image_if_needed(
|
||||
image, mask, max_size
|
||||
)
|
||||
|
||||
# 3. 임시 파일로 이미지 저장 (MIGAN 모듈이 파일 경로를 요구함)
|
||||
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp_file:
|
||||
temp_path = tmp_file.name
|
||||
cv2.imwrite(temp_path, scaled_image)
|
||||
|
||||
try:
|
||||
# 4. 마스크 전처리: 이진화 → 반전 (MIGAN 규칙에 맞춤)
|
||||
mask_uint8 = _np_uint8_2d(scaled_mask, "mask")
|
||||
_, mask_bin = cv2.threshold(mask_uint8, 128, 255, cv2.THRESH_BINARY)
|
||||
mask_known255 = 255 - mask_bin # 텍스트영역(255) → hole(0)
|
||||
|
||||
# 5. 이미지 로드 및 RGB 변환
|
||||
bgr = cv2.imread(temp_path, cv2.IMREAD_COLOR)
|
||||
if bgr is None:
|
||||
raise ValueError(f"임시 이미지 로드 실패: {temp_path}")
|
||||
|
||||
rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)
|
||||
H, W = rgb.shape[:2]
|
||||
|
||||
if mask_known255.shape != (H, W):
|
||||
raise ValueError(f"마스크 크기 불일치: mask={mask_known255.shape}, img={(H,W)}")
|
||||
|
||||
# 6. ONNX 추론을 위한 배치 차원 추가 및 차원 순서 변경
|
||||
inference_start = time.time()
|
||||
|
||||
# 이미지: (H, W, 3) → (1, 3, H, W)
|
||||
rgb_batch = np.expand_dims(rgb, 0).transpose(0, 3, 1, 2).astype(np.uint8)
|
||||
|
||||
# 마스크: (H, W) → (1, 1, H, W)
|
||||
mask_batch = np.expand_dims(mask_known255, (0, 1)).astype(np.uint8)
|
||||
|
||||
self.logger.log(
|
||||
f"추론 입력 - 이미지: {rgb_batch.shape}, 마스크: {mask_batch.shape}",
|
||||
level=logging.DEBUG
|
||||
)
|
||||
|
||||
# ONNX 추론 실행
|
||||
inputs = {
|
||||
self.input_names[0]: rgb_batch,
|
||||
self.input_names[1]: mask_batch
|
||||
}
|
||||
|
||||
outputs = self.session.run(self.output_names, inputs)
|
||||
result = outputs[0] # (1, 3, H, W) 형태 예상
|
||||
|
||||
inference_time = time.time() - inference_start
|
||||
|
||||
# 7. 출력 후처리: (1, 3, H, W) → (H, W, 3)
|
||||
if result.ndim == 4 and result.shape[0] == 1:
|
||||
result = result[0].transpose(1, 2, 0) # (1,3,H,W) → (H,W,3)
|
||||
elif result.ndim == 3 and result.shape[0] == 3:
|
||||
result = result.transpose(1, 2, 0) # (3,H,W) → (H,W,3)
|
||||
|
||||
if not isinstance(result, np.ndarray) or result.ndim != 3:
|
||||
raise ValueError(f"출력 형식 오류: shape={result.shape}, dtype={result.dtype}")
|
||||
|
||||
# uint8 보장
|
||||
if result.dtype != np.uint8:
|
||||
result = np.clip(result, 0, 255).astype(np.uint8)
|
||||
|
||||
# 8. BGR 변환
|
||||
result_bgr = cv2.cvtColor(result, cv2.COLOR_RGB2BGR)
|
||||
|
||||
# 9. 원본 크기로 복원
|
||||
if scale_info['scaled']:
|
||||
result_bgr = self.restore_original_scale(result_bgr, scale_info)
|
||||
|
||||
# 10. 출력 크기 조정 (옵션)
|
||||
if effective_config.get('enable_output_resize', True):
|
||||
max_width = effective_config.get('output_max_width', 800)
|
||||
result_bgr = self.resize_output_if_needed(result_bgr, max_width)
|
||||
|
||||
total_time = time.time() - start_time
|
||||
|
||||
self.logger.log(
|
||||
f"MIGAN 인페인팅 완료: 총 {total_time:.3f}s (추론: {inference_time:.3f}s)",
|
||||
level=logging.INFO
|
||||
)
|
||||
|
||||
return result_bgr
|
||||
|
||||
finally:
|
||||
# 임시 파일 정리
|
||||
try:
|
||||
os.unlink(temp_path)
|
||||
except:
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
self.logger.log(f"MIGAN 인페인팅 실패: {e}", level=logging.ERROR)
|
||||
import traceback
|
||||
self.logger.log(traceback.format_exc(), level=logging.DEBUG)
|
||||
return image # 실패 시 원본 반환
|
||||
|
||||
def cleanup_memory(self):
|
||||
"""
|
||||
🔥 경고: 전역 인스턴스에서는 이 메소드를 호출하지 마세요!
|
||||
메모리 정리 (개발/테스트 환경에서만 사용)
|
||||
"""
|
||||
self.logger.log("⚠️ cleanup_memory 호출됨 - 전역 인스턴스에서는 권장하지 않음", level=logging.WARNING)
|
||||
import gc
|
||||
gc.collect()
|
||||
try:
|
||||
# GPU 메모리 정리
|
||||
if hasattr(self, 'session') and self.session:
|
||||
# ONNX Runtime은 자동으로 메모리를 관리하므로 특별한 정리 불필요
|
||||
pass
|
||||
except Exception as e:
|
||||
self.logger.log(f"메모리 정리 중 오류: {e}", level=logging.WARNING)
|
||||
|
||||
self.logger.log("MIGAN 메모리 정리 완료", level=logging.INFO)
|
||||
|
||||
def get_processing_stats(self, image: np.ndarray, mask: np.ndarray) -> Dict[str, Any]:
|
||||
"""
|
||||
MIGAN 처리 통계 정보 반환
|
||||
|
||||
Args:
|
||||
image: 입력 이미지
|
||||
mask: 입력 마스크
|
||||
|
||||
Returns:
|
||||
처리 통계 딕셔너리
|
||||
"""
|
||||
total_area = image.shape[0] * image.shape[1]
|
||||
mask_area = np.sum(mask > 128)
|
||||
|
||||
return {
|
||||
'total_image_size': total_area,
|
||||
'mask_area': mask_area,
|
||||
'mask_coverage_ratio': mask_area / total_area if total_area > 0 else 0.0,
|
||||
'processing_method': 'full_image_migan',
|
||||
'roi_processing': False,
|
||||
'max_size_limit': self.default_config['max_image_size'],
|
||||
'output_resize_enabled': self.default_config['enable_output_resize']
|
||||
}
|
||||
|
||||
|
||||
# 편의 함수들
|
||||
def create_migan_inpainter(logger=None, config=None):
|
||||
"""MIGAN 인페인팅 모듈 팩토리 함수"""
|
||||
return MIGANInpaintingModule(logger, config)
|
||||
|
||||
|
||||
def build_migan_from_toggle(toggle_states: Dict[str, Any], logger=None) -> MIGANInpaintingModule:
|
||||
"""
|
||||
toggle_states로부터 설정을 읽어 MIGAN 인페인팅 모듈 생성
|
||||
|
||||
Args:
|
||||
toggle_states: 설정 딕셔너리
|
||||
logger: 로거 객체
|
||||
|
||||
Returns:
|
||||
MIGAN 인페인팅 모듈 인스턴스
|
||||
"""
|
||||
config = {
|
||||
'onnx_path': toggle_states.get('migan_onnx_path', '/app/worker/models/migan_pipeline_v2.onnx'),
|
||||
'use_cuda': toggle_states.get('migan_use_cuda', True),
|
||||
'use_tensorrt': toggle_states.get('migan_use_tensorrt', True),
|
||||
'trt_fp16_enable': toggle_states.get('migan_trt_fp16_enable', True),
|
||||
'trt_engine_cache_enable': toggle_states.get('migan_trt_engine_cache_enable', True),
|
||||
'intra_threads': int(toggle_states.get('migan_intra_threads', 0) or 0),
|
||||
'inter_threads': int(toggle_states.get('migan_inter_threads', 0) or 0),
|
||||
'max_image_size': int(toggle_states.get('migan_max_image_size', 1600)),
|
||||
'output_max_width': int(toggle_states.get('migan_output_max_width', 800)),
|
||||
'enable_output_resize': toggle_states.get('migan_enable_output_resize', True),
|
||||
}
|
||||
|
||||
return MIGANInpaintingModule(logger, config)
|
||||
|
||||
|
||||
def quick_migan_inpaint(image: np.ndarray, mask: np.ndarray,
|
||||
logger=None, config=None) -> np.ndarray:
|
||||
"""간단한 MIGAN 인페인팅 수행"""
|
||||
inpainter = create_migan_inpainter(logger, config)
|
||||
result = inpainter.inpaint_with_migan(image, mask, config)
|
||||
inpainter.cleanup_memory()
|
||||
return result
|
||||
Binary file not shown.
|
|
@ -0,0 +1,312 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
MIGAN 인페인팅 모듈 테스트 스크립트
|
||||
|
||||
이 스크립트는 MIGAN 모듈의 기본 사용법과 설정을 보여줍니다.
|
||||
실제 운영 환경에서는 celery_worker.py를 통해 사용하세요.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import numpy as np
|
||||
import cv2
|
||||
from pathlib import Path
|
||||
|
||||
# 현재 디렉토리를 Python 경로에 추가
|
||||
current_dir = Path(__file__).parent
|
||||
sys.path.insert(0, str(current_dir))
|
||||
|
||||
try:
|
||||
from migan_inpainting_module import MIGANInpaintingModule, build_migan_from_toggle, quick_migan_inpaint
|
||||
from loggerModule import Logger
|
||||
except ImportError as e:
|
||||
print(f"모듈 import 실패: {e}")
|
||||
print("worker 디렉토리에서 실행하거나 PYTHONPATH를 설정하세요.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def create_test_image_and_mask():
|
||||
"""테스트용 이미지와 마스크 생성"""
|
||||
# 테스트 이미지 생성 (800x600, 컬러)
|
||||
image = np.random.randint(0, 255, (600, 800, 3), dtype=np.uint8)
|
||||
|
||||
# 중앙에 텍스트 영역 시뮬레이션 (여러 개의 사각형)
|
||||
mask = np.zeros((600, 800), dtype=np.uint8)
|
||||
|
||||
# 텍스트 영역들 (마스크에서 255는 텍스트 영역)
|
||||
rectangles = [
|
||||
(100, 100, 200, 50), # x, y, w, h
|
||||
(300, 200, 150, 40),
|
||||
(500, 300, 180, 60),
|
||||
(200, 400, 220, 45),
|
||||
]
|
||||
|
||||
for x, y, w, h in rectangles:
|
||||
mask[y:y+h, x:x+w] = 255
|
||||
|
||||
return image, mask
|
||||
|
||||
|
||||
def test_basic_usage():
|
||||
"""기본 사용법 테스트"""
|
||||
print("=== MIGAN 인페인팅 모듈 기본 테스트 ===")
|
||||
|
||||
# 로거 생성
|
||||
logger = Logger()
|
||||
|
||||
# 테스트 이미지 및 마스크 생성
|
||||
print("1. 테스트 이미지 및 마스크 생성...")
|
||||
image, mask = create_test_image_and_mask()
|
||||
print(f" 이미지 크기: {image.shape}")
|
||||
print(f" 마스크 크기: {mask.shape}")
|
||||
print(f" 마스크 영역: {np.sum(mask > 0)} 픽셀")
|
||||
|
||||
# MIGAN 모듈 초기화
|
||||
print("\n2. MIGAN 모듈 초기화...")
|
||||
try:
|
||||
# 기본 설정으로 모듈 생성
|
||||
config = {
|
||||
'onnx_path': '/app/worker/models/migan_pipeline_v2.onnx',
|
||||
'use_cuda': False, # CPU 모드로 테스트
|
||||
'use_tensorrt': False,
|
||||
'max_image_size': 1024,
|
||||
'output_max_width': 600,
|
||||
}
|
||||
|
||||
inpainter = MIGANInpaintingModule(logger=logger, config=config)
|
||||
print(" MIGAN 모듈 초기화 성공")
|
||||
|
||||
# 처리 통계 확인
|
||||
print("\n3. 처리 통계 분석...")
|
||||
stats = inpainter.get_processing_stats(image, mask)
|
||||
for key, value in stats.items():
|
||||
print(f" {key}: {value}")
|
||||
|
||||
# 인페인팅 실행 (모델 파일이 없으면 실패할 것임)
|
||||
print("\n4. MIGAN 인페인팅 실행...")
|
||||
try:
|
||||
result = inpainter.inpaint_with_migan(image, mask)
|
||||
print(f" 인페인팅 성공! 결과 이미지 크기: {result.shape}")
|
||||
except FileNotFoundError:
|
||||
print(" ⚠️ 모델 파일을 찾을 수 없습니다: /app/worker/models/migan_pipeline_v2.onnx")
|
||||
print(" 모델 파일을 올바른 위치에 배치한 후 다시 시도하세요.")
|
||||
except Exception as e:
|
||||
print(f" 인페인팅 실패: {e}")
|
||||
|
||||
# 메모리 정리
|
||||
inpainter.cleanup_memory()
|
||||
|
||||
except Exception as e:
|
||||
print(f" MIGAN 모듈 초기화 실패: {e}")
|
||||
|
||||
|
||||
def test_toggle_states_usage():
|
||||
"""toggle_states를 사용한 설정 테스트"""
|
||||
print("\n=== toggle_states 설정 테스트 ===")
|
||||
|
||||
# 로거 생성
|
||||
logger = Logger()
|
||||
|
||||
# toggle_states 설정 예시들
|
||||
test_configs = [
|
||||
{
|
||||
"name": "CPU 모드",
|
||||
"toggle_states": {
|
||||
"inpaint_method": "migan",
|
||||
"migan_use_cuda": False,
|
||||
"migan_use_tensorrt": False,
|
||||
"migan_max_image_size": 800,
|
||||
"migan_output_max_width": 600,
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "GPU 모드 (TensorRT 비활성화)",
|
||||
"toggle_states": {
|
||||
"inpaint_method": "migan",
|
||||
"migan_use_cuda": True,
|
||||
"migan_use_tensorrt": False,
|
||||
"migan_max_image_size": 1600,
|
||||
"migan_output_max_width": 800,
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "최고 성능 모드 (TensorRT + FP16)",
|
||||
"toggle_states": {
|
||||
"inpaint_method": "migan",
|
||||
"migan_use_cuda": True,
|
||||
"migan_use_tensorrt": True,
|
||||
"migan_trt_fp16_enable": True,
|
||||
"migan_trt_engine_cache_enable": True,
|
||||
"migan_max_image_size": 2048,
|
||||
"migan_output_max_width": 1200,
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
for i, config in enumerate(test_configs, 1):
|
||||
print(f"\n{i}. {config['name']} 테스트")
|
||||
print(f" 설정: {config['toggle_states']}")
|
||||
|
||||
try:
|
||||
# toggle_states로부터 MIGAN 모듈 생성
|
||||
inpainter = build_migan_from_toggle(config['toggle_states'], logger)
|
||||
print(" 모듈 생성 성공")
|
||||
|
||||
# 설정 확인
|
||||
print(f" CUDA 사용: {inpainter.default_config['use_cuda']}")
|
||||
print(f" TensorRT 사용: {inpainter.default_config['use_tensorrt']}")
|
||||
print(f" 최대 이미지 크기: {inpainter.default_config['max_image_size']}")
|
||||
|
||||
except Exception as e:
|
||||
print(f" 모듈 생성 실패: {e}")
|
||||
|
||||
|
||||
def test_quick_inpaint():
|
||||
"""빠른 인페인팅 함수 테스트"""
|
||||
print("\n=== 빠른 인페인팅 함수 테스트 ===")
|
||||
|
||||
# 테스트 이미지 생성
|
||||
image, mask = create_test_image_and_mask()
|
||||
|
||||
# 빠른 인페인팅 설정
|
||||
config = {
|
||||
'use_cuda': False,
|
||||
'use_tensorrt': False,
|
||||
'max_image_size': 512,
|
||||
}
|
||||
|
||||
print("1. quick_migan_inpaint 함수 실행...")
|
||||
try:
|
||||
result = quick_migan_inpaint(image, mask, config=config)
|
||||
print(f" 성공! 결과 크기: {result.shape}")
|
||||
except FileNotFoundError:
|
||||
print(" ⚠️ 모델 파일을 찾을 수 없습니다.")
|
||||
except Exception as e:
|
||||
print(f" 실패: {e}")
|
||||
|
||||
|
||||
def test_error_handling():
|
||||
"""에러 처리 테스트"""
|
||||
print("\n=== 에러 처리 테스트 ===")
|
||||
|
||||
logger = Logger()
|
||||
|
||||
# 잘못된 모델 경로로 테스트
|
||||
print("1. 잘못된 모델 경로 테스트...")
|
||||
config = {
|
||||
'onnx_path': '/nonexistent/path/model.onnx',
|
||||
'use_cuda': False,
|
||||
}
|
||||
|
||||
try:
|
||||
inpainter = MIGANInpaintingModule(logger=logger, config=config)
|
||||
image, mask = create_test_image_and_mask()
|
||||
result = inpainter.inpaint_with_migan(image, mask)
|
||||
print(" 예상과 다르게 성공함")
|
||||
except Exception as e:
|
||||
print(f" 예상된 에러 발생: {type(e).__name__}")
|
||||
|
||||
# 잘못된 입력 데이터 테스트
|
||||
print("\n2. 잘못된 입력 데이터 테스트...")
|
||||
try:
|
||||
config = {'use_cuda': False, 'use_tensorrt': False}
|
||||
inpainter = MIGANInpaintingModule(logger=logger, config=config)
|
||||
|
||||
# 잘못된 형태의 이미지/마스크
|
||||
bad_image = np.zeros((100, 100), dtype=np.uint8) # 2D 이미지
|
||||
bad_mask = np.zeros((50, 50), dtype=np.uint8) # 다른 크기
|
||||
|
||||
result = inpainter.inpaint_with_migan(bad_image, bad_mask)
|
||||
print(" 예상과 다르게 성공함")
|
||||
except Exception as e:
|
||||
print(f" 예상된 에러 발생: {type(e).__name__}")
|
||||
|
||||
|
||||
def show_usage_examples():
|
||||
"""사용 예시 출력"""
|
||||
print("\n=== 사용 예시 ===")
|
||||
|
||||
print("\n1. Celery worker에서 ROI vs MIGAN 선택:")
|
||||
print("""
|
||||
# ROI 기반 인페인팅 (기본)
|
||||
toggle_states = {
|
||||
"inpaint_method": "roi"
|
||||
}
|
||||
|
||||
# MIGAN 인페인팅
|
||||
toggle_states = {
|
||||
"inpaint_method": "migan",
|
||||
"migan_use_cuda": True,
|
||||
"migan_trt_fp16_enable": True
|
||||
}
|
||||
""")
|
||||
|
||||
print("\n2. 직접 MIGAN 모듈 사용:")
|
||||
print("""
|
||||
from worker.migan_inpainting_module import MIGANInpaintingModule
|
||||
|
||||
# 모듈 생성
|
||||
inpainter = MIGANInpaintingModule(config={
|
||||
'use_cuda': True,
|
||||
'max_image_size': 1600
|
||||
})
|
||||
|
||||
# 인페인팅 실행
|
||||
result = inpainter.inpaint_with_migan(image, mask)
|
||||
""")
|
||||
|
||||
print("\n3. toggle_states로 모듈 생성:")
|
||||
print("""
|
||||
from worker.migan_inpainting_module import build_migan_from_toggle
|
||||
|
||||
toggle_states = {
|
||||
"migan_use_tensorrt": True,
|
||||
"migan_trt_fp16_enable": True,
|
||||
"migan_max_image_size": 2048
|
||||
}
|
||||
|
||||
inpainter = build_migan_from_toggle(toggle_states)
|
||||
""")
|
||||
|
||||
|
||||
def main():
|
||||
"""메인 테스트 실행"""
|
||||
print("MIGAN 인페인팅 모듈 테스트 시작")
|
||||
print("=" * 50)
|
||||
|
||||
# 환경 정보 출력
|
||||
print(f"Python 버전: {sys.version}")
|
||||
print(f"작업 디렉토리: {os.getcwd()}")
|
||||
print(f"NumPy 버전: {np.__version__}")
|
||||
print(f"OpenCV 버전: {cv2.__version__}")
|
||||
|
||||
# 모델 파일 존재 확인
|
||||
model_path = "/app/worker/models/migan_pipeline_v2.onnx"
|
||||
if os.path.exists(model_path):
|
||||
print(f"✅ 모델 파일 발견: {model_path}")
|
||||
else:
|
||||
print(f"⚠️ 모델 파일 없음: {model_path}")
|
||||
print(" 모델 파일을 배치한 후 테스트하면 실제 인페인팅이 실행됩니다.")
|
||||
|
||||
# 테스트 실행
|
||||
try:
|
||||
test_basic_usage()
|
||||
test_toggle_states_usage()
|
||||
test_quick_inpaint()
|
||||
test_error_handling()
|
||||
show_usage_examples()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n사용자에 의해 중단됨")
|
||||
except Exception as e:
|
||||
print(f"\n예상치 못한 에러: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("테스트 완료")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue