2026-10-14

GeoAI 실제 QGIS 실습 17 — Mask R-CNN 개별 객체 분할 학습과 필지 추론

본 실습에서는 NaGeoAI의 개별 객체 분할(인스턴스 분할) 패널을 이용하여 농경지 필지 경계 라벨로 Mask R-CNN 모델을 학습하고, 학습에 쓰지 않은 시험 타일에 추론하여 필지별 객체 ID 래스터를 생성하였다. 2026년 10월 14일 QGIS 데스크톱(3.44.15)에서 학습·추론·결과 표시와 프로젝트 저장·정상 종료까지 실제로 수행하였다. 이번 회차는 사람이 마우스로 조작하지 않았다. QGIS 시작 스크립트가 NaGeoAI 패널의 입력란을 채우고 버튼을 눌러 실행했으며, 본문의 그림은 그 스크립트가 QGIS 창을 캡처한 것이다. 플러그인 코드와 작업 스레드는 사람이 직접 누를 때와 같은 경로로 동작하며, 아래 메뉴 경로와 입력값으로 손으로 재현할 수 있다. 실습 16의 의미론적 분할이 픽셀마다 "건물/배경"만 구분했다면, 인스턴스 분할은 같은 종류의 객체라도 하나하나에 서로 다른 번호를 붙인다는 점이 다르다.

1. 플러그인 설치와 예제 다운로드

현재 설치 환경은 QGIS 3.44.15-Solothurn과 NaGeoAI 1.9.1이다. NaGeoAI 배포 페이지에서 설치용 ZIP을 확인한 뒤 플러그인 → 플러그인 관리 및 설치 → ZIP에서 설치로 설치하고 플러그인을 활성화한다. 이번 실행에서는 재설치하지 않았다.

입력 자료는 field-boundaries.zip 원본 다운로드로 준비한다. Source Cooperative의 Open Geospatial Solutions 자료 페이지는 GeoAI 모델 학습용 공개 접근 자료로 안내하고 있다. 개별 파일의 재배포 라이선스를 별도로 확인한 것으로 간주하지 않으며, 원본 자료와 학습된 모델 파일은 재배포하지 않는다. 받은 ZIP의 SHA-256은 d2c9f0e8f6b5c9d194f6759087dbfd0c988eb429ae8f4757fb0baf49a9bc5272였고, 원본 ZIP은 보존한 채 별도 작업 폴더에 압축을 풀었다.

원본 QGIS_Play.qgz를 열고 프로젝트 → 다른 이름으로 저장에 해당하는 방식으로 QGIS_Play_NaGeoAI_20261014_instseg17_fields_run.qgz 복사본을 만들었다(최초 5,711바이트, 레이어 0개, 표시 좌표계 EPSG:5187). 원본 파일의 SHA-256은 실행 전후 동일하였다.

2. 입력 자료

폴더용도구성
images/학습 영상 타일724개, 256×256, 4밴드 uint8, EPSG:4326, 약 5.405×10-5°
labels/학습 라벨 타일724개, 같은 격자의 1밴드 객체 ID(필지마다 다른 정수)
test/추론용 타일(라벨 없음)5개, 이번 회차는 g0_00001_7.tif 1개 사용

학습 타일 중 g0_00001_1.tif의 영상과 라벨을 레이어 → 레이어 추가 → 래스터 레이어 추가로 불러와 라벨 구조를 먼저 확인하였다. 라벨은 0이 배경이고 필지마다 서로 다른 정수가 들어 있어 회색조로 보면 필지마다 밝기가 다르게 표시된다. 프로젝트 표시 좌표계 EPSG:5187로 그려지므로 화면의 회전된 모습은 표시 변환 때문이며 원본을 재투영한 것은 아니다.

3. Mask R-CNN 학습

NaGeoAI → 인스턴스 분할 → 2. 학습 탭에서 다음 값을 사용한다. 이 자료는 이미 타일과 객체 ID 라벨로 준비되어 있으므로 1. 데이터 생성 탭은 사용하지 않았다.

