--- comments: true description: Save object detection results to CSV or JSON with supervision's CSVSink and JSONSink — export predictions for analysis and downstream pipelines. authors: - name: Piotr Skalski role: Computer Vision Engineer, Roboflow github: https://github.com/SkalskiP date_modified: 2026-04-22 --- # Save Detections Supervision enables an easy way to save detections in .CSV and .JSON files for offline processing. This guide demonstrates how to perform video inference using the [Inference](https://github.com/roboflow/inference), [Ultralytics](https://github.com/ultralytics/ultralytics) or [Transformers](https://github.com/huggingface/transformers) packages and save their results with [`sv.CSVSink`](https://supervision.roboflow.com/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink) and [`sv.JSONSink`](https://supervision.roboflow.com/latest/detection/tools/save_detections/#supervision.detection.tools.json_sink.JSONSink). ## Run Detection First, you'll need to obtain predictions from your object detection or segmentation model. You can learn more on this topic in our [How to Detect and Annotate](https://supervision.roboflow.com/latest/how_to/detect_and_annotate/) guide. To generate predictions for saving, initialize your model and iterate over video frames using `sv.get_video_frames_generator`. Each frame is passed to the model, and the raw output is converted into a `sv.Detections` object. This detection loop forms the foundation for both CSV and JSON export workflows shown below. === "Inference" ```python import supervision as sv from inference import get_model model = get_model(model_id="yolov8n-640") frames_generator = sv.get_video_frames_generator("") for frame in frames_generator: results = model.infer(image)[0] detections = sv.Detections.from_inference(results) ``` === "Ultralytics" ```python import supervision as sv from ultralytics import YOLO model = YOLO("yolov8n.pt") frames_generator = sv.get_video_frames_generator("") for frame in frames_generator: results = model(frame)[0] detections = sv.Detections.from_ultralytics(results) ``` === "Transformers" ```python import torch import supervision as sv from transformers import DetrImageProcessor, DetrForObjectDetection processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50") model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50") frames_generator = sv.get_video_frames_generator("") for frame in frames_generator: frame = sv.cv2_to_pillow(frame) inputs = processor(images=frame, return_tensors="pt") with torch.no_grad(): outputs = model(**inputs) width, height = frame.size target_size = torch.tensor([[height, width]]) results = processor.post_process_object_detection( outputs=outputs, target_sizes=target_size )[0] detections = sv.Detections.from_transformers(results) ``` ## Save Detections as CSV To save detections to a `.CSV` file, open our [`sv.CSVSink`](https://supervision.roboflow.com/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink) and then pass the [`sv.Detections`](https://supervision.roboflow.com/latest/detection/core/#supervision.detection.core.Detections) object resulting from the inference to it. Its fields are parsed and saved on disk. === "Inference" ```{ .py hl_lines="7 12" } import supervision as sv from inference import get_model model = get_model(model_id="yolov8n-640") frames_generator = sv.get_video_frames_generator("") with sv.CSVSink("") as sink: for frame in frames_generator: results = model.infer(image)[0] detections = sv.Detections.from_inference(results) sink.append(detections, {}) ``` === "Ultralytics" ```{ .py hl_lines="7 12" } import supervision as sv from ultralytics import YOLO model = YOLO("yolov8n.pt") frames_generator = sv.get_video_frames_generator("") with sv.CSVSink("") as sink: for frame in frames_generator: results = model(frame)[0] detections = sv.Detections.from_ultralytics(results) sink.append(detections, {}) ``` === "Transformers" ```{ .py hl_lines="9 23" } import torch import supervision as sv from transformers import DetrImageProcessor, DetrForObjectDetection processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50") model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50") frames_generator = sv.get_video_frames_generator("") with sv.CSVSink("") as sink: for frame in frames_generator: frame = sv.cv2_to_pillow(frame) inputs = processor(images=frame, return_tensors="pt") with torch.no_grad(): outputs = model(**inputs) width, height = frame.size target_size = torch.tensor([[height, width]]) results = processor.post_process_object_detection( outputs=outputs, target_sizes=target_size)[0] detections = sv.Detections.from_transformers(results) sink.append(detections, {}) ``` | x_min | y_min | x_max | y_max | class_id | confidence | tracker_id | class_name | | ------- | ------- | ------- | ------- | -------- | ---------- | ---------- | ---------- | | 2941.14 | 1269.31 | 3220.77 | 1500.67 | 2 | 0.8517 | | car | | 944.889 | 899.641 | 1235.42 | 1308.80 | 7 | 0.6752 | | truck | | 1439.78 | 1077.79 | 1621.27 | 1231.40 | 2 | 0.6450 | | car | ## Custom Fields Besides regular fields in [`sv.Detections`](https://supervision.roboflow.com/latest/detection/core/#supervision.detection.core.Detections), [`sv.CSVSink`](https://supervision.roboflow.com/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink) also allows you to add custom information to each row, which can be passed via the `custom_data` dictionary. Let's utilize this feature to save information about the frame index from which the detections originate. === "Inference" ```{ .py hl_lines="8 12" } import supervision as sv from inference import get_model model = get_model(model_id="yolov8n-640") frames_generator = sv.get_video_frames_generator("") with sv.CSVSink("") as sink: for frame_index, frame in enumerate(frames_generator): results = model.infer(image)[0] detections = sv.Detections.from_inference(results) sink.append(detections, {"frame_index": frame_index}) ``` === "Ultralytics" ```{ .py hl_lines="8 12" } import supervision as sv from ultralytics import YOLO model = YOLO("yolov8n.pt") frames_generator = sv.get_video_frames_generator("") with sv.CSVSink("") as sink: for frame_index, frame in enumerate(frames_generator): results = model(frame)[0] detections = sv.Detections.from_ultralytics(results) sink.append(detections, {"frame_index": frame_index}) ``` === "Transformers" ```{ .py hl_lines="10 23" } import torch import supervision as sv from transformers import DetrImageProcessor, DetrForObjectDetection processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50") model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50") frames_generator = sv.get_video_frames_generator("") with sv.CSVSink("") as sink: for frame_index, frame in enumerate(frames_generator): frame = sv.cv2_to_pillow(frame) inputs = processor(images=frame, return_tensors="pt") with torch.no_grad(): outputs = model(**inputs) width, height = frame.size target_size = torch.tensor([[height, width]]) results = processor.post_process_object_detection( outputs=outputs, target_sizes=target_size)[0] detections = sv.Detections.from_transformers(results) sink.append(detections, {"frame_index": frame_index}) ``` | x_min | y_min | x_max | y_max | class_id | confidence | tracker_id | class_name | frame_index | | ------- | ------- | ------- | ------- | -------- | ---------- | ---------- | ---------- | ----------- | | 2941.14 | 1269.31 | 3220.77 | 1500.67 | 2 | 0.8517 | | car | 0 | | 944.889 | 899.641 | 1235.42 | 1308.80 | 7 | 0.6752 | | truck | 0 | | 1439.78 | 1077.79 | 1621.27 | 1231.40 | 2 | 0.6450 | | car | 0 | ## Save Detections as JSON If you prefer to save the result in a `.JSON` file instead of a `.CSV` file, all you need to do is replace [`sv.CSVSink`](https://supervision.roboflow.com/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink) with [`sv.JSONSink`](https://supervision.roboflow.com/latest/detection/tools/save_detections/#supervision.detection.tools.json_sink.JSONSink). === "Inference" ```{ .py hl_lines="7" } import supervision as sv from inference import get_model model = get_model(model_id="yolov8n-640") frames_generator = sv.get_video_frames_generator("") with sv.JSONSink("") as sink: for frame_index, frame in enumerate(frames_generator): results = model.infer(image)[0] detections = sv.Detections.from_inference(results) sink.append(detections, {"frame_index": frame_index}) ``` === "Ultralytics" ```{ .py hl_lines="7" } import supervision as sv from ultralytics import YOLO model = YOLO("yolov8n.pt") frames_generator = sv.get_video_frames_generator("") with sv.JSONSink("") as sink: for frame_index, frame in enumerate(frames_generator): results = model(frame)[0] detections = sv.Detections.from_ultralytics(results) sink.append(detections, {"frame_index": frame_index}) ``` === "Transformers" ```{ .py hl_lines="9" } import torch import supervision as sv from transformers import DetrImageProcessor, DetrForObjectDetection processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50") model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50") frames_generator = sv.get_video_frames_generator("") with sv.JSONSink("") as sink: for frame_index, frame in enumerate(frames_generator): frame = sv.cv2_to_pillow(frame) inputs = processor(images=frame, return_tensors="pt") with torch.no_grad(): outputs = model(**inputs) width, height = frame.size target_size = torch.tensor([[height, width]]) results = processor.post_process_object_detection( outputs=outputs, target_sizes=target_size)[0] detections = sv.Detections.from_transformers(results) sink.append(detections, {"frame_index": frame_index}) ``` ## Frequently Asked Questions ### How do I save detections to CSV with supervision? Open `sv.CSVSink("output.csv")` as a context manager and call `sink.append(detections)` for each frame. The CSV includes box coordinates, confidence, class ID, tracker ID, and any fields stored in `detections.data`. ### Can I save detections to JSON instead? Yes. Open `sv.JSONSink("output.json")` as a context manager and call `sink.append(detections)` for each frame. The file is written as a JSON array when the context exits. ### Can I add custom fields to the saved output? Yes. Pass a dict as the second argument: `sink.append(detections, {"frame_index": 5})` — the keys become extra columns in the CSV or extra fields in the JSON. ### Can I save only specific classes or confidence levels? Filter the `Detections` object before saving: `sink.append(detections[detections.confidence > 0.7])`. ## Author - [Piotr Skalski](https://github.com/SkalskiP) — Computer Vision Engineer, Roboflow