航少

航少

Segment Anything Simple Web demo的踩坑經歷

前兩天參加面試,面試官喊我運行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 告訴你應該裝啥

接下來下載 Model Checkpoints
默認的是vit_h

然後 fb 的文檔直接喊你 Export the image embedding 導出圖像遮罩,實際上是有問題的
我建議先導出 ONNX model,因為 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 圖中展示掩碼、點和框,如果你單純跑 web 似乎不需要

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)

後面就是這個文檔中兩個錯誤的地方

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)

上面第一行代碼意思是從 src/assets/ 讀取 dogs.jpg,但是實際上 src/assets/ 裡面根本沒有圖片,圖片是在 src/assets/data 裡,應該把那張圖片複製到 src/assets/ 目錄下
第二個問題是 fb 文檔說Save the new image and embedding in src/assets/data,會把處理完的圖片和遮罩保存到 src/assets/data 裡,實際上第一它根本不會保留處理過的圖片,第二它遮罩的那個.npy 文件實際上保存在你運行 python 的那個目錄
這就導致一個 bug,sam 接受的最長邊長是 1024 像素,進行預處理的時候對圖片進行一定程度的縮放拉伸,具體處理的應該是 SamPredictor 裡__init__的時候會調用的 ResizeLongestSide 函數,在瀏覽器運行的時候是 helpers/scaleHelper.tsx 處理,前者有四捨五入後者沒有,並且執行完以後並沒有保存圖片,把原圖和 npy 放到 src/assets/data 的話,最後打開圖片會發現遮罩出現一定程度的偏移

一種解決方法是你在 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/
大功告成
image

image

額外的學習內容,在研究這個 demo 如何在瀏覽器中啟用多線程的時候我還學到了跨域
跨域原因
域名不同:比如從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 方式請求服務器,服務器返回一段可執行的 JavaScript 代碼。

CORS(跨域資源共享):在服務端設置相應的響應頭,允許跨域請求。通過在服務器端設置 Access-Control-Allow-Origin 等相關響應頭,來告訴瀏覽器允許哪些域名的訪問。

代理服務器:在同源策略下,通過在同一域名下建立一個代理服務器,將客戶端請求發送到目標服務器,並將響應返回給客戶端。

WebSocket:使用 WebSocket 協議進行雙向通信,WebSocket 不受同源策略的限制。

demo 裡具體是創建一個 “跨源隔離狀態”(Cross-Origin Isolation State)。設置一組特定的 HTTP 響應標頭來指示瀏覽器將網頁視為獨立的原始來源,以啟用 SharedArrayBuffer 的使用,實現跨域

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。