항목입력값
형식directory
Images / Labels압축 해제 폴더의 images, labels
Channels / Classes4 / 2(배경·필지)
배치 크기 / Epochs / LR / Val Split4 / 10 / 0.005 / 0.20(패널 기본값)
Output새 models 폴더
인스턴스 분할 패널 학습 탭에 directory 형식, Channels 4, Classes 2, 배치 4, 10에폭이 입력되고 학습 타일과 라벨이 지도에 표시된 QGIS 화면
그림 1. 학습 탭 입력값과 학습 타일 g0_00001_1의 영상·라벨. 회색조 라벨의 밝기 차이가 필지별 객체 ID이다. 그림을 클릭하면 원본 크기로 확인할 수 있다.

학습 시작을 누르면 별도 작업 스레드에서 학습이 진행되고 하단 로그에 에폭별 손실과 IoU가 표시된다. 이 PC(RTX 3080 Ti)에서 10에폭에 약 11분(668.9초)이 걸렸고, 완료 후 best_model.pth 저장 메시지와 함께 3. 추론 탭으로 자동 전환되었다.

학습 완료 후 Model saved 메시지 상자와 최고 검증 IoU 0.7218 로그가 보이는 QGIS 화면
그림 2. 학습 완료 메시지와 하단 로그의 최종 검증 지표. 그림을 클릭하면 원본 크기로 확인할 수 있다.
에폭12345678910
학습 손실1.90871.45421.39251.34501.31491.28281.26461.24601.23551.2208
검증 IoU0.61360.67350.67580.71080.72100.68750.70870.69830.72180.6994

패널 요약에는 최고 검증 IoU 0.7218, 최종 검증 손실 1.3195가 기록되었다. 이 값은 같은 자료 묶음에서 무작위로 떼어 낸 20% 타일로 계산한 것이며, 인접 타일이 학습과 검증에 나뉘어 들어갈 수 있으므로 지역이 분리된 독립 평가가 아니다. 또한 10에폭의 짧은 학습이어서 IoU가 에폭마다 오르내리고 있어 수렴했다고 보기 어렵다. 이 수치를 필지 경계 추출 정확도로 해석하지 않는다.

4. 시험 타일 추론

3. 추론 탭에서 래스터를 test/g0_00001_7.tif로 지정한다. 모델 경로에는 학습 직후 best_model.pth가 자동 입력된다. Channels 4, Classes 2가 학습 설정과 같은지 확인하고, 타일 크기에 맞추어 Window 256, Overlap 128로 바꾼다. 신뢰도 0.50, Batch 4는 기본값이며 범위는 전체 래스터(Full raster)이다. 출력 파일을 새 폴더에 지정하고 결과를 지도에 추가를 켠 뒤 추론 실행을 누른다.

추론 탭에 시험 타일 g0_00001_7, best_model.pth, Window 256, Overlap 128, 신뢰도 0.50이 입력된 QGIS 화면
그림 3. 추론 탭 입력값과 학습에 쓰지 않은 시험 타일. 그림을 클릭하면 원본 크기로 확인할 수 있다.

추론은 1개 창을 처리하여 1초 미만(로그 0.67초)에 끝났다. 로그에는 NMS 전 92개, NMS 후 17개 검출이 기록되었고 객체 ID·클래스·점수 래스터 3개가 저장되었다. 그런데 완료 직후 "No instances were detected" 경고 상자가 표시되었다. 플러그인 코드를 확인한 결과, 객체 수를 세는 부분이 QGIS 내부 Python에서 rasterio를 불러오지 못하면 개수를 0으로 둔 채 넘어가도록 되어 있다. 이 환경의 QGIS 내부 Python에는 rasterio가 없어 실제 결과와 무관하게 경고가 뜬 것이다. 아래 5절의 독립 검사에서 출력에는 객체 17개가 들어 있음을 확인하였으므로, 이 경고는 메시지 표시상의 문제로 판단한다.

