wing-ops/prediction/image/optimization.md
jeonghyo.k 3946ff6a25 feat(prediction): 이미지 분석 서버 Docker 패키징 + DB 코드 제거
- prediction/image/ FastAPI 서버 Docker 환경 구성
  - Dockerfile: PyTorch 2.1 + CUDA 12.1 기반 GPU 이미지
  - docker-compose.yml: GPU 할당 + 데이터 볼륨 마운트
  - requirements.txt: 서버 의존성 목록
  - .env.example: 환경변수 템플릿
  - DOCKER_USAGE.md: 빌드/실행/API 사용법 문서
  - Dockerfile에 .dockerignore 제외 폴더 mkdir -p 추가
- .gitignore: prediction/image 결과물 및 모델 가중치(.pth) 제외 추가
- dbInsert_csv.py, dbInsert_shp.py 삭제 (미사용 DB 로직)
- api.py: dbInsert import 및 주석 처리된 DB 호출 코드 제거
- aerialRouter.ts: req.params 타입 오류 수정
2026-03-10 18:37:36 +09:00

5.8 KiB

run-script API 성능 최적화 기록

결과 요약

단계 소요 시간
최적화 전 35.12초
1차 최적화 후 ~12초
2차 최적화 후 ~8~10초 (예상)

1차 최적화 (~35초 → ~12초)

문제 원인

위치 원인
Inference.py 모듈 레벨 init_segmentor() 호출 → 요청마다 GPU 모델 재로딩 (15~20초)
api.py subprocess.run() 으로 Combine_module.py 실행 → 매 요청마다 Python 인터프리터 재시작
Combine_module.py Step1~4를 순차 subprocess 4개로 실행
Georeference.py 내부 루프 np.ones((3,3), np.uint8) dilate 커널을 루프마다 재생성
Oilshape.py pixel_to_geo() rasterio.transform.xy() 를 좌표 1개씩 루프로 호출
Inference.py 내부 루프 palette_array = np.array(model.PALETTE) 를 이미지마다 재생성

개선 내용

mx15hdi/Detect/Inference.py

  • load_model() 함수 분리 — 모델 초기화를 서버 시작 시 1회로 분리
  • run_inference(model, file_id) 함수화 — 사전 로드된 모델을 인자로 수신
  • palette_array 루프 외부로 이동 (이미지마다 재생성 제거)
  • cv2.cvtColor 제거 → numpy 슬라이싱 color_mask[:, :, ::-1].copy() 로 대체

mx15hdi/Metadata/Scripts/Export_Metadata_mx15hdi.py

  • 모듈 레벨 PaddleOCR() 초기화 제거 → _get_ocr_engine() lazy 초기화로 변경
  • run_metadata_export(file_id) 함수화 + 절대 경로(_MX15HDI_DIR) 기반으로 전환
  • deprecated API 수정: fillna(method='ffill')ffill() / bfill()

mx15hdi/Georeference/Scripts/Create_Georeferenced_Images_nadir.py

  • run_georeference(file_id) 함수화 + 절대 경로 기반으로 전환
  • dilate_kernel = np.ones((3,3), np.uint8) 루프 외부로 이동 (루프마다 재생성 제거)

mx15hdi/Polygon/Scripts/Oilshape.py

  • run_oilshape(file_id) 함수화 + 절대 경로 기반으로 전환
  • pixel_to_geo() 벡터화: 좌표 배열 일괄 처리 (rasterio.transform.xy 배열 입력)

mx15hdi/Main/Combine_module.py

  • subprocess 4개 → 직접 함수 호출로 교체
  • run_pipeline(file_id, model=None) 함수 추가

api.py

  • FastAPI lifespan 이벤트로 서버 시작 시 모델 1회 로딩
  • ThreadPoolExecutor(max_workers=4) 추가
  • _run_mx15hdi_pipeline() 비동기 함수: Step1(추론) + Step2(메타데이터)를 asyncio.gather로 병렬 실행

2차 최적화 (~12초 → ~8~10초)

문제 원인

위치 원인
Inference.py cv2.imread(image_path) 로드 후 inference_segmentor(model, image_path) 에 경로 문자열 전달 → mmseg 내부에서 동일 이미지 재읽기
Inference.pyGeoreference.py blended/mask를 Detect/result/, Detect/Mask_result/ 에 저장 후 georeference에서 다시 읽음 (디스크 왕복 1)
Georeference.pyOilshape.py mask를 Georeference/Mask_Tif/ 에 LZW 압축 GeoTIF로 저장 후 oilshape에서 다시 읽음 (디스크 왕복 2)
Georeference.py scipy.ndimage.binary_dilation 사용 (Python 구현, OpenCV 대비 느림)
Georeference.py rgb와 mask의 빈 픽셀 fill_mask를 중복 계산

개선 내용

mx15hdi/Detect/Inference.py

  • inference_segmentor(model, img_bgr) — 경로 대신 배열 직접 전달 (이중 읽기 제거)
  • write_files: bool = False 파라미터 추가 — 중간 파일 저장 선택적 처리
  • inference_cache dict 반환: {filename: {'blended': ndarray, 'mask': ndarray, 'ext': str}}

mx15hdi/Georeference/Scripts/Create_Georeferenced_Images_nadir.py

  • inference_cache: dict = None 파라미터 추가 — 있으면 메모리 배열 사용, 없으면 디스크 폴백
  • scipy.ndimage.binary_dilation 제거 → cv2.dilate 로 통일
  • rgb/mask 공통 fill_mask 재사용 — 중복 계산 제거
  • Mask_Tif GeoTIF 디스크 저장 생략 — georef_cache 로 반환
  • georef_cache dict 반환: {filename: {'mask': ndarray, 'transform': ..., 'crs': ...}}

mx15hdi/Polygon/Scripts/Oilshape.py

  • georef_cache: Optional[dict] = None 파라미터 추가
  • 있으면 메모리 배열 직접 처리, 없으면 Mask_Tif/ 디스크 폴백
  • _process_mask_entry() 헬퍼 함수 분리

api.py

  • _run_mx15hdi_pipeline() 캐시 체이닝:
    inference_cache, _ = await asyncio.gather(
        loop.run_in_executor(_executor, run_inference, _model, file_id),
        loop.run_in_executor(_executor, run_metadata_export, file_id),
    )
    georef_cache = await loop.run_in_executor(
        _executor, run_georeference, file_id, inference_cache
    )
    await loop.run_in_executor(_executor, run_oilshape, file_id, georef_cache)
    

디스크 I/O 흐름 변화

최적화 전:

Inference  →  Detect/result/ (write)
               Detect/Mask_result/ (write)
Georeference ← Detect/result/ (read)        ← 왕복 1
               Detect/Mask_result/ (read)
Georeference →  Georeference/Mask_Tif/ (write)
Oilshape     ← Georeference/Mask_Tif/ (read) ← 왕복 2

최적화 후:

Inference  →  inference_cache (메모리)
Georeference ← inference_cache (메모리)      ← 왕복 1 제거
Georeference →  georef_cache (메모리)
Oilshape     ← georef_cache (메모리)          ← 왕복 2 제거
Georeference →  Georeference/Tif/ (write)    ← /get-image/ API 전용, 유지

수정 파일 목록

파일 1차 2차
mx15hdi/Detect/Inference.py
mx15hdi/Metadata/Scripts/Export_Metadata_mx15hdi.py
mx15hdi/Georeference/Scripts/Create_Georeferenced_Images_nadir.py
mx15hdi/Polygon/Scripts/Oilshape.py
mx15hdi/Main/Combine_module.py
api.py