Refer to the official repository: Segment Anything 2 (SAM2).
To visualize the masks as contours:
pip install scikit-image
pip install pycocotools
To convert a video into frames, install ffmpeg:
sudo apt update
sudo apt install ffmpeg
pip install ffmpeg-python- 8.18: Added support for using bounding boxes directly as ground truth in
main_bbox.py. - 8.19: Implemented negative points sampling for pixel masks in
main_point.py(for pixel mask only). - 8.20: Added support for negative points visualization for pixel masks in
main_point.py(for pixel mask only).
-
8.24
- bugs fixed:
- fix the bug that in
inference.py. the modulo number is not correct before. - fix the bug that in
inference.py. the code now properly converts the mask to a bounding box. - fix the bug that in
utils.py. the code now can properly handle the case that the mask contains few points. - fix the bug that in
convert.ipynb. the code now won't create annotations with the same id.
- fix the bug that in
- problems:
- the code now does not support using the
variable_catsandclip_lengthat the same time. When you want to useclip_length, you need to setvariable_catstoFalse. - Using
variable_catswill leading to worse results for unknown reasons. Guess it's because of the multiple frames prompting.
- the code now does not support using the
- Things you need to do:
- rerun the
convert.ipynbto generate the new annotations.
- rerun the
- TODO:
- add noise to prompts.
- bugs fixed:
-
8.25
- bugs fixed:
- forget to update the
utils.pyfile in theEndoscapes2023_Pipelinefolder in the previous version.
- forget to update the
- new features:
- support noised prompts. You need to set
noised_prompttoTruein theinference.pyfile. For more details, please refer to theutils.pyfile, and see theadd_noise_to_objfunction.
- support noised prompts. You need to set
- Reorganize the code structure of the
inference.pyandutils.pyfiles.
- bugs fixed:
-
8.26
- add experiments configurations and the corresponding processing scripts. See
ex.pyandconfig.yamlunderEndoscapes2023_Pipelinefor details.
- add experiments configurations and the corresponding processing scripts. See
-
8.27
- add the
visualize.pyfile to visualize the results. - update the
inference.pyandutils.pyfiles to support the visualization of the results. - You need to re-run the
inference.pyfile to generate the visualization results.
- add the
-
8.30
-
New features:
- You can now control the maximum noise intensity using the
noise_intensityparameter ininference.py. - You can control the bbox noise type using the
bbox_noise_typeparameter ininference.py. - Added
visualize_all.pyto process all visualizations at once. - Added GIF generation functionality to
visualize.py.
- You can now control the maximum noise intensity using the
-
9.7
- Add the
visualize_cv.pyto speed up the visualization process. - Fix the bug in
inference.py. The code now properly supports sampling negative points from pixel masks.
- Add the
Refer https://sjtu.feishu.cn/docx/Q8YBd1VAOo0e8YxUe4Zcs5f9nYf for the results.
python main_point.py --sampled_points 2 --negative_sample_points 1 --video_dir examples/video_pixel2/frames --sam2_checkpoint checkpoints/sam2_hiera_large.pt --output_dir test_negative_output --gt_path examples/video_pixel2/0_mask.png --gt_type pixel_mask--sampled_points: Determines how many positive points to sample.--negative_sample_points: Determines how many negative points to sample (a class's sampled negative points are near the other-class sampled points).- You can change the
betato decide how near it is inutils/negative_helpers.pyingenerate_negative_samples(sampled_point_classes, sampled_points, n, height, width, beta).
- You can change the
python main_bbox.py --video_dir examples/video_mask/frames --sam2_checkpoint checkpoints/sam2_hiera_large.pt --output_dir bbox_output --gt_path examples/video_mask/annotation_coco_vid.jsonCurrently, the script only supports processing a single video, provided as individual frames. To convert a video to frames:
ffmpeg -i /path/to/video.mp4 -q:v 2 -start_number 0 /path/to/output/frames/'%05d.jpg'Only pixel-mask GT are tested.
python main_pixel_mask.py --video_dir examples/video_pixel --sam2_checkpoint checkpoints/sam2_hiera_tiny.pt --output_dir pm_color_pixel_output --gt_path examples/video_pixel/frame_561_endo_color_mask.pngThis case, we convert any other GT format to point prompts, e.g., a pixel mask's center can be one point prompt. Currently, the script only supports processing a single video, provided as individual frames. To convert a video to frames:
ffmpeg -i /path/to/video.mp4 -q:v 2 -start_number 0 /path/to/output/frames/'%05d.jpg'Note: Ensure frame filenames are indexed numerically in time order.
Run main_point.py with the following command:
python main_point.py --video_dir <path_to_video_frames> --sam2_checkpoint <path_to_sam2_checkpoint> --output_dir <output_directory> --gt_path <path_to_ground_truth_json> --gt_type <bbox_or_mask>--video_dir: Directory containing video frames (required).--sam2_checkpoint: Path to the SAM2 checkpoint file (required).--output_dir: Output directory for results (default: current directory).--vis_frame_stride: Stride for visualization frames (default: 15).--gt_path: Path to ground truth data in JSON format or the path to a pixel mask image.--gt_type: Type of ground truth data, either 'bbox' or 'mask' or 'pixel-mask' (required).--sample_points: Number of points to sample for each object (default: 1).
- Uses ground truth (GT) data from the first frame only.
- Generates point prompts from bounding boxes or masks.
- Initializes SAM2 with these prompts.
- Segments the first frame and propagates to subsequent frames.
- Efficiently segments video using only initial frame's GT data.
The script generates in the specified output directory:
- Pixel masks for each frame and object.
- Visualizations of segmentation results.
- GIF animation of the segmentation process.
- COCO format annotations in JSON.
- Visualization of the first frame with bounding boxes and sampled points.
Convert video to frames:
mkdir -p examples/video3/frames/
ffmpeg -i examples/video3/video3.mp4 -q:v 2 -start_number 0 examples/video3/frames/'%05d.jpg'Generate masks:
python main_point.py --video_dir examples/video_mask/frames --sam2_checkpoint checkpoints/sam2_hiera_tiny.pt --output_dir test_bbx_output --gt_path examples/video_mask/annotation_coco_vid.json --gt_type bboxAssuming frames are in examples/video_mask/frames:
python main_point.py --video_dir examples/video_mask/frames --sam2_checkpoint checkpoints/sam2_hiera_tiny.pt --output_dir test_mask_output --gt_path examples/video_mask/annotation_coco_vid.json --gt_type maskAssuming frames and pixel-mask gt are in examples/video_pixel:
This is for watershed pixel mask
python main_point.py --video_dir examples/video_pixel --sam2_checkpoint checkpoints/sam2_hiera_tiny.pt --output_dir test_watermask_pixel_output --gt_path examples/video_pixel/frame_561_endo_watershed_mask.png --gt_type pixel_maskThis is for colored pixel mask
python main_point.py --video_dir examples/video_pixel --sam2_checkpoint checkpoints/sam2_hiera_tiny.pt --output_dir test_color_pixel_output --gt_path examples/video_pixel/frame_561_endo_color_mask.png --gt_type pixel_masksee the example_COCO_on_CholecSeg8k/convert.ipynb for step-by-step guide.
Once you have the COCO format, you can technically use all the functions implemented under the Endoscapes2023_Pipeline folder.
It now contains the following files:
It has the following features:
- Supports multiple video processing as long as the COCO format file is generated based on the
example_COCO_on_CholecSeg8k/convert.ipynbnotebook. - Supports multiple prompt types, including
points,bboxandmask. - Automatically tracks different objects in the same category, even if they are in the same mask.
- Supports re-intialization for every
clip_lengthframes. - Supports re-intialization when new categories are detected.
- Automatically find the first frame with valid ground truth.
Now let's see how to use it.
if __name__ == "__main__":
inference(
coco_path="coco_annotations.json",
output_path="./",
prompt_type="points",
clip_length=None,
variable_cats=False,
save_video_list=None,
)coco_path: path to coco annotation fileoutput_path: path to output directoryprompt_type: type of prompts, either "points", "bbox" or "mask"clip_length: length of each video segment to be processed. It will re-intialize everyclip_lengthframes. If None, process the entire video as a single segmentvariable_cats: whether to re-intialize when new categories are detectedsave_video_list: list of video ids to be saved, if None, save all videos
It will generate two files under the corresponding output directory:
predict.json: COCO format predictionsprompt.pkl: information for the prompts including the obejcts, frames, video ids, prompt types, etc.
Once you get the predictions from the inference.py, you can evaluate the results with eval.py.
if __name__ == "__main__":
eval(
predict_path="/bd_byta6000i0/users/sam2/kyyang/sam2_predict/test/output/points/predict.json",
coco_path="coco_annotations.json",
output_path="output/points/",
)predict_path: path to the predicted json filecoco_path: path to the coco annotation fileoutput_path: path to the output directory
It will calculate the following metrics for each frame, category, and video, and the average score for each category and video:
iou: intersection over unionmae: mean absolute errordice: dice score
It will generate a eval.pkl file under the corresponding output directory, whhich contains the evaluation results.

The folder evaluation contains different metric for different datasets. See utils.py for details.
| Dataset | Metric |
|---|---|
| EndoVis'18 | Dice score, IOU per class |
| CholecSeg8k | Dice score, IOU per class |
| Endoscape2023 | Ap50, MAP 50-95 |
| Cadis | |
| EndoNeRF | Dice, IOU, MAE |
| EndoVis’17 | Dice, IOU, MAE |
| SurgToolLoc | Dice, IOU, MAE, |
One version of reinitialization is implemented in Eodoscapes2023/main.py. You can focus on the following pieces of code to have an overview of the reinitialization method.
def process_video_clip(
video_info, video_order, coco_info, prompt_type, start_idx, end_idx, output_path
):
video_dir = create_symbol_link_for_video(
video_info[video_order]["frames"][start_idx : end_idx + 1]
)
prompt_frame = find_prompt_frame(
video_info, video_order, coco_info, start_idx, end_idx
)
if prompt_frame is None:
return {}
prompt_objs = get_each_obj(prompt_frame, coco_info)
predictor = build_sam2_video_predictor(model_cfg, sam2_checkpoint)
inference_state = predictor.init_state(video_path=video_dir)
predictor, inference_state, out_obj_ids, out_mask_logits = add_prompt(
prompt_objs,
predictor,
inference_state,
prompt_frame["order_in_video"] - start_idx,
prompt_type,
)
video_output_path = os.path.join(
output_path, f"video_{video_info[video_order]["video_id"]}"
)
os.makedirs(video_output_path, exist_ok=True)
save_prompt_frame(
video_info[video_order]["frames"][prompt_frame["order_in_video"]],
prompt_objs,
prompt_type,
out_obj_ids,
out_mask_logits,
len(coco_info.cats),
video_output_path,
)
video_segments = predict_on_video(predictor, inference_state, start_idx)
del predictor
torch.cuda.empty_cache()
return video_segmentsdef process_singel_video(
video_info, video_order, coco_info, prompt_type, clip_length, output_path
):
video_segments = {}
if clip_length is None:
clip_length = len(video_info[video_order]["frames"])
for start_idx in range(0, len(video_info[video_order]["frames"]), clip_length):
end_idx = min(
start_idx + clip_length - 1, len(video_info[video_order]["frames"]) - 1
)
video_segments.update(
process_video_clip(
video_info,
video_order,
coco_info,
prompt_type,
start_idx,
end_idx,
output_path,
)
)
return video_segmentsFor other details, please have a look at the Eodoscapes2023_Pipeline/main.py file.
- The code now is NOT based on the
main_point.pyormain_pixel_mask.py. As COCO data format is used in Endoscapes2023 instead of pixel masks. And there are extra configuration you need to make it work on segments. - The
process_video_clipfunction is designed to process a segment of a video, which is defined bystart_idxandend_idx. This function is called repeatedly byprocess_singel_videoto process the entire video in segments. - The
clip_lengthparameter inprocess_singel_videodetermines the length of each video segment to be processed. Ifclip_lengthis set toNone, the entire video will be processed as a single segment.
- support to process a single video with a GT json file that contains complete annotations
- Implement search for first frame with valid GT
- Disentangle contents in main_point.py
- Develop main_mask.py for direct mask input processing
- Develop main_bbx.py for direct bounding box input handling
- Interactive point prompting in a .ipynb file
- Implement multi-video batch processing

