initial release
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
#
|
||||
# sam
|
||||
|
||||
set(TEST_TARGET sam)
|
||||
add_executable(${TEST_TARGET} sam.cpp)
|
||||
target_link_libraries(${TEST_TARGET} PRIVATE ggml common)
|
||||
|
||||
#
|
||||
# sam-quantize
|
||||
|
||||
#set(TEST_TARGET sam-quantize)
|
||||
#add_executable(${TEST_TARGET} quantize.cpp)
|
||||
#target_link_libraries(${TEST_TARGET} PRIVATE ggml common)
|
||||
@@ -0,0 +1,95 @@
|
||||
# SAM.cpp
|
||||
|
||||
Inference of Meta's [Segment Anything Model](https://github.com/facebookresearch/segment-anything/) in pure C/C++
|
||||
|
||||
## Description
|
||||
|
||||
The example currently supports only the [ViT-B SAM model checkpoint](https://huggingface.co/facebook/sam-vit-base).
|
||||
|
||||
## Next steps
|
||||
|
||||
- [X] Reduce memory usage by utilizing the new ggml-alloc
|
||||
- [X] Remove redundant graph nodes
|
||||
- [ ] Make inference faster
|
||||
- [X] Fix the difference in output masks compared to the PyTorch implementation
|
||||
- [X] Filter masks based on stability score
|
||||
- [ ] Add support for user input
|
||||
- [ ] Support F16 for heavy F32 ops
|
||||
- [ ] Test quantization
|
||||
- [X] Support bigger model checkpoints
|
||||
- [ ] GPU support
|
||||
|
||||
## Quick start
|
||||
Setup Python and build examples according to main README.
|
||||
|
||||
```bash
|
||||
# Download PTH model
|
||||
wget -P examples/sam/ https://dl.fbaipublicfiles.com/segment_anything/sam_vit_b_01ec64.pth
|
||||
|
||||
# Convert PTH model to ggml
|
||||
python examples/sam/convert-pth-to-ggml.py examples/sam/sam_vit_b_01ec64.pth examples/sam/ 1
|
||||
|
||||
# run inference
|
||||
./bin/sam -t 16 -i ../examples/sam/example.jpg -m ../examples/sam/ggml-model-f16.bin
|
||||
```
|
||||
|
||||
## Downloading and converting the model checkpoints
|
||||
|
||||
You can download a [model checkpoint](https://github.com/facebookresearch/segment-anything/tree/main#model-checkpoints) and convert it to `ggml` format using the script `convert-pth-to-ggml.py`:
|
||||
|
||||
## Example output on M2 Ultra
|
||||
```
|
||||
$ ▶ make -j sam && time ./bin/sam -t 8 -i img.jpg
|
||||
[ 28%] Built target common
|
||||
[ 71%] Built target ggml
|
||||
[100%] Built target sam
|
||||
main: seed = 1693224265
|
||||
main: loaded image 'img.jpg' (680 x 453)
|
||||
sam_image_preprocess: scale = 0.664062
|
||||
main: preprocessed image (1024 x 1024)
|
||||
sam_model_load: loading model from 'models/sam-vit-b/ggml-model-f16.bin' - please wait ...
|
||||
sam_model_load: n_enc_state = 768
|
||||
sam_model_load: n_enc_layer = 12
|
||||
sam_model_load: n_enc_head = 12
|
||||
sam_model_load: n_enc_out_chans = 256
|
||||
sam_model_load: n_pt_embd = 4
|
||||
sam_model_load: ftype = 1
|
||||
sam_model_load: qntvr = 0
|
||||
operator(): ggml ctx size = 202.32 MB
|
||||
sam_model_load: ...................................... done
|
||||
sam_model_load: model size = 185.05 MB / num tensors = 304
|
||||
embd_img
|
||||
dims: 64 64 256 1 f32
|
||||
First & Last 10 elements:
|
||||
-0.05117 -0.06408 -0.07154 -0.06991 -0.07212 -0.07690 -0.07508 -0.07281 -0.07383 -0.06779
|
||||
0.01589 0.01775 0.02250 0.01675 0.01766 0.01661 0.01811 0.02051 0.02103 0.03382
|
||||
sum: 12736.272313
|
||||
|
||||
Skipping mask 0 with iou 0.705935 below threshold 0.880000
|
||||
Skipping mask 1 with iou 0.762136 below threshold 0.880000
|
||||
Mask 2: iou = 0.947081, stability_score = 0.955437, bbox (371, 436), (144, 168)
|
||||
|
||||
|
||||
main: load time = 51.28 ms
|
||||
main: total time = 2047.49 ms
|
||||
|
||||
real 0m2.068s
|
||||
user 0m16.343s
|
||||
sys 0m0.214s
|
||||
```
|
||||
|
||||
Input point is (414.375, 162.796875) (currently hardcoded)
|
||||
|
||||
Input image:
|
||||
|
||||

|
||||
|
||||
Output mask (mask_out_2.png in build folder):
|
||||
|
||||

|
||||
|
||||
## References
|
||||
|
||||
- [ggml](https://github.com/ggerganov/ggml)
|
||||
- [SAM](https://segment-anything.com/)
|
||||
- [SAM demo](https://segment-anything.com/demo)
|
||||
@@ -0,0 +1,147 @@
|
||||
# Convert a SAM model checkpoint to a ggml compatible file
|
||||
#
|
||||
|
||||
import sys
|
||||
import torch
|
||||
import struct
|
||||
import numpy as np
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: convert-pth-to-ggml.py file-model dir-output [ftype]\n")
|
||||
print(" ftype == 0 -> float32")
|
||||
print(" ftype == 1 -> float16")
|
||||
sys.exit(1)
|
||||
|
||||
# output in the same directory as the model
|
||||
fname_model = sys.argv[1]
|
||||
dir_out = sys.argv[2]
|
||||
fname_out = dir_out + "/ggml-model.bin"
|
||||
|
||||
# possible data types
|
||||
# ftype == 0 -> float32
|
||||
# ftype == 1 -> float16
|
||||
#
|
||||
# map from ftype to string
|
||||
ftype_str = ["f32", "f16"]
|
||||
|
||||
ftype = 1
|
||||
if len(sys.argv) > 3:
|
||||
ftype = int(sys.argv[3])
|
||||
|
||||
if ftype < 0 or ftype > 1:
|
||||
print("Invalid ftype: " + str(ftype))
|
||||
sys.exit(1)
|
||||
|
||||
fname_out = fname_out.replace(".bin", "-" + ftype_str[ftype] + ".bin")
|
||||
|
||||
# Default params are set to sam_vit_b checkpoint
|
||||
n_enc_state = 768
|
||||
n_enc_layers = 12
|
||||
n_enc_heads = 12
|
||||
n_enc_out_chans = 256
|
||||
n_pt_embd = 4
|
||||
|
||||
model = torch.load(fname_model, map_location="cpu")
|
||||
for k, v in model.items():
|
||||
print(k, v.shape)
|
||||
if k == "image_encoder.blocks.0.norm1.weight":
|
||||
n_enc_state = v.shape[0]
|
||||
|
||||
if n_enc_state == 1024: # sam_vit_l
|
||||
n_enc_layers = 24
|
||||
n_enc_heads = 16
|
||||
elif n_enc_state == 1280: # sam_vit_h
|
||||
n_enc_layers = 32
|
||||
n_enc_heads = 16
|
||||
|
||||
hparams = {
|
||||
"n_enc_state": n_enc_state,
|
||||
"n_enc_layers": n_enc_layers,
|
||||
"n_enc_heads": n_enc_heads,
|
||||
"n_enc_out_chans": n_enc_out_chans,
|
||||
"n_pt_embd": n_pt_embd,
|
||||
}
|
||||
|
||||
print(hparams)
|
||||
|
||||
for k, v in model.items():
|
||||
print(k, v.shape)
|
||||
|
||||
#exit()
|
||||
#code.interact(local=locals())
|
||||
|
||||
fout = open(fname_out, "wb")
|
||||
|
||||
fout.write(struct.pack("i", 0x67676d6c)) # magic: ggml in hex
|
||||
fout.write(struct.pack("i", hparams["n_enc_state"]))
|
||||
fout.write(struct.pack("i", hparams["n_enc_layers"]))
|
||||
fout.write(struct.pack("i", hparams["n_enc_heads"]))
|
||||
fout.write(struct.pack("i", hparams["n_enc_out_chans"]))
|
||||
fout.write(struct.pack("i", hparams["n_pt_embd"]))
|
||||
fout.write(struct.pack("i", ftype))
|
||||
|
||||
for k, v in model.items():
|
||||
name = k
|
||||
shape = v.shape
|
||||
|
||||
if name[:19] == "prompt_encoder.mask":
|
||||
continue
|
||||
|
||||
print("Processing variable: " + name + " with shape: ", shape, " and type: ", v.dtype)
|
||||
|
||||
#data = tf.train.load_variable(dir_model, name).squeeze()
|
||||
#data = v.numpy().squeeze()
|
||||
data = v.numpy()
|
||||
n_dims = len(data.shape)
|
||||
|
||||
# for efficiency - transpose some matrices
|
||||
# "model/h.*/attn/c_attn/w"
|
||||
# "model/h.*/attn/c_proj/w"
|
||||
# "model/h.*/mlp/c_fc/w"
|
||||
# "model/h.*/mlp/c_proj/w"
|
||||
#if name[-14:] == "/attn/c_attn/w" or \
|
||||
# name[-14:] == "/attn/c_proj/w" or \
|
||||
# name[-11:] == "/mlp/c_fc/w" or \
|
||||
# name[-13:] == "/mlp/c_proj/w":
|
||||
# print(" Transposing")
|
||||
# data = data.transpose()
|
||||
|
||||
dshape = data.shape
|
||||
|
||||
# default type is fp16
|
||||
ftype_cur = 1
|
||||
if ftype == 0 or n_dims == 1 or \
|
||||
name == "image_encoder.pos_embed" or \
|
||||
name.startswith("prompt_encoder") or \
|
||||
name.startswith("mask_decoder.iou_token") or \
|
||||
name.startswith("mask_decoder.mask_tokens"):
|
||||
print(" Converting to float32")
|
||||
data = data.astype(np.float32)
|
||||
ftype_cur = 0
|
||||
else:
|
||||
print(" Converting to float16")
|
||||
data = data.astype(np.float16)
|
||||
|
||||
# reshape the 1D bias into a 4D tensor so we can use ggml_repeat
|
||||
# keep it in F32 since the data is small
|
||||
if name == "image_encoder.patch_embed.proj.bias":
|
||||
data = data.reshape(1, data.shape[0], 1, 1)
|
||||
n_dims = len(data.shape)
|
||||
dshape = data.shape
|
||||
|
||||
print(" New shape: ", dshape)
|
||||
|
||||
# header
|
||||
str = name.encode('utf-8')
|
||||
fout.write(struct.pack("iii", n_dims, len(str), ftype_cur))
|
||||
for i in range(n_dims):
|
||||
fout.write(struct.pack("i", dshape[n_dims - 1 - i]))
|
||||
fout.write(str)
|
||||
|
||||
# data
|
||||
data.tofile(fout)
|
||||
|
||||
fout.close()
|
||||
|
||||
print("Done. Output file: " + fname_out)
|
||||
print("")
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user