wing-ops/prediction/image/project_brief.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

15 KiB

Project Brief — prediction/image

1. 프로젝트 목적

항공/드론 카메라로 촬영된 해양 유류 오염 이미지를 자동 분석하여 유류 확산 정보를 추출·반환하는 이미지 분석 백엔드 서비스이다.

  • 드론(mx15hdi) 또는 열화상(starsafire) 카메라 이미지를 입력받아
  • AI 세그멘테이션으로 유류 유형(검정/갈색/무지개/은색)을 탐지하고
  • 지리참조(GeoTIFF) 변환 후 유류 면적·부피가 담긴 Shapefile을 생성하며
  • 최종적으로 위치 메타데이터와 유류 분석 결과를 JSON으로 반환한다.

지원 카메라 타입: mx15hdi (EO/나디르 드론), starsafire (열화상 카메라)


2. 전체 처리 흐름

클라이언트
  │
  │ POST /run-script/  (camTy, fileId, image 파일)
  ▼
api.py
  ├─ GPS EXIF 검증 (check_gps_info)
  ├─ 이미지 저장  →  {camTy}/Metadata/Image/Original_Images/{fileId}/
  │
  └─ subprocess: Combine_module.py {fileId}
       │
       ├── [1] Detect/Inference.py          AI 세그멘테이션
       │        └─ 결과: Detect/result/{fileId}/        (블렌딩 이미지)
       │                 Detect/Mask_result/{fileId}/   (컬러 마스크)
       │
       ├── [2] Metadata/Scripts/Export_Metadata_mx15hdi.py
       │        └─ EXIF 추출 + 보간 저장
       │                 Metadata/CSV/{fileId}/mx15hdi.csv
       │                 Metadata/CSV/{fileId}/mx15hdi_interpolation.csv
       │
       ├── [3] Georeference/Scripts/Create_Georeferenced_Images_nadir.py
       │        └─ 핀홀 투영 → GeoTIFF 저장
       │                 Georeference/Tif/{fileId}/        (컬러 블렌딩 TIF)
       │                 Georeference/Mask_Tif/{fileId}/   (마스크 TIF)
       │
       └── [4] Polygon/Scripts/Oilshape.py
                └─ 마스크 TIF → 폴리곤 추출 → Shapefile 저장
                         Polygon/Shp/{fileId}/*.shp
  │
  ├─ extract_data.get_metadata()   → CSV에서 첫 번째 행 추출
  └─ extract_data.get_oil_type()   → Shapefile에서 유류 폴리곤 목록 추출
       │
       ▼
  JSON 응답 { meta: "...", data: [...] }

3. 주요 스크립트 설명

3-1. api.py — FastAPI 서버 (포트 5001)

프로젝트의 진입점. 모든 엔드포인트를 정의하며 파이프라인 실행 및 결과 반환을 담당한다.

보조 함수

함수 설명
check_gps_info(image_path) 이미지 EXIF에서 GPS IFD 존재 여부 확인
check_camera_info(image_file) 이미지 EXIF에서 카메라 모델명 추출

3-2. extract_data.py — 결과 데이터 추출

파이프라인 완료 후 CSV·Shapefile에서 결과를 읽어 API 응답에 포함시키는 유틸리티 모듈.

get_metadata(camTy, fileId)

  • CSV 파일 경로를 카메라 타입에 따라 선택
    • mx15hdi: {camTy}/Metadata/CSV/{fileId}/mx15hdi_interpolation.csv
    • starsafire: {camTy}/Metadata/CSV/{fileId}/Metadata_Extracted.csv
  • 헤더 이후 첫 번째 데이터 행을 쉼표로 이어 문자열 반환

get_oil_type(camTy, fileId)

  • {camTy}/Polygon/Shp/{fileId}/*.shp 에서 첫 번째 Shapefile 로드
  • CRS를 EPSG:4326으로 변환
  • 각 피처에서 class_id, area_m2, volume_m3, note 추출
  • 유류 유형별 두께 매핑 적용 (mm → m 변환)
    • 1(검정/Emulsion): 1.0mm, 2(갈색/Crude): 0.1mm, 3(무지개): 0.0003mm, 4(은색): 0.0001mm
  • 반환값: [{classId, area, volume, note, thickness, wkt}, ...]

3-3. dbInsert_csv.py — CSV 메타데이터 DB 저장

현재 API에서는 주석 처리되어 있으며 독립 실행(CLI)으로도 사용 가능한 스크립트.

  • CSV에서 촬영 일시 및 대상 위경도(DMS → 십진수 변환)를 파싱
  • env_safe.unmnd_poll_info 테이블에 INSERT
  • 카메라 타입에 따라 CSV 컬럼 파싱 방식이 다름
    • mx15hdi: Date1/Date2/Date3, Time1/Time2/Time3, DMS 6필드
    • starsafire: Date, Time, DMS 4필드(초 없음)

3-4. dbInsert_shp.py — Shapefile 유류 폴리곤 DB 저장

현재 API에서는 주석 처리되어 있으며 독립 실행(CLI)으로도 사용 가능한 스크립트.

  • Shapefile에서 유류 폴리곤 피처를 읽어 env_safe.poll_mat_info 테이블에 배치 INSERT
  • 폴리곤 지오메트리를 ST_GeomFromText(wkt, 4326)으로 저장
  • 컬럼: poll_id, algo_ty(유류명), mat_ty(note), mat_area, mat_thick, mat_vol, mat_geom

3-5. pic_gps.py — 드론/폰 사진 스티칭 + GPS 보존

/stitch 엔드포인트가 호출하는 이미지 합성 전용 스크립트. CLI 독립 실행도 지원.

  • 스티칭 모드: drone(SCANS 우선) / phone(PANORAMA 우선), 실패 시 폴백 가능
  • 전처리: 최대 해상도 리사이즈, CLAHE 대비 보정(--enhance)
  • GPS 전략: center(GPS 중앙값), first(첫 이미지 GPS), none
    • Haversine 공식으로 중심 좌표에서 가장 가까운 이미지 선택
  • 출력: GPS EXIF가 삽입된 JPEG 합성 이미지

3-6. mx15hdi/Main/Combine_module.py — 파이프라인 오케스트레이터

/run-script/ 엔드포인트가 subprocess로 실행하는 메인 파이프라인 스크립트.

  • 커맨드라인 인자: fileId
  • 다음 스크립트를 순서대로 실행:
    1. ../Detect/Inference.py
    2. ../Metadata/Scripts/Export_Metadata_mx15hdi.py
    3. ../Georeference/Scripts/Create_Georeferenced_Images_nadir.py
    4. ../Polygon/Scripts/Oilshape.py

3-7. mx15hdi/Detect/Inference.py — AI 세그멘테이션 추론

MMSegmentation 기반 유류 세그멘테이션 모델을 실행하는 스크립트.

  • 모델: V7_SPECIAL.py 설정 + epoch_165.pth 가중치, cuda:0 디바이스
  • 분류 클래스: background, black, brown, rainbow, silver (5클래스)
  • 처리 흐름:
    1. Original_Images/{fileId}/ 내 이미지 파일 열거
    2. inference_segmentor()로 세그멘테이션 맵 생성
    3. 팔레트 기반 컬러 마스크 생성 (alpha=0.6 블렌딩)
    4. 블렌딩 이미지 → Detect/result/{fileId}/
    5. 컬러 마스크 이미지 → Detect/Mask_result/{fileId}/

3-8. mx15hdi/Metadata/Scripts/Export_Metadata_mx15hdi.py — 메타데이터 추출

이미지의 EXIF 정보를 파싱하여 CSV로 저장하는 스크립트.

  • meta_info.extract_and_save_image_metadata(): Pillow로 EXIF 읽기
    • 촬영 시각(DateTimeOriginal), GPS(위도/경도/고도) 추출
    • DMS 형식으로 변환하여 CSV 저장 (mx15hdi.csv)
    • 컬럼: Filename, Tlat_d/m/s, Tlon_d/m/s, Alat_d/m/s, Alon_d/m/s, Az, El, Alt, Date1/2/3, Time1/2/3
  • meta_info.geo_info(): OCR(PaddleOCR)로 이미지 HUD에서 메타데이터 추출 (현재 주석 처리)
  • meta_info.interpolation(): 결측값 전방/후방 보간 후 mx15hdi_interpolation.csv 저장

3-9. mx15hdi/Georeference/Scripts/Create_Georeferenced_Images_nadir.py — 지리참조 변환

나디르(수직 하향) 촬영 이미지를 GeoTIFF로 변환하는 스크립트.

  • 카메라 파라미터: Hasselblad L2D-20c 기준 (초점거리 12.29mm, 4/3" 센서)
  • 처리 흐름:
    1. mx15hdi_interpolation.csv에서 위경도(DMS) 및 고도(feet) 로드
    2. EPSG:4326 → EPSG:5187(한국 TM 좌표계) 변환
    3. 핀홀 카메라 투영으로 픽셀 → 지상 좌표 매핑
    4. GSD(지상 샘플 거리) 계산 및 출력 해상도 결정
    5. 블렌딩 이미지 → Georeference/Tif/{fileId}/*_gsd.tif
    6. 마스크 이미지 → Georeference/Mask_Tif/{fileId}/*_gsd.tif
  • 결측 픽셀 보완: binary_dilation + cv2.dilate 2회 반복

3-10. mx15hdi/Polygon/Scripts/Oilshape.py — 유류 폴리곤 생성

마스크 GeoTIFF에서 유류 영역을 폴리곤으로 추출하여 Shapefile로 저장하는 스크립트.

  • get_class_mask(): RGB 마스크를 유클리드 거리 기반으로 클래스 ID 마스크로 변환
  • mask_to_polygons(): cv2.findContours(RETR_CCOMP)로 외곽선 추출, 홀(내부 폴리곤) 처리, approxPolyDP로 단순화
  • save_polygons_to_shapefile():
    • 픽셀 좌표 → 지리 좌표 변환 (rasterio.transform.xy)
    • Shapely Polygon 생성 + .buffer(0)으로 geometry 정규화
    • 면적(m²), 두께(mm→m), 부피(m³), note 계산 후 GeoDataFrame 저장
    • 출력: Polygon/Shp/{fileId}/*.shp

4. API 엔드포인트 상세

POST /run-script/

전체 분석 파이프라인을 실행하고 결과를 반환하는 메인 엔드포인트.

입력 (multipart/form-data)

파라미터 타입 필수 설명
camTy string 카메라 타입. "mx15hdi" 또는 "starsafire"
fileId string 분석 세션 식별자 (파일 저장 폴더명으로 사용)
image file 분석할 이미지 파일 (PNG/JPG)

처리 흐름

  1. camTy 유효성 검사
  2. 이미지를 {camTy}/Metadata/Image/Original_Images/{fileId}/에 저장
  3. GPS EXIF 검증 — GPS 없으면 {"detail": "GPS Infomation Not Found"} 반환
  4. Combine_module.py {fileId} subprocess 실행 (타임아웃 300초)
  5. get_metadata() + get_oil_type() 호출

출력 (200 OK)

{
  "meta": "filename,lat_d,lat_m,lat_s,...",
  "data": [
    {
      "classId": "검정",
      "area": 152.3,
      "volume": 0.1523,
      "note": "Black - Emulsion",
      "thickness": 0.001,
      "wkt": "POLYGON ((...))"
    }
  ]
}

에러 응답

코드 조건
400 camTy가 유효하지 않은 경우
404 Combine_module.py 파일이 없는 경우
500 subprocess 타임아웃 또는 기타 오류

GET /get-metadata/{camTy}/{fileId}

이미 처리된 결과에서 메타데이터와 유류 정보를 조회하는 엔드포인트 (파이프라인 실행 없음).

입력 (Path Parameters)

파라미터 타입 설명
camTy string 카메라 타입 (mx15hdi / starsafire)
fileId string 분석 세션 식별자

출력 (200 OK)

/run-script/와 동일한 응답 구조.

{
  "meta": "filename,lat_d,lat_m,...",
  "data": [ { "classId": "갈색", "area": ..., "wkt": "..." } ]
}

GET /get-original-image/{camTy}/{fileId}

저장된 원본 이미지를 Base64 문자열로 반환하는 엔드포인트.

입력 (Path Parameters)

파라미터 타입 설명
camTy string 카메라 타입
fileId string 분석 세션 식별자

처리: {camTy}/Metadata/Image/Original_Images/{fileId}/ 디렉토리에서 .png 또는 .jpg 파일을 찾아 Base64 인코딩

출력 (200 OK)

"<base64 인코딩된 이미지 문자열>"

GET /get-image/{camTy}/{fileId}

지리참조된 GeoTIFF를 PNG로 변환하여 좌표 경계(Bounding Box)와 함께 반환하는 엔드포인트.

입력 (Path Parameters)

파라미터 타입 설명
camTy string 카메라 타입
fileId string 분석 세션 식별자

처리:

  1. {camTy}/Georeference/Tif/{fileId}/*.tif 파일 탐색
  2. rasterio로 CRS 및 Bounds 추출
  3. CRS가 EPSG:4326이 아닌 경우 pyproj로 변환
  4. raster 데이터를 PNG로 변환 후 Base64 인코딩

출력 (200 OK)

{
  "minLon": 126.123456,
  "minLat": 34.123456,
  "maxLon": 126.234567,
  "maxLat": 34.234567,
  "image": "<base64 인코딩된 PNG 문자열>"
}

POST /stitch

여러 장의 드론/폰 사진을 스티칭하여 합성 이미지를 반환하는 엔드포인트.

입력 (multipart/form-data)

파라미터 타입 필수 설명
files file[] 합성할 이미지 파일 목록 (최소 2장)
fileId string 저장 디렉토리 식별자

처리 흐름

  1. 파일 수 최소 2장 검증
  2. 각 파일의 카메라 모델명 추출 (check_camera_info)
  3. stitch/{fileId}/ 디렉토리에 파일 저장 (파일명 형식: {model}_{idx:03d}_{원본파일명})
  4. 가장 많이 나온 카메라 모델을 --model 인자로 사용
  5. pic_gps.py --mode drone --input ... --out ... --model ... --enhance subprocess 실행 (타임아웃 300초)

출력 (200 OK)

합성된 JPEG 이미지 파일 (FileResponse, image/jpeg)

에러 응답

코드 조건
400 이미지가 2장 미만인 경우
500 스티칭 subprocess 실패

5. 디렉토리 구조

prediction/image/
├── api.py                          FastAPI 서버 (포트 5001)
├── extract_data.py                 결과 데이터 추출 유틸리티
├── dbInsert_csv.py                 CSV → PostgreSQL (현재 비활성화)
├── dbInsert_shp.py                 Shapefile → PostgreSQL (현재 비활성화)
├── pic_gps.py                      이미지 스티칭 + GPS 보존
├── mx15hdi/                        mx15hdi 카메라 처리 모듈
│   ├── Main/
│   │   └── Combine_module.py       파이프라인 오케스트레이터
│   ├── Detect/
│   │   ├── Inference.py            MMSeg AI 세그멘테이션
│   │   ├── V7_SPECIAL.py           모델 설정
│   │   ├── epoch_165.pth           학습된 모델 가중치
│   │   └── mmsegmentation/         MMSegmentation 라이브러리
│   ├── Metadata/
│   │   ├── Image/Original_Images/  업로드 이미지 저장소
│   │   ├── CSV/                    메타데이터 CSV 출력
│   │   └── Scripts/
│   │       └── Export_Metadata_mx15hdi.py
│   ├── Georeference/
│   │   ├── Tif/                    컬러 GeoTIFF 출력
│   │   ├── Mask_Tif/               마스크 GeoTIFF 출력
│   │   └── Scripts/
│   │       └── Create_Georeferenced_Images_nadir.py
│   ├── Polygon/
│   │   ├── Shp/                    Shapefile 출력
│   │   └── Scripts/
│   │       └── Oilshape.py
│   └── GSD/                        GSD 중간 데이터
├── starsafire/                     starsafire 카메라 처리 모듈 (구조 동일)
└── stitch/                         스티칭 결과 저장소

6. 주요 의존성

라이브러리 용도
fastapi, uvicorn API 서버
rasterio, pyproj, osgeo(GDAL) 지리참조·좌표 변환
geopandas, shapely Shapefile 처리
mmsegmentation, torch AI 세그멘테이션
paddleocr HUD OCR (starsafire 전용)
opencv-contrib-python 이미지 처리·스티칭
Pillow, piexif EXIF 메타데이터
psycopg2 PostgreSQL 연결
pandas, numpy 데이터 처리