Save video only object detected using YOLOv5

yolov5 YOLOv5

This presentation explains how to use YOLOv5 to store video only when a specific object is detected. It will also explain how to store video in the AWS cloud.

Purpose

When viewing surveillance video later, it is possible to reduce the time and effort required to search for surveillance targets and reduce the storage capacity if the video that does not include the target of surveillance can be automatically deleted. This article describes a system that uses YOLOv5 to automatically delete video that does not include the monitored object and store only the video that does include the monitored object in the cloud. This time, AWS S3 will be used as the cloud storage.

Plan

I would like to implement the following plan.

  • YOLOv5 performs object detection and saves the video locally every minute.
  • If a monitored object is included in the video locally saved, it is uploaded in the cloud.
  • If a monitored object is not included in the video locally saved, it is deleted.

Implementation

This time, we will implement it using YOLOv5 with the python version of OpenCV used in this article.

The source code is available in this repository.

The main function is explained below.

def main(is_cuda, video, class_list, object_ids, s3_bucket): executor = ThreadPoolExecutor() colors = [(255, 255, 0), (0, 255, 0), (0, 255, 255), (255, 0, 0)] net = build_model(is_cuda) capture = cv2.VideoCapture(video) start = time.time_ns() frame_count = 0 total_frames = 0 fps = -1 is_save = False writer = None timestamp = datetime.now().strftime("%Y%m%d%H%M") video_name = f"{timestamp}.mp4" while True: _, frame = capture.read() if frame is None: print("End of stream") break inputImage = format_yolov5(frame) outs = detect(inputImage, net) class_ids, confidences, boxes = wrap_detection(inputImage, outs[0]) if len([c for c in class_ids if c in object_ids]) != 0: is_save = True frame_count += 1 total_frames += 1 for (classid, confidence, box) in zip(class_ids, confidences, boxes): color = colors[int(classid) % len(colors)] cv2.rectangle(frame, box, color, 2) cv2.rectangle( frame, (box[0], box[1] - 20), (box[0] + box[2], box[1]), color, -1 ) cv2.putText( frame, class_list[classid], (box[0], box[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), ) if frame_count >= 30: end = time.time_ns() fps = 1000000000 * frame_count / (end - start) frame_count = 0 start = time.time_ns() if fps > 0: fps_label = "FPS: %.2f" % fps cv2.putText( frame, fps_label, (10, 25), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2 ) cv2.imshow("output", frame) if writer is None and fps > 0: w, h = frame.shape[1], frame.shape[0] writer = cv2.VideoWriter( video_name, cv2.VideoWriter_fourcc(*"mp4v"), fps, (w, h) ) if writer: writer.write(frame) # update timestamp new_timestamp = datetime.now().strftime("%Y%m%d%H%M") if timestamp != new_timestamp: writer.release() writer = None if is_save: print(f"save {video_name}") executor.submit(upload, s3_bucket, video_name) else: print(f"remove {video_name}") os.remove(video_name) is_save = False timestamp = new_timestamp if cv2.waitKey(1) & 0xFF == ord("q"): print("finished by user") running = False if writer: writer.release() break
Code language: Python (python)

build_model returns an object of the YOLOv5 model. If is_cuda is True and CUDA is available, it returns an object using CUDA, otherwise, it returns an object using CPU.

def build_model(is_cuda): net = cv2.dnn.readNet("config_files/yolov5s.onnx") if is_cuda: print("Attempty to use CUDA") net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA) net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA_FP16) else: print("Running on CPU") net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV) net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU) return net
Code language: Python (python)

cv2.VideoCapture opens the device with the web camera device number specified by video. 0 is specified if the web camera device is /dev/video0.

capture = cv2.VideoCapture(video)
Code language: Python (python)

Get video frames with capture.read()

_, frame = capture.read()
Code language: Python (python)

The frame is converted with format_yolov5, the object is detected with detect, and the detected object’s ID (class_id), the confidence level of the detected object (confidences), and the bounding boxes (boxes) are converted with wrap_detection.

inputImage = format_yolov5(frame) outs = detect(inputImage, net) class_ids, confidences, boxes = wrap_detection(inputImage, outs[0])
Code language: Python (python)

If the ID (class_id) of the detected object is included in the object_ids passed as the main argument, is_save is set to True in order to save the video being created. If object_ids is set to a person’s ID (=0), is_save will be set to True and the video will be saved when that ID (=0) is detected. See config_files/classes.txt for the ID numbers.

