前の 2 日間は面接に参加しました。面接官は私にSegment Anything Simple Web demoを実行するように指示しました。実際に実行してみると、FB のドキュメントは非常に悪く書かれていることがわかりました。この記事では、この経験を記録しておきます。
まず、プロジェクトの要件は次のとおりです。
python>=3.8 pytorch>=1.7 torchvision>=0.8
torchvision は、pytorch をインストールする際にデフォルトで一緒にインストールされるはずです。
次に、プロジェクトをローカルにクローンします。
pip install git+https://github.com/facebookresearch/segment-anything.git
segment-anything\demo ディレクトリで yarn をインストールします。
npm install --g yarn
その他の環境依存関係もあります。
pip install opencv-python pycocotools matplotlib onnxruntime onnx
後続の実行でライブラリが不足しているというエラーが発生した場合(おそらく sam というものもあると思います)、chatgpt に直接インストールするべきものを教えてもらうことをお勧めします。
次に、モデルのチェックポイントをダウンロードします。
デフォルトはvit_hです。
そして、FB のドキュメントでは直接画像の埋め込みをエクスポートするように指示されていますが、実際には問題があります。
まず、ONNX モデルをエクスポートすることをお勧めします。なぜなら、SAM の軽量マスクデコーダーは ONNX 形式でエクスポートできるからです。これは、ONNX ランタイムをサポートする環境で実行できます。後でブラウザで実行する際に使用します。
以下の --checkpoint --model-type -output をダウンロードしたものに置き換えてください。
python scripts/export_onnx_model.py --checkpoint <path/to/checkpoint> --model-type <model_type> --output <path/to/output>
実行が完了すると、sam_onnx_quantized_example.onnx という名前のモデルファイルが生成されます。このファイルを segment-anything\demo\model ディレクトリに配置します。
次に、マスクの生成プロセスが始まります。FB のドキュメントではインポートの手順が省略されていますが、私のノートにはあります。
まず、必要な関数をインポートします。おすすめは、Model Checkpoints ファイルのディレクトリで Python を実行することです。
import numpy as np
import torch
import matplotlib.pyplot as plt
import cv2
次に、このドキュメントにはインデントエラーがありますが、マスク、ポイント、およびボックスを matplotlib グラフに表示するためのものです。ウェブだけを実行する場合は不要かもしれません。
def show_mask(mask, ax, random_color=False):
if random_color:
color = np.concatenate([np.random.random(3), np.array([0.6])], axis=0)
else:
color = np.array([30/255, 144/255, 255/255, 0.6])
h, w = mask.shape[-2:]
mask_image = mask.reshape(h, w, 1) * color.reshape(1, 1, -1)
ax.imshow(mask_image)
def show_points(coords, labels, ax, marker_size=375):
pos_points = coords[labels==1]
neg_points = coords[labels==0]
ax.scatter(pos_points[:, 0], pos_points[:, 1], color='green', marker='*', s=marker_size, edgecolor='white', linewidth=1.25)
ax.scatter(neg_points[:, 0], neg_points[:, 1], color='red', marker='*', s=marker_size, edgecolor='white', linewidth=1.25)
def show_box(box, ax):
x0, y0 = box[0], box[1]
w, h = box[2] - box[0], box[3] - box[1]
ax.add_patch(plt.Rectangle((x0, y0), w, h, edgecolor='green', facecolor=(0,0,0,0), lw=2))
sam モデルと前処理器をロードします。
import sys
sys.path.append("..")
from segment_anything import sam_model_registry, SamPredictor
sam_checkpoint = "sam_vit_h_4b8939.pth"
model_type = "vit_h"
device = "cuda"
sam = sam_model_registry[model_type](checkpoint=sam_checkpoint)
sam.to(device=device)
predictor = SamPredictor(sam)
その後、このドキュメントの 2 つの間違いがあります。
image = cv2.imread('src/assets/dogs.jpg')
predictor.set_image(image)
image_embedding = predictor.get_image_embedding().cpu().numpy()
np.save("dogs_embedding.npy", image_embedding)
上記の 1 行目のコードは、src/assets/ から dogs.jpg を読み込むことを意味していますが、実際には src/assets/ に画像は存在しません。画像は src/assets/data にありますので、その画像を src/assets/ ディレクトリにコピーする必要があります。
2 番目の問題は、FB のドキュメントが「新しい画像と埋め込みを src/assets/data に保存する」と言っているが、実際には最初に画像を処理していないし、遮蔽の.npy ファイルは実際には Python を実行するディレクトリに保存されます。これにより、1 つのバグが発生します。sam は最大の辺が 1024 ピクセルであることを受け入れるため、画像をある程度のスケーリングやストレッチを行うための前処理が行われます。具体的な処理は、SamPredictor の__init__で呼び出される ResizeLongestSide 関数で行われます。ブラウザで実行する場合は、helpers/scaleHelper.tsx で処理されますが、前者は四捨五入され、後者はされません。また、処理が完了した後に画像が保存されないため、元の画像と npy を src/assets/data に配置すると、最終的に遮蔽がある程度オフセットされることがわかります。
解決策の 1 つは、segment_anything/utils/transforms.py に画像を保存するコードを追加することです。
最も簡単な解決策は、小さな解像度の画像を使用することです。私はテスト済みで完全に問題ありません。終了後、Python を実行するディレクトリに.npy ファイルが生成されます。手間を省くなら、src/assets/data ディレクトリに切り取るだけです。
最後に、App.tsx でファイルパスを設定します。
const IMAGE_PATH = "/assets/data/dogs.jpg";
const IMAGE_EMBEDDING = "/assets/data/dogs_embedding.npy";
const MODEL_DIR = "/model/sam_onnx_quantized_example.onnx";
起動します。
yarn && yarn start
ブラウザのhttp://localhost:8081 / を開きます。
成功です。
追加の学習内容として、ブラウザでマルチスレッドを有効にする方法を研究している間に、クロスオリジンについても学びました。
クロスオリジンの原因
異なるドメイン:たとえば、http://www.example.com のページから http://api.example.com に対して Ajax リクエストを送信すると、ドメインが異なるため、クロスオリジンと見なされます。
異なるプロトコル:たとえば、https://www.example.com のページから http://www.example.com に対して Ajax リクエストを送信すると、プロトコルが異なる(HTTP と HTTPS)ため、クロスオリジンと見なされます。
異なるポート番号:たとえば、http://www.example.com:8080 のページから http://www.example.com:3000 に対して Ajax リクエストを送信すると、ポート番号が異なるため、クロスオリジンと見なされます。
処理方法は次のとおりです。
JSONP(JSON with Padding):動的に script タグを作成し、データを JavaScript のコールバック関数の引数としてラップし、GET 方式でサーバーにリクエストを送信します。
CORS(Cross-Origin Resource Sharing):サーバー側で適切なレスポンスヘッダーを設定し、クロスオリジンリクエストを許可します。サーバー側で Access-Control-Allow-Origin などの関連するレスポンスヘッダーを設定することによって、どのドメインからのアクセスを許可するかをブラウザに伝えます。
プロキシサーバー:同一ドメイン内にプロキシサーバーを作成し、クライアントのリクエストをターゲットサーバーに送信し、レスポンスをクライアントに返します。
WebSocket:WebSocket プロトコルを使用して双方向通信を行います。WebSocket は同一オリジンポリシーの制限を受けません。
デモでは、"Cross-Origin Isolation State"(クロスオリジン分離状態)を作成します。特定の HTTP レスポンスヘッダーを設定して、ブラウザにウェブページを独立したオリジンとして扱うように指示し、SharedArrayBuffer の使用を有効にします。これにより、クロスオリジンが実現されます。