--- myst: html_meta: description: "Upload object detections from a video next to the video itself, so you can review them frame by frame." --- # Object identification {.lead} Upload a video with the objects a computer vision model found in each frame, such as counts and confidence scores. In a run, the detections chart in sync with the video, and [checklists](/guides/checklists/authoring.md) can flag frames that need review. The examples also need `pip install polars huggingface_hub`. ```{video} https://res.cloudinary.com/didkpxvqu/video/upload/v1727580510/montage_video_owslrd.mov ``` ```{include} /guides/_snippets/connect-the-client.md ``` ## Download the sample The sample is drone footage annotated by the [RT-DETR](https://huggingface.co/docs/transformers/en/model_doc/rt_detr) model, and a CSV with one row per detected object. The video is 225 MB. ```python import polars as pl from huggingface_hub import hf_hub_download repo = {"repo_id": "nominal-io/drone-flight-object-identification", "repo_type": "dataset"} video_path = hf_hub_download(filename="all_scores_bounding_box_output.mov", **repo) detections = pl.read_csv(hf_hub_download(filename="object_detection_metadata.csv", **repo)) ``` Each row has the frame number, timestamp, object label, confidence `score`, bounding box, and per-frame counts such as `car_count` and `total_object_count`. The first frame is at `2011-11-11T11:11:11` UTC. ## Upload the detections and the video Put both in one dataset so they share timestamps. Convert the video first, as in [Video ingest](/guides/video/video-ingest.md#convert-the-video): ```python from datetime import datetime, timezone from nominal.experimental.video_processing import normalize_video from nominal.thirdparty.pandas import upload_dataframe dataset = upload_dataframe( client, detections.to_pandas(), "Drone flight detections", "timestamps", timestamp_type="iso_8601", ) normalize_video(video_path, "drone.mp4") start = datetime(2011, 11, 11, 11, 11, 11, tzinfo=timezone.utc) dataset.add_video("drone.mp4", channel="rt_detr_video", start=start).poll_until_ingestion_completed() ``` The video's `start` must match the detections' first timestamp, or the two won't line up. ## Review the detections Create a run over the detections' time range: ```python end = datetime.fromisoformat(str(detections["timestamps"].max())).replace(tzinfo=timezone.utc) run = client.create_run("RT-DETR drone analysis", start=start, end=end) run.add_dataset("rt_detr", dataset) print(run.nominal_url) ``` In a workbook on the run, add a video panel for `rt_detr_video` and charts of the counts and scores. ```{video} /guides/images/b2/CleanShot_2024-09-25_at_18.35.32_nbtuko.mp4 :loop: :alt: A workbook syncing object counts and confidence scores with the annotated video ``` ## Detect objects in your own video To make a detections table from your own video, read its frames with [OpenCV](https://pypi.org/project/opencv-python/) and run a model on each. This example uses RT-DETR from [transformers](https://huggingface.co/docs/transformers/), and needs `pip install opencv-python torch transformers pillow`. Long videos can take hours. ```python from datetime import timedelta import cv2 import torch from PIL import Image from transformers import RTDetrForObjectDetection, RTDetrImageProcessor processor = RTDetrImageProcessor.from_pretrained("PekingU/rtdetr_r50vd") model = RTDetrForObjectDetection.from_pretrained("PekingU/rtdetr_r50vd") rows = [] cap = cv2.VideoCapture("my_video.mp4") while True: ok, frame = cap.read() if not ok: break timestamp = start + timedelta(milliseconds=cap.get(cv2.CAP_PROP_POS_MSEC)) image = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) with torch.no_grad(): outputs = model(**processor(images=image, return_tensors="pt")) result = processor.post_process_object_detection( outputs, target_sizes=torch.tensor([image.size[::-1]]), threshold=0.3 )[0] for score, label in zip(result["scores"], result["labels"]): rows.append({"timestamp": timestamp, "object": model.config.id2label[label.item()], "score": score.item()}) cap.release() detections = pl.DataFrame(rows) ``` Count objects per frame with Polars, such as `detections.group_by("timestamp", "object").len()`, and upload the result as above.