추론 후 No instances were detected 경고 상자가 떴지만 하단 로그에는 Final instances 17이 표시되고 지도에 결과 레이어가 추가된 QGIS 화면
그림 4. 경고 상자와 하단 로그의 "Final instances: 17"이 서로 다르게 표시된 화면. 지도에는 결과 레이어가 회색조로 추가되어 있다. 그림을 클릭하면 원본 크기로 확인할 수 있다.

결과 레이어는 회색조로 추가되므로 객체를 구분하기 어렵다. 레이어 속성 → 심볼 → 렌더 유형: 팔레트/고유값 → 분류에서 무작위 색상을 지정하고 값 0을 투명으로, 불투명도를 70%로 두면 객체별로 색이 다르게 보인다. 이번 실행에서는 시작 스크립트가 같은 설정을 적용하였고 학습 타일 레이어는 표시를 껐다.

시험 타일 위에 객체 17개가 서로 다른 색으로 표시된 인스턴스 분할 결과 화면
그림 5. 객체 ID별 무작위 색상으로 표시한 추론 결과. 영상 가운데 아래쪽 넓은 지역은 객체로 검출되지 않았다. 그림을 클릭하면 원본 크기로 확인할 수 있다.

5. 출력 래스터 검증

객체 ID 래스터는 QGIS 화면과 별도로 rasterio 읽기 전용 검사로 확인하였다.

항목검증 결과
CRS·크기EPSG:4326, 256×256
밴드·자료형·NoData1밴드, uint32, NoData 0
입력 대비 격자CRS·너비·높이·affine transform 일치
객체 수17개(ID 1~17)
픽셀 수배경 49,358 / 객체 합계 16,178(약 24.7%)
객체당 픽셀 수최소 9, 최대 4,734
파일 크기262,758바이트

출력 SHA-256은 9dc85c662e6c2278f2c0ab393565a4a4e5d2b32e38955c56a70b6e72db4f4cb2이며 입력 시험 타일의 SHA-256은 0356f52aaa8897c1d7f2bb0c8775aa02c33d3cf9884712c9e8006e24c8b6f7a4이다. 시험 타일에는 정답 라벨이 없으므로 이번 회차에서는 검출된 필지가 실제 필지 경계와 일치하는지 평가하지 않았다. 9픽셀처럼 매우 작은 객체도 포함되어 있어, 실무에서는 최소 면적 기준으로 걸러 내는 후처리가 필요할 수 있다. 패널 아래쪽의 벡터화(선택 사항)로 폴리곤을 만들 수 있으나 이번 회차에서는 실행하지 않았다.

6. 프로젝트 저장과 정상 종료

마지막으로 실습 복사본을 저장하였다. 저장 후 파일 크기는 9,922바이트로 갱신되었고 저장 직후 미저장 상태가 아님(isDirty false)을 확인하였다. 원본 QGIS_Play.qgz의 SHA-256은 실행 전후 동일하였다. 이후 QGIS 자체의 프로젝트 → QGIS 종료 동작으로 정상 종료하였고 QGIS 프로세스가 남아 있지 않은 것을 확인하였다. 강제 종료하지 않았다.

본 실습은 공개 예제 타일로 소규모 Mask R-CNN을 짧게 학습하는 절차를 보여 주며, 측량 성과 검증이나 독립 자료 기반 정확도 평가는 포함하지 않는다. 실제 업무에 적용하려면 지역이 분리된 검증 자료와 지적·현장 기반 참값으로 별도 평가해야 한다. 다음 회차에서는 분할 결과의 벡터화 등 아직 다루지 않은 NaGeoAI 기능을 이어서 확인한다.

댓글 없음:

댓글 쓰기

GeoAI 실제 QGIS 실습 19 — DeepForest 탐지 결과를 PASCAL VOC·COCO·YOLO 학습 자료로 내보내기

본 실습에서는 NaGeoAI 수목 분할(DeepForest) 패널로 나무를 탐지한 뒤, 출력 탭의 학습 데이터 내보내기 로 탐지 결과를 PASCAL VOC·COCO·YOLO 세 가지 학습 자료 형식으로 저장하였다. 모델이 찾은 결과를 사람이 검토·수정...