🎬 poc of video utils documentation

This commit is contained in:
SkalskiP 2023-01-31 09:36:39 +01:00
parent 8d14a4d93b
commit 1561bd0027
12 changed files with 258 additions and 130 deletions

18
.github/workflows/docs.yml vendored Normal file
View File

@ -0,0 +1,18 @@
name: Docs WorkFlow
on:
push:
branches:
- master
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.x
- run: pip install mkdocs-material
- run: pip install "mkdocstrings[python]"
- run: mkdocs gh-deploy --force

View File

@ -1,3 +1,5 @@
name: Welcome WorkFlow
on:
issues:
types: [opened]

View File

@ -61,18 +61,6 @@ A set of easy-to-use utils that will come in handy in any Computer Vision projec
pre-release stage. 🚧 Keep your eyes open for potential bugs and be aware that at this stage our API is still fluid
and may change.
## 🎬 videos
Learn how to use YOLOv8, ByteTrack and **Supervision** to detect, track and count objects. 🔥
[Subscribe](https://www.youtube.com/@Roboflow), and stay up to date with our latest YouTube videos!
<p align="center">
<a href="https://youtu.be/OS5qI9YBkfk">
<img src="https://user-images.githubusercontent.com/26109316/213702005-ddd568f0-b902-46c2-9af9-e6ff33db23bf.jpg" alt="latest-roboflow-tutorial">
</a>
</p>
## 💻 install
Pip install the supervision package in a
@ -100,9 +88,18 @@ pip install -e ".[dev]"
</details>
## 🎬 videos
Learn how to use YOLOv8, ByteTrack and **Supervision** to detect, track and count objects. 🔥
[Subscribe](https://www.youtube.com/@Roboflow), and stay up to date with our latest YouTube videos!
## 🧹 Code Quality
<p align="center">
<a href="https://youtu.be/OS5qI9YBkfk">
<img src="https://user-images.githubusercontent.com/26109316/213702005-ddd568f0-b902-46c2-9af9-e6ff33db23bf.jpg" alt="latest-roboflow-tutorial">
</a>
</p>
## 🧹 code quality
We provide two handy commands inside the `Makefile`, namely:
@ -111,6 +108,10 @@ We provide two handy commands inside the `Makefile`, namely:
So far, **there is no types checking with mypy**. See [issue](https://github.com/roboflow-ai/template-python/issues/4).
## 🧪 Tests
## 🧪 tests
[`pytests`](https://docs.pytest.org/en/7.1.x/) is used to run our tests.
## 🪪
Supervision is available under the MIT license - see the [LICENSE](https://github.com/roboflow/supervision/blob/main/LICENSE.md) file for details.

View File

@ -0,0 +1,36 @@
<div align="center">
<p>
<a align="center" href="" target="_blank">
<img
width="850"
src="https://media.roboflow.com/open-source/supervision/roboflow-supervision-banner.png?ik-sdk-version=javascript-1.4.3&updatedAt=1674062891088"
>
</a>
</p>
</div>
## 👋 hello
A set of easy-to-use utils that will come in handy in any Computer Vision project. **Supervision** is still in
pre-release stage. 🚧 Keep your eyes open for potential bugs and be aware that at this stage our API is still fluid
and may change.
## 💻 install
Pip install the supervision package in a
[**3.10>=Python>=3.7**](https://www.python.org/) environment.
!!! example "Pip install method (recommended)"
```bash
pip install subervision
```
!!! example "Git clone method (for development)"
```bash
git https://github.com/roboflow/supervision.git
cd supervision
pip install -e '.[dev]'
```
See contributing section to know more about contributing to the project

15
docs/video.md Normal file
View File

@ -0,0 +1,15 @@
## VideoInfo
:::supervision.video.VideoInfo
## VideoSink
:::supervision.video.VideoSink
## get_video_frames_generator
:::supervision.video.get_video_frames_generator
## process_video
:::supervision.video.process_video

View File

@ -1,4 +1,4 @@
site_name: supervision
site_name: Supervision
site_url: https://roboflow.github.io/supervision
site_author: Roboflow
site_description: A set of easy-to-use utils that will come in handy in any Computer Vision project
@ -9,17 +9,35 @@ copyright: Roboflow 2023. All rights reserved.
extra:
social:
- icon: fontawesome/brands/github
link: https://github.com/roboflow
- icon: fontawesome/brands/youtube
link: https://www.youtube.com/roboflow
- icon: fontawesome/brands/linkedin
link: https://www.linkedin.com/company/roboflow-ai/mycompany/
- icon: fontawesome/brands/twitter
link: https://twitter.com/roboflow
nav:
- Home: index.md
- Video: video.md
theme:
name: 'material'
logo: https://raw.githubusercontent.com/roboflow/supervision/main/docs/assets/roboflow_logomark_white.svg
favicon: https://raw.githubusercontent.com/roboflow/supervision/main/docs/assets/roboflow_logomark_color.svg
palette:
primary: 'deep purple'
accent: 'teal'
font:
text: Roboto
code: Roboto Mono
plugins:
- mkdocstrings
- search
markdown_extensions:
- admonition
- pymdownx.details
- pymdownx.superfences

View File

@ -37,7 +37,8 @@ setuptools.setup(
'pytest',
'wheel',
'notebook',
'mkdocs-material'
'mkdocs-material',
'mkdocstrings[python]'
],
},
classifiers=[

151
supervision/video.py Normal file
View File

@ -0,0 +1,151 @@
from __future__ import annotations
from typing import Callable, Generator, Optional, Tuple
import cv2
import numpy as np
class VideoInfo:
"""
A class to store video information, including width, height, fps and total number of frames.
Attributes:
width (int): width of the video in pixels
height (int): height of the video in pixels
fps (int): frames per second of the video
total_frames (int, optional): total number of frames in the video, default is None
Examples:
```python
>>> from supervision.video import VideoInfo
>>> video_info = VideoInfo.from_video_path(video_path='video.mp4')
>>> video_info
VideoInfo(width=3840, height=2160, fps=25, total_frames=538)
```
"""
def __init__(self, width: int, height: int, fps: int, total_frames: Optional[int] = None):
self.width = width
self.height = height
self.fps = fps
self.total_frames = total_frames
@classmethod
def from_video_path(cls, video_path: str) -> VideoInfo:
video = cv2.VideoCapture(video_path)
if not video.isOpened():
raise Exception(f"Could not open video at {video_path}")
width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = int(video.get(cv2.CAP_PROP_FPS))
total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
video.release()
return VideoInfo(width, height, fps, total_frames)
@property
def resolution(self) -> Tuple[int, int]:
return self.width, self.height
class VideoSink:
"""
Context manager that saves video frames to a file using OpenCV.
Attributes:
target_path (str): The path to the output file where the video will be saved.
video_info (VideoInfo): Information about the video resolution, fps, and total frame count.
Examples:
```python
>>> from supervision.video import VideoInfo
>>> from supervision.video import VideoSink
>>> video_info = VideoInfo.from_video_path(video_path='source_video.mp4')
>>> with VideoSink(target_path='target_video.mp4', video_info=video_info) as s:
... frame = ...
... s.write_frame(frame=frame)
```
"""
def __init__(self, target_path: str, video_info: VideoInfo):
self.target_path = target_path
self.video_info = video_info
self.__fourcc = cv2.VideoWriter_fourcc(*"mp4v")
self.__writer = None
def __enter__(self):
self.__writer = cv2.VideoWriter(
self.target_path,
self.__fourcc,
self.video_info.fps,
self.video_info.resolution,
)
return self
def write_frame(self, frame: np.ndarray):
self.__writer.write(frame)
def __exit__(self, exc_type, exc_val, exc_tb):
self.__writer.release()
def get_video_frames_generator(source_path: str) -> Generator[np.ndarray, None, None]:
"""
Get a generator that yields the frames of the video.
Args:
source_path (str): The path of the video file.
Returns:
(Generator[np.ndarray, None, None]): A generator that yields the frames of the video.
Examples:
```python
>>> from supervision.video import get_video_frames_generator
>>> for frame in get_video_frames_generator(source_path='source_video.mp4'):
... ...
```
"""
video = cv2.VideoCapture(source_path)
if not video.isOpened():
raise Exception(f"Could not open video at {source_path}")
success, frame = video.read()
while success:
yield frame
success, frame = video.read()
video.release()
def process_video(source_path: str, target_path: str, callback: Callable[[np.ndarray, int], np.ndarray]) -> None:
"""
Process a video file by applying a callback function on each frame and saving the result to a target video file.
Args:
source_path (str): The path to the source video file.
target_path (str): The path to the target video file.
callback (Callable[[np.ndarray, int], np.ndarray]): A function that takes in a numpy ndarray representation of a video frame and an int index of the frame and returns a processed numpy ndarray representation of the frame.
Examples:
```python
>>> from supervision.video import process_video
>>> def process_frame(frame: np.ndarray) -> np.ndarray:
... ...
>>> process_video(
... source_path='source_video.mp4',
... target_path='target_video.mp4',
... callback=process_frame
... )
```
"""
source_video_info = VideoInfo.from_video_path(video_path=source_path)
with VideoSink(target_path=target_path, video_info=source_video_info) as sink:
for index, frame in enumerate(get_video_frames_generator(source_path=source_path)):
result_frame = callback(frame, index)
sink.write_frame(frame=result_frame)

View File

@ -1,46 +0,0 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Optional, Tuple
import cv2
@dataclass
class VideoInfo:
"""
Data class containing information about the video resolution, fps, and total frame count.
:param width: int : The width of the video frames in pixels.
:param height: int : The height of the video frames in pixels.
:param fps: int : The frames per second of the video.
:param total_frames: int : The total number of frames in the video.
"""
width: int
height: int
fps: int
total_frames: Optional[int] = None
@classmethod
def from_video_path(cls, video_path: str) -> VideoInfo:
"""
Returns a VideoInfo data class containing information about the video resolution, fps, and total frame count.
:param video_path: str : The path of the video file.
:return: VideoInfo : A data class containing information about the video resolution, fps, and total frame count.
"""
video = cv2.VideoCapture(video_path)
if not video.isOpened():
raise Exception(f"Could not open video at {video_path}")
width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = int(video.get(cv2.CAP_PROP_FPS))
total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
video.release()
return VideoInfo(width, height, fps, total_frames)
@property
def resolution(self) -> Tuple[int, int]:
return self.width, self.height

View File

@ -1,48 +0,0 @@
import cv2
import numpy as np
from supervision.video.dataclasses import VideoInfo
class VideoSink:
"""
A context manager that uses OpenCV to save video frames to a file.
:param output_path: str : The path to the output file where the video will be saved.
:param video_info: VideoInfo : An instance of VideoInfo containing information about the video resolution, fps, and total frame count.
"""
def __init__(self, output_path: str, video_info: VideoInfo):
"""
Initializes the VideoSink with the specified output path and video information.
"""
self.output_path = output_path
self.video_info = video_info
self.fourcc = cv2.VideoWriter_fourcc(*"mp4v")
self.writer = None
def __enter__(self):
"""
Opens the output file and returns the VideoSink instance.
"""
self.writer = cv2.VideoWriter(
self.output_path,
self.fourcc,
self.video_info.fps,
self.video_info.resolution,
)
return self
def write_frame(self, frame: np.ndarray):
"""
Writes a frame to the output video file.
:param frame: np.ndarray : The frame to be written.
"""
self.writer.write(frame)
def __exit__(self, exc_type, exc_val, exc_tb):
"""
Closes the output file.
"""
self.writer.release()

View File

@ -1,20 +0,0 @@
from typing import Generator
import cv2
def get_video_frames_generator(video_path: str) -> Generator[int, None, None]:
"""
Returns a generator that yields the frames of the video.
:param video_path: str : The path of the video file.
:return: Generator[int, None, None] : Generator that yields the frames of the video.
"""
video = cv2.VideoCapture(video_path)
if not video.isOpened():
raise Exception(f"Could not open video at {video_path}")
success, frame = video.read()
while success:
yield frame
success, frame = video.read()
video.release()