if len([c for c in class_ids if c in object_ids]) != 0: is_save = True
Code language: Python (python)

cv2.VideoWriter_fourcc specifies the format of the video to be saved. “mp4v” is specified as the format. The available video formats depend on the OS, supported codecs, and OpenCV’s ffmpeg support. This article may be helpful. fps (Frame Per Second) specifies the most recent fps.

writer = cv2.VideoWriter( video_name, cv2.VideoWriter_fourcc(*"mp4v"), fps, (w, h) )
Code language: Python (python)

The timestamp is updated every minute in the format YYYYmmddHHMM.

# update timestamp new_timestamp = datetime.now().strftime("%Y%m%d%H%M") if timestamp != new_timestamp: writer.release() writer = None
Code language: Python (python)

If is_save is True, the upload thread is invoked with executor.submit and the locally stored video (video_name) is uploaded to the cloud. If False, the locally stored video is simply deleted.

if is_save: print(f"save {video_name}") executor.submit(upload, s3_bucket, video_name) else: print(f"remove {video_name}") os.remove(video_name)
Code language: Python (python)

In upload, the file is uploaded to S3 by calling Bucket.upload_file using the AWS API python library boto3. The uploaded file is no longer needed, so it is deleted with os.remove.

def upload(s3_bucket, file): if s3_bucket: s3 = boto3.resource("s3") bucket = s3.Bucket(s3_bucket) # upload bucket.upload_file(file, file) print(f"upload {file} done.") # remove os.remove(file)
Code language: Python (python)

AWS

The following operations require an AWS account.

In addition, administrator privileges are required to run the AWS CLI.

To upload files to AWS S3, prepare an S3 bucket and a dedicated IAM User that can upload files to the S3 bucket. The following is an AWS CloudFormation template.

AWSTemplateFormatVersion: 2010-09-09 Description: Create IAM Users Parameters: UserName: Type: String Description: username to upload files to s3 BucketName: Type: String Description: bucket name to create Resources: S3Bucket: Type: AWS::S3::Bucket Properties: BucketName: !Ref BucketName S3UploadUser: Type: AWS::IAM::User Properties: UserName: !Ref UserName Policies: - PolicyName: S3UploadOnly PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - s3:PutObject Resource: - !Sub arn:aws:s3:::${BucketName}/* DependsOn: S3Bucket
Code language: YAML (yaml)

The permission required for boto3’s Bucket.upload_file is s3:PutObject, so only grant that permission. The S3 bucket name is global, so if you specify a bucket name that already exists, the bucket creation will fail. DependsOn: S3Bucket is specified so that S3UploadUser is created only when the bucket is created successfully.

Run the following to create the S3 bucket yolov5-opencv-save-detected and the IAM user my-s3-uploader.

aws cloudformation deploy --template-file template.yaml --stack-name my-stack-name --parameter-overrides UserName=my-s3-uploader BucketName=yolov5-opencv-save-detected --capabilities CAPABILITY_NAMED_IAM
Code language: Bash (bash)

After creating the IAM user my-s3-uploader above, use the following command to obtain an access key.

$ aws iam create-access-key --user-name my-s3-uploader { "AccessKey": { "UserName": "my-s3-uploader", "AccessKeyId": "xxxxxxxxxxxxxxxxxxxx", "Status": "Active", "SecretAccessKey": "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", "CreateDate": "2022-10-13T12:41:00+00:00" } }
Code language: Bash (bash)

Specify the obtained AccessKeyId and SecretAccessKey as AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, respectively, and execute the program as follows. Specify the bucket name as –s3-bucket.

Run

AWS_ACCESS_KEY_ID=xxxxxxxxxxxxxxxxxxxx AWS_SECRET_ACCESS_KEY=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy python3 python/yolo.py --s3-bucket yolov5-opencv-save-detected
Code language: Bash (bash)

Confirm that the video in which the person is detected is uploaded to the S3 bucket yolov5-opencv-save-detected.

$ aws s3 ls s3://yolov5-opencv-save-detected/ 2022-10-13 22:10:01 908341 202210132209.mp4 2022-10-13 22:14:01 666566 202210132213.mp4
Code language: Bash (bash)

All codes can found at https://github.com/otamajakusi/yolov5-opencv-save-detected

That’s all.

Reference