Moments/GIS

스트리트뷰로 본 녹시율(Green View Index) 지도 그려보기 🌳🗺️ [딥러닝 Semantic segmentation + Green View Index]

행복한 메타몽 2025. 9. 4. 02:43

지난번에는 스트리트뷰 이미지를 딥러닝으로 분할해서 도로, 보도, 자동차, 나무를 구분하고, 그중 식생 픽셀 비율로 녹시율(GVI)을 계산해 봤다. 이번에는 한 장 한 장 이미지 단위로 끝내는 게 아니라, 좌표와 함께 모아서 지도 위에 시각화 해보려고 한다.
“보행자가 체감하는 도시 녹시율”을 한눈에 보여주는 지도를 만드는 거다. 레츠꼬 🌳🗺️

 

이전 게시글 보기  👉

https://happy-metamong.tistory.com/3

 

스트리트뷰 이미지에서 도로/보도/자동차/나무 의미론적 분할해보기🌳🚗 [딥러닝 Semantic segmenta

얼마 전엔 따릉이 데이터로 지도 시각화를 했는데, 이번엔 조금 색다른 걸 해보려 한다.스트리트 뷰 이미지를 가져와서 도로, 보도, 자동차, 나무, 건축물 이런 요소들을 이미지에서 분할하는 거

happy-metamong.tistory.com

 

 


 

1. 데이터 준비하기

녹시율을 지도에 그리려면 스트리트뷰 이미지 + 좌표 정보가 필요하다. 먼저, 시각화하고 싶은 영역의 도로 라인이 필요하므로 V-World에서 도로중심선 정보를 다운받아준다. 

 

👉  V-World 도로중심선 자료

 

도로중심선 shp 파일 불러온 모습
도로중심선 shp 파일 불러온 모습

 

shp 파일을 불러와 필요한 구간만 추출해 준다. 나는 예시로 잠실 석촌호수와 방이동 근처만 시각화 해보려고 한다.

잠실 석촌호수와 방이동 근처
잠실 석촌호수와 방이동 근처

 

그다음 포인트 생성 도구로 도로망을 따라 일정 간격(10m)으로 포인트를 생성한다. 이 포인트는 스트리트뷰 이미지를 매칭할 기준 좌표가 된다.

백터 도형 → 도형 경계를 따르는 포인트 → 거리에 '10' 미터 입력
백터 도형 → 도형 경계를 따르는 포인트 → 거리에 '10' 미터 입력

 

 

이제 이 좌표에 해당하는 스트리트뷰 이미지를 다운받으면 되는데, 원래는 구글 스트리트뷰 API를 사용하려고 했으나.. 얼마나 나올지 과금이 두려워 (전에 과제하면서 구글 API 썼다가 10만 원이 나왔다..!) 그냥 카카오맵에서 내가 캡처했다. 약간의 노다가는 정신을 맑게 해 준다ㅎㅎ 무튼 아래처럼 녹시율을 계산할 스트리트뷰 이미지를 준비하면 된다.

 

카카오맵에서 캡쳐한 스트리트뷰 이미지
카카오맵에서 캡쳐한 스트리트뷰 이미지

 

 

2. 녹시율 (Green View Index) 계산

이제 준비된 스트리트뷰 이미지를 딥러닝 모델에 넣어서 녹시율(Green View Index, GVI)을 계산해 준다.
여기서는 지난번에 사용했던 SegFormer (nvidia/segformer-b4-finetuned-ade-512-512) 모델을 그대로 활용한다. ADE20K 데이터셋으로 학습된 모델이라 도로·보도·차량·건물·식생 같은 도시 장면을 잘 분할한다.

녹시율 계산 과정은 간단하다:

  1. 이미지를 모델에 입력해 픽셀 단위 분할 결과 획득
  2. 클래스 중 tree, plant, grass 픽셀만 필터링
  3. 전체 픽셀 대비 식생 픽셀 비율(%) 계산
  4. 좌표와 매칭 후 ID, 위도/경도, GVI 값을 CSV로 저장

(지난번 코드에 이어서 작성)

# from transformers import SegformerFeatureExtractor, SegformerForSemanticSegmentation
# from PIL import Image
# import torch
# import matplotlib.pyplot as plt
# import numpy as np

import pandas as pd
import os

# 1. 경로설정
INPUT_CSV  = "gis data/잠실_도로중심선_part2_point.csv" # 원본 CSV
OUTPUT_CSV = "gis data/잠실_도로중심선_part2_point_GVI.csv" # 결과 CSV
ID_COL     = "ID" # ID 컬럼명
IMAGE_DIR  = "raw data" # 이미지 폴더
IMAGE_EXT  = ".png" # 확장자


# 2. 유니크 ID만 GVI 계산 → CSV에 조인
def compute_gvi(image_path: str) -> float:
    """단일 이미지의 GVI(%) 계산. 실패 시 NaN."""
    try:
        img = Image.open(image_path).convert("RGB")
    except Exception as e:
        print(f"[WARN] 이미지 열기 실패: {image_path} ({e})")
        return np.nan

    ow, oh = img.size
    with torch.no_grad():
        _inputs = feature_extractor(images=img, return_tensors="pt")
        _outputs = model(**_inputs)
        _pred = _outputs.logits.argmax(dim=1)[0].cpu().numpy()

    _pred_resized = Image.fromarray(_pred.astype(np.uint8)).resize((ow, oh), Image.NEAREST)
    _pred_np = np.array(_pred_resized)
    _green_mask = np.isin(_pred_np, green_ids)

    g = float(_green_mask.sum())
    t = float(_pred_np.size)
    return (g / t * 100.0) if t > 0 else np.nan

# 3. CSV에서 유니크 ID만 추출
df = pd.read_csv(INPUT_CSV)
unique_ids = pd.Series(df[ID_COL].astype(str).unique())

# 4. GVI 계산
gvi_records = []
for sid in unique_ids:
    img_path = os.path.join(IMAGE_DIR, f"{sid}{IMAGE_EXT}")
    gvi = compute_gvi(img_path)
    gvi_records.append({"_ID_str": sid, "GVI": round(gvi, 2) if pd.notna(gvi) else np.nan})

gvi_df = pd.DataFrame(gvi_records)

# 5. 원본 df에 ID로 조인(타입 혼동 방지)
df["_ID_str"] = df[ID_COL].astype(str)
df = df.merge(gvi_df, on="_ID_str", how="left").drop(columns=["_ID_str"])

# 6. 저장
df.to_csv(OUTPUT_CSV, index=True, encoding="utf-8")

 

녹시율이 제대로 생성되었는지 몇 개만 예시로 보면, 첫 번째 뷰는 녹시율 0.00%, 두 번째 뷰는 49.21%로 계산이 아주 잘 된 걸 볼 수 있다.

녹시율 계산 결과
녹시율 계산 결과

 

 

3. QGIS에서 시각화하기

좌표(lat, lon)와 각 지점별 GVI 값이 붙었으니 이제 QGIS에서 불러와 시각화하면된다. 좌표가 있는 CSV 파일 불러오는 방법은 데이터 소스 관리자에서 "구분자로 분리된 텍스트 레이어 추가" 로 가져오면 된다.

 

데이터 소스 관리자 → 구분자로 분리된 텍스트 레이어 → 도형 정의 체크 -> 위경도 좌표 입력
데이터 소스 관리자 → 구분자로 분리된 텍스트 레이어 → 도형 정의 체크 -> 위경도 좌표 입력

 

짜잔. 불러와진 모습. 이제 레이어 스타일만 주면 된다. 

잘 불러와진 모습
잘 불러와진 모습

 

어두운 배경지도에 단계구분도로 GVI 색상을 줬다. 시각화는 간단해서 뭐 설명할 게 없다.

짜잔
짜잔

 

인쇄 조판에서 축척이랑 범례를 넣어준다. 제목이랑 범례는 포토샵 가서 넣어주겠다.

인쇄 조판에서 범례랑 축척을 넣은 모습
인쇄 조판에서 범례랑 축척을 넣은 모습

 

 

짜잔 진짜 완성!

이렇게 해서 잠실 일대 스트리트뷰 녹시율 지도를 만들어봤다. Mit senseable city lab에서 예전에 Treepedia라는 프로젝트로 전 세계 도시 녹시율을 비교했었는데, 나는 좀 더 로컬하게 동네 단위로 계산해 본 셈이다. 보행자의 시선에서 본 체감 녹지를 이렇게 지도 위에 표현할 수 있다는 게 재밌다. 지역별로 비교하거나 아니면 NDVI 같은 지표와 비교해 보면 더 흥미로운 결과가 나올 것 같다. 끝! 🌳🗺️

 

결과