SAM (Segment Anything Model)の基礎とGISで使う方法

# 概要

機械学習初心者がSegment Anything Model (SAM)をGISで応用的に使いたかったのでその概要や使い方をメモしておく。
SAMを使うと，ラスターがベクターにできる……？！

# Segment Anything Modelとは

Meta社が開発した，画像中の物体を分類するモデル。

https://segment-anything.com/

公式ウェブサイトはキャッチ―で分かりやすいのだけれど，下記arXivの論文を確認してみることにする。全部読もうかと思ったけど，とりあえずイントロを読めば良さそうなのでイントロだけ。

https://arxiv.org/abs/2304.02643

# Segment Anything論文の解説

## Introduction [^intro]

[^intro]: とりあえずイントロだけ。

### 背景～ゴール

- 大規模言語モデルはすごいよね。*zero-shot*とか*few-shot*で，訓練に使った以上の汎化性能が発揮されてるよね。こういうのを基礎モデル（*foundation model*）と呼ぶよ。
    - *zero-shot*では，追加訓練なしで結果が出力される
    - *few-shot*では，わずかな追加指示（プロンプト/*prompt engineering*）で結果が出力される
- コンピューター・ヴィジョンでも，自然言語ほどではないけれども，基礎モデルが研究されているよ。テキストと画像に関するモデルが有名だね。
    - *zero-shot*で画像からテキストを生成したり，テキストから画像を生成したりするよ。
    - でも，コンピューター・ビジョンには，この様な問題設定を超えるものもあるし，訓練に使えるデータが少ないよ。
- この研究では，画像のセグメンテーション（*image segmentation*）に関する基礎モデルを開発することがゴールだよ。
    - 指示可能なモデル(*promptable model*)を作るよ
    - タスクを使った幅広いデータに基づく，訓練済みモデルを作るよ
    - 高い汎化性能を持つよ

### キークエスチョン

3つの要素：タスク（**task**），モデル（**model**），データ（**data**），が，この研究の成功を左右するので，次のようなキークエスチョンを定めたよ
- *zero-shot*を可能とする**task**は何か
- それに対応する**model**は何か
- この**task**や**model**を可能とする**data**は何か

### **task**について

これまでの基礎モデルに倣って，指示可能なセグメンテーションタスク（*promptable segmentation task*）を提案するよ。次の画像の様に。

![sa-ta.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617623/87ee3fb8-a2ef-85e6-8135-fc2c8e631a48.png)

画像の位置や文字での指示が可能だよ。

### **model**について

- 指示可能なセグメンテーションタスク（*promptable segmentation task*）のために，モデルの要件も定まるよ。
    - フレキシブルな指示（*flexible prompt*）への対応
    - リアルタイム・インタラクティブな処理
    - あいまいさ（*ambiguity-aware*）への対応
- 驚くべきことに（！），シンプルなデザインでこれらの要件が満たされるよ[^1]。画像の様に。これをSAM（Segment Anything Model）と呼ぶよ。
![sa-mo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617623/849ec42e-a68a-e6c4-983d-22d552ab0fbc.png)
- 画像のエンコーダーとプロンプトのエンコーダーを分けることで，計算コストが有利で，リアルタイム・インタラクティブな予測が可能だよ。

[^1]: 機械学習において，エンコードは（潜在的な）特徴の抽出，デコードは抽出された特徴に基づく出力のこと。

### Data engineについて

普通はインターネットからデータを集めるけど，マスクのデータが入手できるケースは少ないから，data engineというアプローチをとるよ。モデルと同時にデータセットを開発したよ[^2]。
- *assisted-manual*: 古典的なやり方と同様，人（annotator）がマスクに注釈を付ける作業をSAMがアシストする
- *semi-automatic*: SAMに一部のオブジェクトについての指示を与えることでマスクを自動生成し，残りのオブジェクトについて人（annotator）が残りのオブジェクトを担当する
- *fully automatic*: 格子点を指示として与え，SAMにマスクを生成させる。

![sa-dt.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617623/6136940c-98ca-150a-2a12-a9343ed2b1e3.png)


[^2]: いまいちよくわかっていない。まずは人がセグメンテーションをしたものを与え，徐々にそれをSAMに受け渡していくということか。

### Datasetについて

データセット（SA-1B）は，1100万の画像と10億以上のマスクが含まれるよ。
既存のデータと比べると400倍くらいのサイズがあるよ。

### Responsible AIについて

差別を助長しない，公平性を持ったAIを作るよ

### Experimentsについて

- 23の新たなデータセットを使ってSAMを評価したよ。
- 画像中の1点を指示することによるマスクの生成については，手作業による分類と比べてごくわずかに劣る程度だったよ
- プロンプトエンジニアリングに基づく様々な下流タスク（エッジ検出，オブジェクト生成提案，インスタンスセグメンテーション，テキストに基づくマスクの探索的な予測）について，定性的・定量的に一貫した結果を示したよ

### Releaseについて

データセットSA-1BとモデルSAMについて，研究目的で公表するよ。

# Segment anything論文で書いていないこと

## Checkpoint

機械学習だと当然の用語なのか……？
学習中のモデルのスナップショットのことをcheckpointと呼ぶらしい。

Segment Anything Modelでは，backbone（ニューラルネットワークにおける，序盤から中盤までの層全体を指すらしい）が異なるいくつかの学習済みモデル（`vit_b` (Base)，`vit_l` (Large)，`vit_h` (Huge)）が提供されている。正確性とパフォーマンスのトレードオフで，どれを選択するか決定する。デフォルトは`vit_h`だが，`vit_l`のバランスが良いらしい。
    - `vit_h`: 正確だが時間がかかる 
    - `vit_l`: 2つのモデルの中間
    - `vit_b`: 高速だがやや不正確

# GISでSAM：Geo-SAM

QGISプラグインになっているようなので，導入してみた。環境はWindows 11 + QGIS 3.32

https://geo-sam.readthedocs.io/en/latest/

## 導入手順

公式（<https://geo-sam.readthedocs.io/en/latest/Installation.html>）と同じだが，メモに残しておく。

1. OSGeo4Wshellを開いて，以下を入力
    ```
    pip3 install torch torchvision
    pip3 install torchgeo
    pip3 install segment-anything
    ```

1. プラグインzipをダウンロード（<https://github.com/coolzhao/Geo-SAM/releases/tag/v1.1.1>）
1. プラグインをQGISプラグインのパス（`%APPDATA%\QGIS\QGIS3\profiles\default\python\plugins`）に展開。ただし，フォルダ名が`Geo-SAM`になるように注意（そのまま展開すると`Geo-SAN-1.1.1`とかになるので）
1. QGISでプラグインを有効化

これで，簡単に使える。

## 使用法

公式の通りなので省略

# GISでSAM：segment-geospatial

適当なtifの全体についてマスクを生成したかった。
ざっと見てGeo-SAMでの方法が分からなかったが，pythonのsegment-geospatialパッケージを使うと簡単にできることがわかった。

https://samgeo.gishub.org/

## 導入手順

こちらもほぼ公式と同じだが

1. 適当なpython環境に`segment-geospatial`をインストール
    ```
    pip install segment-geospatial
    ```
1. 同じく，GDALをインストール。ただし，私のpython環境では`pip`ではうまく行かず，アーカイブ（<https://www.lfd.uci.edu/~gohlke/pythonlibs/>）から適当な`.whl`ファイル（たとえば`GDAL-3.4.3-cp310-cp310-win_amd64.whl`）をダウンロードしてくる必要があった。
    ```
    pip install GDAL-3.4.3-cp310-cp310-win_amd64.whl
    ```
1. SAMのcheckpoint (<https://github.com/facebookresearch/segment-anything#model-checkpoints>)をダウンロードする。

## 使用法

下記のpythonコードで，適当な画像（ここでは1286 x 697の衛星画像，解像度1.2mくらい）についてマスクを生成できる。
ただし，python実行環境にSAMチェックポイント`sam_vit_l_0b3195.pth`を格納している。

```python
from samgeo import SamGeo, tms_to_geotiff

bbox = [-122.2659, 37.8682, -122.2521, 37.8741]
image = "satellite.tif"
tms_to_geotiff(output = image, bbox = bbox, zoom = 17, source = "Satellite", overwrite = True)

sam = SamGeo(model_type = "vit_l", checkpoint_dir=".", sam_kwargs = None)

sam.generate(image, output="masks.tif", foreground=True, unique=True)

sam.tiff_to_vector("masks.tif", "masks.gpkg")
```

これが元画像
![satellite.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617623/d48f5870-b7be-43b7-0e34-67be6947e39b.png)

マスクして，QGISにインポートすると，こんな感じ

![mask.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617623/5d3516f7-61a1-fb28-17cc-ebcebc83e330.png)

精度はともかく，tiffからベクトルが生成できることはわかった。