Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9590b72f44 | ||
|
|
77dd68ee27 | ||
|
|
c61fbf6355 | ||
|
|
62efdee025 | ||
|
|
f28874c0c2 | ||
|
|
982c801db7 | ||
|
|
0ce6846880 | ||
|
|
f5baeef7ae | ||
|
|
09f3ea13e7 | ||
|
|
b7ad4c1df0 | ||
|
|
ed7606f886 | ||
|
|
a0f5c9a41a | ||
|
|
23fca01cfc | ||
|
|
35cb41bd21 | ||
|
|
6dce8ba38d | ||
|
|
10f4b86b70 | ||
|
|
6fa7395b99 | ||
|
|
eddcf3d09f | ||
|
|
e275a239ad | ||
|
|
cd36ae05e9 | ||
|
|
3997182ee7 | ||
|
|
845e47f472 | ||
|
|
565cd919e9 | ||
|
|
e514142b3d | ||
|
|
72fc49f124 | ||
|
|
51dd062407 | ||
|
|
e19f3120ca | ||
|
|
6b5eed2ba1 | ||
|
|
5a29ad9525 | ||
|
|
9b7099d3fc | ||
|
|
ee8a61a3e7 | ||
|
|
48035c14e2 | ||
|
|
ddb3515b56 | ||
|
|
85ba8be9dd | ||
|
|
129118817a | ||
|
|
9ed1f07ca0 | ||
|
|
38be1be4b2 | ||
|
|
d33715464b | ||
|
|
e6fab4b7fa | ||
|
|
8b60ee1a0e | ||
|
|
a6ee3e98c7 | ||
|
|
8b2f961ba8 | ||
|
|
c8a8540698 | ||
|
|
d6a24d0dd9 | ||
|
|
d94db74493 | ||
|
|
fdaadacbe3 | ||
|
|
819fcde7e4 | ||
|
|
ad7a9988e7 | ||
|
|
cee411689d | ||
|
|
600099e8ab | ||
|
|
7912e7e5bb | ||
|
|
2dee466abc | ||
|
|
c010656161 | ||
|
|
ca9bff184c | ||
|
|
e4cedb6861 | ||
|
|
664bf3cf79 | ||
|
|
a673640aad | ||
|
|
7d8b2b3fc5 | ||
|
|
93f5622d15 | ||
|
|
a78bf58c6d | ||
|
|
e5fef4501b | ||
|
|
0c69d0d333 | ||
|
|
6d92214718 | ||
|
|
eef16454e2 | ||
|
|
87c63fe7a1 | ||
|
|
9eba61de10 | ||
|
|
b385508606 | ||
|
|
d70bc4f58b | ||
|
|
c2dedba3db | ||
|
|
6ca9555efd | ||
|
|
ae2c356321 | ||
|
|
f8f62891de | ||
|
|
162a46cae1 | ||
|
|
b5ccf6882d | ||
|
|
6576a846d9 | ||
|
|
49a0f372d7 | ||
|
|
91c5f3f106 | ||
|
|
0b4cc5ff5b | ||
|
|
0ed48f0227 | ||
|
|
acbaf4ff25 | ||
|
|
124be19928 | ||
|
|
839d117f6c | ||
|
|
9955f0137a | ||
|
|
9fcabc8b9a | ||
|
|
e5ff27b0aa | ||
|
|
a00ea2087f | ||
|
|
2dd1bc5df6 | ||
|
|
83dfc87ef5 | ||
|
|
00c96c03ee | ||
|
|
6f1eb63825 | ||
|
|
e4d0be777d | ||
|
|
54328e37a0 | ||
|
|
3dd9e56da4 | ||
|
|
6d35301672 | ||
|
|
8e4fedf3f1 | ||
|
|
cd0aac2655 | ||
|
|
6969e7a78c | ||
|
|
ea3a0e9a4e | ||
|
|
53950fd426 | ||
|
|
c67d977682 | ||
|
|
5c5bfe5544 | ||
|
|
0ebab21e9c | ||
|
|
6566063e2b | ||
|
|
ecdbea340e | ||
|
|
5ce6e484b8 | ||
|
|
2d0404e67c | ||
|
|
d89ebde4d1 | ||
|
|
a897905955 | ||
|
|
b8a2a6996c | ||
|
|
2ca9bee083 | ||
|
|
6af1ab31f4 | ||
|
|
5b56d40ef9 | ||
|
|
2abdccbe1d | ||
|
|
d925cdf666 | ||
|
|
c592cd9ff3 | ||
|
|
66c49b2e80 | ||
|
|
0efb55ec19 | ||
|
|
11f483b75e | ||
|
|
1688e71ed9 | ||
|
|
45aedbda7e | ||
|
|
6a1b2b8eb4 | ||
|
|
0dc9188c5b | ||
|
|
a4de1a0f47 | ||
|
|
195f4618a3 | ||
|
|
fd2d63c1d7 | ||
|
|
c1887da1e8 | ||
|
|
efbb5e2599 | ||
|
|
6149bacc77 | ||
|
|
86b268d396 | ||
|
|
a5b51a11e5 | ||
|
|
2b1abb1f5b | ||
|
|
cfaea751f2 | ||
|
|
6e8e6fb276 | ||
|
|
6c2c3f8862 | ||
|
|
64acee9bec | ||
|
|
5f17003f17 | ||
|
|
cda79458a9 | ||
|
|
566844f591 | ||
|
|
4c35dee692 | ||
|
|
e2ca285610 | ||
|
|
c999fad06f | ||
|
|
a542406945 | ||
|
|
00a123b5fb | ||
|
|
3d939fd4a2 | ||
|
|
f005ea1d17 | ||
|
|
f6326d5590 | ||
|
|
b4262bb08e | ||
|
|
a9dad95fa7 | ||
|
|
91ff090168 | ||
|
|
6d68a825ab | ||
|
|
2043ba7feb | ||
|
|
5ac08abcf3 | ||
|
|
1c37d143b4 | ||
|
|
f78b891011 | ||
|
|
f2fc5b2952 | ||
|
|
09565ddacd | ||
|
|
780db7520d | ||
|
|
fd9083ae76 | ||
|
|
ebe3fd4d2f | ||
|
|
ce4204adc1 | ||
|
|
48450b5da3 | ||
|
|
fbd6f4dc5a | ||
|
|
4c1f33189b | ||
|
|
dcce0e4807 | ||
|
|
b8e88bf9c5 | ||
|
|
3b09a6356a | ||
|
|
5ced8c4ea0 | ||
|
|
b25556dae9 | ||
|
|
beaa0be014 | ||
|
|
a765bd9bd7 | ||
|
|
7bf03f5a3c | ||
|
|
93d419d7c6 | ||
|
|
44614dea94 | ||
|
|
ac379e467a | ||
|
|
7507d3a68c | ||
|
|
06bd638f15 | ||
|
|
5d60d78362 | ||
|
|
377a87dbba | ||
|
|
c2c7ca03d3 | ||
|
|
21482e9109 | ||
|
|
0bf55b7ac7 | ||
|
|
0b48ad1c22 | ||
|
|
31e478b84c | ||
|
|
2bbb9ef2ed | ||
|
|
b85b41d5f1 | ||
|
|
8419279fb9 | ||
|
|
1bab3fff8f | ||
|
|
82a0aa2463 | ||
|
|
f5a8bb93e8 | ||
|
|
a90a1c967b | ||
|
|
e2c380acbc | ||
|
|
819d74f57c | ||
|
|
47e6a4bc88 | ||
|
|
09de0f4f06 | ||
|
|
f1e0c9cdd7 | ||
|
|
72d55a2938 | ||
|
|
3abb6245c1 | ||
|
|
a352e23d95 | ||
|
|
75a2036ea3 | ||
|
|
3d7642c09e | ||
|
|
fabbff2b60 | ||
|
|
751eddc0ce | ||
|
|
8df7aea949 | ||
|
|
bbc96e14c9 | ||
|
|
5695b7f58f | ||
|
|
a1c58a92e2 | ||
|
|
886a44b102 | ||
|
|
9b91c779e5 | ||
|
|
3697b2298c | ||
|
|
e861878a22 | ||
|
|
834fd8db05 | ||
|
|
fba1713944 | ||
|
|
b143121859 | ||
|
|
132cc056f6 | ||
|
|
f6e0896278 | ||
|
|
de4f060d3b | ||
|
|
14b7f91c5c | ||
|
|
5016ed3c8a | ||
|
|
d570366fb4 | ||
|
|
95ef33a102 | ||
|
|
fc11beac78 | ||
|
|
cb1fcecaf2 | ||
|
|
afdbdc55f7 | ||
|
|
28dfc39c02 | ||
|
|
793f246ebc | ||
|
|
9aa9ef5bbe | ||
|
|
6e18804c55 | ||
|
|
bca3c60bb3 | ||
|
|
7f7b25d7e7 | ||
|
|
72506888c0 | ||
|
|
c5cc24f22d | ||
|
|
47574ed29d | ||
|
|
6119f2017e | ||
|
|
bbc37b4b6a | ||
|
|
e12bfe263d | ||
|
|
373fbae1fa | ||
|
|
790ccace2c | ||
|
|
a99015ac2d | ||
|
|
52c772d778 | ||
|
|
5af13c0f8c | ||
|
|
e0edb28ce6 | ||
|
|
c35a35cefd | ||
|
|
9bd8f71f26 | ||
|
|
e29e3832fb | ||
|
|
a287e68c92 | ||
|
|
0422a66eaa | ||
|
|
31db2ded11 | ||
|
|
9ab9650763 | ||
|
|
353bd5336b | ||
|
|
7301156e46 | ||
|
|
e68c908077 | ||
|
|
2edcbdc487 | ||
|
|
25c2f5cf0d | ||
|
|
316cb6271d | ||
|
|
70e9af9200 | ||
|
|
38f89125b1 | ||
|
|
80d76e712a | ||
|
|
a296712000 | ||
|
|
12d7c369db | ||
|
|
c8223cbab2 | ||
|
|
2c522a9931 | ||
|
|
fc0e133ea9 | ||
|
|
15364ac9a3 | ||
|
|
e82b5b9d74 | ||
|
|
c8f5c741fd | ||
|
|
8d464aa1b5 | ||
|
|
ce28ab7fc7 | ||
|
|
fae1bc2661 | ||
|
|
4663fab4e4 | ||
|
|
db0892f3e9 | ||
|
|
d330bafaa7 | ||
|
|
8b91d733af | ||
|
|
97bf652d90 | ||
|
|
b0bbb74558 | ||
|
|
1d73468dc5 | ||
|
|
dfd2a60855 | ||
|
|
f563c5a634 | ||
|
|
35272b40d3 | ||
|
|
9320be5d5b | ||
|
|
d109fba9d8 | ||
|
|
42d92ba913 | ||
|
|
1e2dc8f88b | ||
|
|
f716b7fa3b | ||
|
|
f6d52b53db | ||
|
|
61cca76acc | ||
|
|
9ad850af46 | ||
|
|
eee6a85eaa | ||
|
|
bb87ab0a6e | ||
|
|
da7c5cd65a | ||
|
|
ceefe0bab2 | ||
|
|
78439e03cc | ||
|
|
b66416e074 | ||
|
|
6c7c49e051 | ||
|
|
b1b69ad972 | ||
|
|
52be9dc818 | ||
|
|
c7e95483bf | ||
|
|
65f6e418a1 | ||
|
|
1fef946a19 | ||
|
|
4c3d51f186 | ||
|
|
9340cb962b | ||
|
|
6578b4ea52 | ||
|
|
0bd60874ca | ||
|
|
b02ec93b30 | ||
|
|
89ee57f519 | ||
|
|
1c8398942a | ||
|
|
3e7e4caf3f | ||
|
|
a3ea8cdb1f | ||
|
|
32dca290ef | ||
|
|
03f623920a | ||
|
|
98476dfacf | ||
|
|
35ff87b08d | ||
|
|
03eed3fd64 | ||
|
|
272d9ed5bf | ||
|
|
371f8ecc5b | ||
|
|
171687f3ab | ||
|
|
421ad21c98 | ||
|
|
50358e96b6 | ||
|
|
86e10db19e | ||
|
|
deb1c9c4f4 | ||
|
|
6679b1f5a9 | ||
|
|
c7de70ed20 | ||
|
|
2cdb70eb91 | ||
|
|
2ef029ef4b | ||
|
|
dce96d462e | ||
|
|
3fcbef9482 | ||
|
|
c8b966be1a | ||
|
|
72621dce32 | ||
|
|
098d5e3eb1 | ||
|
|
8c49465d49 | ||
|
|
4ad36db3bb | ||
|
|
a933b93234 | ||
|
|
cbb58a8c6f | ||
|
|
8fc22f0281 | ||
|
|
4b7b347aea | ||
|
|
5c6745291b | ||
|
|
83d490dadc | ||
|
|
005cdbd37a | ||
|
|
ade2668ae3 | ||
|
|
46e5965680 | ||
|
|
aab3160cd7 | ||
|
|
13ab215fdf | ||
|
|
a08cda30fb | ||
|
|
652b0a190f | ||
|
|
6f87e2639e | ||
|
|
e1393691a4 | ||
|
|
90f8582e4f | ||
|
|
0c9fea4d9f | ||
|
|
cdca2e6b51 | ||
|
|
8407d5a692 | ||
|
|
eaf6cdd477 | ||
|
|
f447b6fa95 | ||
|
|
8c6ac2dd29 | ||
|
|
22b6d3d464 | ||
|
|
dda3900943 | ||
|
|
ac0c367e81 | ||
|
|
6f2490cc23 | ||
|
|
4a9d06eb50 | ||
|
|
21e311a319 | ||
|
|
71a14812fe | ||
|
|
35fdcb0a1a | ||
|
|
6d26a9766c | ||
|
|
1cfaef9405 | ||
|
|
063e2092c8 | ||
|
|
cb215d26f3 | ||
|
|
8bbe80e683 | ||
|
|
9a0483446a | ||
|
|
f4561f9ba7 | ||
|
|
beaf89cb22 | ||
|
|
96b58c2860 | ||
|
|
8873da4a30 | ||
|
|
1362850986 | ||
|
|
98a4391677 | ||
|
|
036a956473 | ||
|
|
b381fa198f | ||
|
|
8ca2e9fe8d | ||
|
|
bc1d73a307 | ||
|
|
9679cd5c4e | ||
|
|
00817df9e2 | ||
|
|
ec189bf388 | ||
|
|
58bdce5707 | ||
|
|
8262b6e64b | ||
|
|
72fe0bf47f | ||
|
|
137078602b | ||
|
|
8fa21dd165 | ||
|
|
da70389546 | ||
|
|
cc1525707a | ||
|
|
b3aee43c3b | ||
|
|
932c1aa91e | ||
|
|
76ee05420d | ||
|
|
a470bc2976 | ||
|
|
cf725dd4db | ||
|
|
317ac0810e | ||
|
|
cb6018ef04 | ||
|
|
580fcdde4c | ||
|
|
ad4f44644c | ||
|
|
0c8a9d34a6 | ||
|
|
d067f32678 | ||
|
|
d251677774 | ||
|
|
b1780fbae5 | ||
|
|
e8cb962fcc | ||
|
|
1e84351d60 | ||
|
|
baa0402d5a | ||
|
|
c54d16c57e | ||
|
|
1adb75520f | ||
|
|
db51a9ff98 | ||
|
|
f044398c17 | ||
|
|
704652a0d2 | ||
|
|
6e3001194d | ||
|
|
d7774381cc | ||
|
|
1c8ec0a287 | ||
|
|
118e3a496c | ||
|
|
a139fb0b49 | ||
|
|
afdbb0a75c | ||
|
|
001a4254f8 | ||
|
|
2e72e70893 | ||
|
|
2907c40ecc | ||
|
|
2c67cee720 | ||
|
|
0ddbcb2010 | ||
|
|
40d4c6f554 | ||
|
|
bf88636487 | ||
|
|
a444b3bde8 | ||
|
|
4009f9d886 | ||
|
|
3f926c618a | ||
|
|
71d6670e7d | ||
|
|
08226e9199 | ||
|
|
535cb2a91b | ||
|
|
8e845caeeb | ||
|
|
9e2a535044 | ||
|
|
f737782ae8 |
@@ -0,0 +1,41 @@
|
||||
# see https://docs.codecov.io/docs/codecov-yaml
|
||||
# Validation check:
|
||||
# $ curl --data-binary @.codecov.yml https://codecov.io/validate
|
||||
|
||||
# https://docs.codecov.io/docs/codecovyml-reference
|
||||
codecov:
|
||||
bot: "codecov-io"
|
||||
strict_yaml_branch: "yaml-config"
|
||||
require_ci_to_pass: yes
|
||||
notify:
|
||||
# after_n_builds: 2
|
||||
wait_for_ci: yes
|
||||
|
||||
coverage:
|
||||
precision: 0 # 2 = xx.xx%, 0 = xx%
|
||||
round: nearest # how coverage is rounded: down/up/nearest
|
||||
range: 40...100 # custom range of coverage colors from red -> yellow -> green
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
informational: true
|
||||
target: 95% # specify the target coverage for each commit status
|
||||
threshold: 10% # allow this little decrease on project
|
||||
# branches: master
|
||||
if_ci_failed: error
|
||||
patch:
|
||||
default:
|
||||
informational: true
|
||||
target: 95% # enforce same coverage target for new/changed code as the project
|
||||
threshold: 10% # allow only a small decrease on patch coverage
|
||||
changes: false
|
||||
|
||||
# https://docs.codecov.com/docs/github-checks#disabling-github-checks-patch-annotations
|
||||
github_checks:
|
||||
annotations: false
|
||||
|
||||
comment:
|
||||
layout: header, diff
|
||||
require_changes: false
|
||||
behavior: default # update if exists else create new
|
||||
# branches: *
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# These owners will be the default owners for everything in
|
||||
# the repo. They will be requested for review when someone
|
||||
# opens a pull request.
|
||||
* @SkalskiP @onuralpszr
|
||||
* @SkalskiP @soumik12345
|
||||
|
||||
@@ -21,20 +21,20 @@ community include:
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
and learning from the experience
|
||||
- Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
@@ -83,18 +83,20 @@ git push -u origin <your_branch_name>
|
||||
|
||||
Use conventional commit messages to clearly describe your changes. The format is:
|
||||
|
||||
<type>\[optional scope\]: <description>
|
||||
```
|
||||
<type>[optional scope]: <description>
|
||||
```
|
||||
|
||||
Common types include:
|
||||
|
||||
- feat: A new feature
|
||||
- fix: A bug fix
|
||||
- docs: Documentation only changes
|
||||
- style: Changes that do not affect the meaning of the code (white-space, formatting, etc)
|
||||
- refactor: A code change that neither fixes a bug nor adds a feature
|
||||
- perf: A code change that improves performance
|
||||
- test: Adding missing tests or correcting existing tests
|
||||
- chore: Changes to the build process or auxiliary tools and libraries
|
||||
- `feat`: A new feature
|
||||
- `fix`: A bug fix
|
||||
- `docs`: Documentation only changes
|
||||
- `style`: Changes that do not affect the meaning of the code (white-space, formatting, etc)
|
||||
- `refactor`: A code change that neither fixes a bug nor adds a feature
|
||||
- `perf`: A code change that improves performance
|
||||
- `test`: Adding missing tests or correcting existing tests
|
||||
- `chore`: Changes to the build process or auxiliary tools and libraries
|
||||
|
||||
Then, go back to your fork of the `supervision` repository, click "Pull Requests", and click "New Pull Request".
|
||||
|
||||
@@ -116,7 +118,7 @@ When creating new functions, please ensure you have the following:
|
||||
2. Unit tests for the function.
|
||||
3. Examples in the documentation for the function.
|
||||
4. Created an entry in our docs to autogenerate the documentation for the function.
|
||||
5. Please share a Google Colab with minimal code to test new feature or reproduce PR whenever it is possible. Please ensure that Google Colab can be accessed without any issue.
|
||||
5. Please share a Google Colab with minimal code to test a new feature or reproduce the issue whenever possible. Please ensure that Google Colab can be accessed without any restrictions.
|
||||
|
||||
When you submit your Pull Request, you will be asked to sign a Contributor License Agreement (CLA) by the `cla-assistant` GitHub bot. We can only respond to PRs from contributors who have signed the project CLA.
|
||||
|
||||
@@ -128,7 +130,7 @@ PRs must pass all tests and linting requirements before they can be merged.
|
||||
|
||||
Before starting your work on the project, set up your development environment:
|
||||
|
||||
1. Clone your fork of the project (recommended to use shallow clone of develop branch):
|
||||
1. **Clone your fork of the project:**
|
||||
|
||||
**Option A: Recommended for most contributors (shallow clone of develop branch):**
|
||||
|
||||
@@ -139,33 +141,50 @@ Before starting your work on the project, set up your development environment:
|
||||
|
||||
Replace `YOUR_USERNAME` with your GitHub username.
|
||||
|
||||
> Note: Using `--depth 1` creates a shallow clone with minimal history and `-b develop` ensures you start with the development branch. This significantly reduces download size while providing everything needed to contribute.
|
||||
> **Note**: Using `--depth 1` creates a shallow clone with minimal history and `-b develop` ensures you start with the development branch. This significantly reduces download size while providing everything needed to contribute.
|
||||
|
||||
**Option B: Full repository clone (if you need complete history):**
|
||||
|
||||
```bash
|
||||
git clone https://github.com/YOUR_USERNAME/supervision.git
|
||||
cd supervision
|
||||
git checkout develop
|
||||
```
|
||||
|
||||
2. Create and activate a virtual environment:
|
||||
2. **Set up the upstream remote:**
|
||||
|
||||
```bash
|
||||
git remote add upstream https://github.com/roboflow/supervision.git
|
||||
git fetch upstream
|
||||
```
|
||||
|
||||
3. **Create and activate a virtual environment:**
|
||||
|
||||
**On Linux/macOS:**
|
||||
|
||||
```bash
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
3. Install `uv`:
|
||||
**On Windows:**
|
||||
|
||||
```cmd
|
||||
python -m venv .venv
|
||||
.venv\Scripts\activate
|
||||
```
|
||||
|
||||
4. **Install `uv`:**
|
||||
|
||||
Follow the instructions on the [uv installation page](https://docs.astral.sh/uv/getting-started/installation/).
|
||||
|
||||
4. Install project dependencies:
|
||||
5. **Install project dependencies:**
|
||||
|
||||
```bash
|
||||
uv pip install -r pyproject.toml --extra dev --extra docs --extra metrics
|
||||
uv pip install -r pyproject.toml --group dev --group docs --extra metrics
|
||||
```
|
||||
|
||||
5. Run pytest to verify the setup:
|
||||
6. **Verify the setup:**
|
||||
|
||||
```bash
|
||||
uv run pytest
|
||||
@@ -181,13 +200,29 @@ Furthermore, we have integrated a pre-commit GitHub Action into our workflow. Th
|
||||
|
||||
To run the pre-commit tool, follow these steps:
|
||||
|
||||
1. Install pre-commit by running the following command: `uv pip install -r pyproject.toml --extra dev`. It will not only install pre-commit but also install all the deps and dev-deps of project
|
||||
1. **Install pre-commit** (already included if you followed the installation steps above):
|
||||
|
||||
2. Once pre-commit is installed, navigate to the project's root directory.
|
||||
```bash
|
||||
uv sync --group dev
|
||||
```
|
||||
|
||||
3. Run the command `pre-commit run --all-files`. This will execute the pre-commit hooks configured for this project against the modified files. If any issues are found, the pre-commit tool will provide feedback on how to resolve them. Make the necessary changes and re-run the pre-commit command until all issues are resolved.
|
||||
2. **Navigate to the project's root directory** (if not already there).
|
||||
|
||||
4. You can also install pre-commit as a git hook by executing `pre-commit install`. Every time you do a `git commit` pre-commit run automatically for you.
|
||||
3. **Run pre-commit checks**:
|
||||
|
||||
```bash
|
||||
uv run pre-commit run --all-files
|
||||
```
|
||||
|
||||
This will execute the pre-commit hooks configured for this project. If any issues are found, the pre-commit tool will provide feedback on how to resolve them. Make the necessary changes and re-run the command until all issues are resolved.
|
||||
|
||||
4. **Install pre-commit as a git hook** (optional but recommended):
|
||||
|
||||
```bash
|
||||
uv run pre-commit install
|
||||
```
|
||||
|
||||
This will automatically run pre-commit checks every time you make a `git commit`.
|
||||
|
||||
### Docstrings
|
||||
|
||||
@@ -197,13 +232,27 @@ All new functions and classes in `supervision` should include docstrings. This i
|
||||
|
||||
### Type checking
|
||||
|
||||
So far, **there is no type checking with mypy**. See [issue](https://github.com/roboflow-ai/template-python/issues/4).
|
||||
Currently, there is no systematic type checking with mypy implemented in the project. This is a known limitation that may be addressed in future updates.
|
||||
|
||||
## 📝 Documentation
|
||||
|
||||
The `supervision` documentation is stored in a folder called `docs`. The project documentation is built using `mkdocs`.
|
||||
|
||||
To run the documentation, install the project requirements with `uv pip install -r pyproject.toml --extra dev --extra docs`. Then, run `mkdocs serve` to start the documentation server.
|
||||
To run the documentation locally:
|
||||
|
||||
1. **Install documentation dependencies** (if not already installed):
|
||||
|
||||
```bash
|
||||
uv sync --group docs
|
||||
```
|
||||
|
||||
2. **Start the documentation server**:
|
||||
|
||||
```bash
|
||||
uv run mkdocs serve
|
||||
```
|
||||
|
||||
3. **Access the documentation** at `http://127.0.0.1:8000` in your browser.
|
||||
|
||||
You can learn more about mkdocs on the [mkdocs website](https://www.mkdocs.org/).
|
||||
|
||||
@@ -216,15 +265,121 @@ submit a PR with your example. Here are some guidelines for submitting a new exa
|
||||
- Create a new notebook in the [`docs/notebooks`](https://github.com/roboflow/supervision/tree/develop/docs/notebooks) folder.
|
||||
- Add a link to the new notebook in [`docs/theme/cookbooks.html`](https://github.com/roboflow/supervision/blob/develop/docs/theme/cookbooks.html). Make sure to add the path to the new notebook, as well as a title, labels, author and supervision version.
|
||||
- Use the [Count Objects Crossing the Line](https://supervision.roboflow.com/develop/notebooks/count-objects-crossing-the-line/) example as a template for your new example.
|
||||
- Freeze the version of `supervision` you are using.
|
||||
- Place an appropriate Open in Colab button at the top of the notebook. You can find an example of such a button in the aforementioned `Count Objects Crossing the Line` cookbook.
|
||||
- Notebook should be self-contained. If you rely on external data ( videos, images, etc.) or libraries, include download and installation commands in the notebook.
|
||||
- Pin the version of `supervision` you are using in the notebook.
|
||||
- Place an appropriate "Open in Colab" button at the top of the notebook. You can find an example of such a button in the aforementioned `Count Objects Crossing the Line` cookbook.
|
||||
- **Notebook should be self-contained**. If you rely on external data (videos, images, etc.) or libraries, include download and installation commands in the notebook.
|
||||
- Annotate the code with appropriate comments, including links to the documentation describing each of the tools you have used.
|
||||
|
||||
## 🧪 Tests
|
||||
|
||||
[`pytests`](https://docs.pytest.org/en/7.1.x/) is used to run our tests.
|
||||
[`pytest`](https://docs.pytest.org/en/7.1.x/) is used to run our tests.
|
||||
|
||||
To run tests:
|
||||
|
||||
```bash
|
||||
uv run pytest
|
||||
```
|
||||
|
||||
To run tests with coverage:
|
||||
|
||||
```bash
|
||||
uv run pytest --cov=supervision
|
||||
```
|
||||
|
||||
## 🔍 PR Review Guidelines
|
||||
|
||||
These guidelines help reviewers provide consistent, actionable feedback efficiently. Your goals: validate completeness, identify risks, provide actionable feedback, and highlight quality gaps.
|
||||
|
||||
### Overall Recommendation
|
||||
|
||||
Start with a clear recommendation using these levels:
|
||||
|
||||
- 🟢 **Approve** — Ready to merge
|
||||
- 🟡 **Minor Suggestions** — Improvements recommended but not blocking
|
||||
- 🟠 **Request Changes** — Must address issues before merge
|
||||
- 🔴 **Block** — Critical issues require major rework
|
||||
|
||||
Example: `🟠 Request Changes — Missing unit tests for PolygonMerger and no mkdocs entry.`
|
||||
|
||||
### PR Completeness
|
||||
|
||||
Verify requirements are met (✅ Complete / ⚠️ Incomplete / ❌ Missing / 🔵 N/A):
|
||||
|
||||
- [ ] Clear description of what changed and why
|
||||
- [ ] Tests added/updated for new functionality or bug fixes
|
||||
- [ ] Docstrings follow [Google-style](https://google.github.io/styleguide/pyguide.html#383-functions-and-methods)
|
||||
- [ ] Docs entry added to mkdocs (new functions/classes only)
|
||||
- [ ] Google Colab provided (if demonstrating feature/fix)
|
||||
- [ ] Screenshots/videos included (visual changes only)
|
||||
|
||||
Call out missing items explicitly in your review.
|
||||
|
||||
### Quality Scores
|
||||
|
||||
Use **n/5 scoring** with inline code comments for specifics:
|
||||
|
||||
**Code Quality (n/5):**
|
||||
|
||||
- 5/5 🟢 Excellent — 4/5 🟢 Good — 3/5 🟡 Acceptable — 2/5 🟠 Needs Work — 1/5 🔴 Poor
|
||||
- Check: correctness (edge cases, None checks, bounds), Python best practices (idiomatic patterns, error handling, type hints), project conventions (docstrings, linting, import order, PEP 8 naming)
|
||||
|
||||
**Testing (n/5):**
|
||||
|
||||
- 5/5 🟢 Comprehensive — 4/5 🟢 Good — 3/5 🟡 Adequate — 2/5 🟠 Insufficient — 1/5 🔴 Missing
|
||||
- Verify: unit tests for new code, edge cases covered, specific assertions, realistic scenarios, clear test names
|
||||
|
||||
**Documentation (n/5):**
|
||||
|
||||
- 5/5 🟢 Excellent — 4/5 🟢 Good — 3/5 🟡 Adequate — 2/5 🟠 Insufficient — 1/5 🔴 Missing
|
||||
- Confirm: docstrings for public functions/classes, parameters/returns/exceptions documented, usage examples, mkdocs integration, changelog entry for user-facing changes
|
||||
|
||||
### Risk Assessment
|
||||
|
||||
Flag risks with severity (5/5 🔴 Critical — 4/5 🟠 High — 3/5 🟡 Medium — 2/5 🟢 Low — 1/5 🟢 Negligible):
|
||||
|
||||
**Common risk categories:**
|
||||
|
||||
1. **Breaking changes** — API changes, removed features, behavior modifications (must include migration guide)
|
||||
2. **Performance** — Inefficient algorithms, memory-intensive operations, bottlenecks
|
||||
3. **Compatibility** — New Python/dependency requirements, platform-specific code
|
||||
4. **Security** — Unvalidated input, code execution risks, data exposure
|
||||
|
||||
### Review Summary Template
|
||||
|
||||
```markdown
|
||||
## Review Summary
|
||||
|
||||
**Recommendation:** [emoji] [Status] — [justification]
|
||||
|
||||
**PR Completeness:**
|
||||
- ✅ Complete: [items]
|
||||
- ❌ Missing: [gaps]
|
||||
|
||||
**Quality Scores:**
|
||||
- Code: n/5 [emoji] — [reason]
|
||||
- Testing: n/5 [emoji] — [reason]
|
||||
- Documentation: n/5 [emoji] — [reason]
|
||||
|
||||
**Risk Level:** n/5 [emoji] — [description]
|
||||
|
||||
**Critical Issues (Must Fix):**
|
||||
1. [Issue] — See comment on `file.py`
|
||||
|
||||
**Suggestions (Optional):**
|
||||
1. [Improvement] — See suggestion on `file.py`
|
||||
|
||||
**Next Steps:**
|
||||
1. [Action item]
|
||||
```
|
||||
|
||||
### Review Best Practices
|
||||
|
||||
**DO:** Use inline GitHub comments with suggestions, explain *why* (not just *what*), distinguish blocking vs. nice-to-have, acknowledge good work, run linter if needed (`uv run pre-commit run --all-files`)
|
||||
|
||||
**DON'T:** Mention line numbers in summary (use inline comments), give vague feedback, nitpick style (defer to tools), assume knowledge of conventions, block on minor issues
|
||||
|
||||
**Tone:** Be respectful, specific, pragmatic, and consistent. Focus on actionable feedback that moves PRs toward merge.
|
||||
|
||||
## 📄 License
|
||||
|
||||
By contributing, you agree that your contributions will be licensed under an [MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md).
|
||||
By contributing, you agree that your contributions will be licensed under an [MIT license](../LICENSE.md).
|
||||
@@ -1,65 +1,68 @@
|
||||
name: 🐞 Bug Report
|
||||
# title: " "
|
||||
description: Problems with Supervision
|
||||
title: "[Bug]: "
|
||||
description: Report a bug or unexpected behavior in Supervision
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for submitting a Supervision 🐞 Bug Report!
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Search before asking
|
||||
description: >
|
||||
Please search the [issues](https://github.com/roboflow/supervision/issues) to see if a similar bug report already exists.
|
||||
description: Please search [issues](https://github.com/roboflow/supervision/issues) and [discussions](https://github.com/roboflow/supervision/discussions) first.
|
||||
options:
|
||||
- label: >
|
||||
I have searched the Supervision [issues](https://github.com/roboflow/supervision/issues) and found no similar bug report.
|
||||
- label: I have searched the issues and discussions and found no similar bug report.
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Bug
|
||||
description: Provide console output with error messages and/or screenshots of the bug.
|
||||
description: Describe the bug, what you expected, and what actually happened.
|
||||
placeholder: |
|
||||
💡 ProTip! Include as much information as possible (screenshots, logs, tracebacks etc.) to receive the most helpful response.
|
||||
**What's the bug?**
|
||||
Brief description...
|
||||
|
||||
**Expected behavior:**
|
||||
What should happen...
|
||||
|
||||
**Actual behavior:**
|
||||
What actually happens...
|
||||
|
||||
**Error/Traceback (if any):**
|
||||
```python
|
||||
# Paste error messages here
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Environment
|
||||
description: Please specify the software and hardware you used to produce the bug.
|
||||
description: Your setup details
|
||||
placeholder: |
|
||||
- Supervision: 0.1.0
|
||||
- OS: Ubuntu 20.04
|
||||
- Python: 3.8.10
|
||||
- Supervision: 0.23.0
|
||||
- Python: 3.10.12
|
||||
- OS: Ubuntu 22.04
|
||||
value: |
|
||||
- Supervision:
|
||||
- Python:
|
||||
- OS:
|
||||
validations:
|
||||
required: false
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Minimal Reproducible Example
|
||||
description: >
|
||||
When asking a question, people will be better able to provide help if you provide code that they can easily understand and use to **reproduce** the problem.
|
||||
This is referred to by community members as creating a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example).
|
||||
description: Code to reproduce the bug ([guide](https://stackoverflow.com/help/minimal-reproducible-example))
|
||||
placeholder: |
|
||||
```
|
||||
# Code to reproduce your issue here
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
# Your code here
|
||||
```
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional
|
||||
description: Anything else you would like to share?
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Are you willing to submit a PR?
|
||||
description: >
|
||||
(Optional) We encourage you to submit a [Pull Request](https://github.com/roboflow/supervision/pulls) (PR) to help improve Supervision for everyone, especially if you have a good understanding of how to implement a fix or feature.
|
||||
description: (Optional) We encourage community contributions!
|
||||
options:
|
||||
- label: Yes I'd like to help by submitting a PR!
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: ❓ Ask a Question
|
||||
url: https://github.com/roboflow/supervision/discussions/new?category=q-a
|
||||
about: Ask questions about using Supervision in GitHub Discussions
|
||||
- name: 📚 Documentation
|
||||
url: https://supervision.roboflow.com/
|
||||
about: Check the official Supervision documentation for guides and API references
|
||||
- name: 💬 GitHub Discussions
|
||||
url: https://github.com/roboflow/supervision/discussions
|
||||
about: Join community discussions, share ideas, and get help
|
||||
- name: 🌟 Show and Tell
|
||||
url: https://github.com/roboflow/supervision/discussions/categories/show-and-tell
|
||||
about: Share your projects and creations built with Supervision
|
||||
- name: 🗣️ Roboflow Discord
|
||||
url: https://discord.com/invite/GbfgXGJ8Bk
|
||||
about: Join our Discord to collaborate with other community members
|
||||
@@ -1,6 +1,6 @@
|
||||
name: 🤩 Feature Request
|
||||
description: Suggest a Supervision idea
|
||||
# title: " "
|
||||
title: "[Feature]: "
|
||||
description: Suggest a new feature or improvement
|
||||
labels: [enhancement]
|
||||
body:
|
||||
- type: markdown
|
||||
@@ -11,39 +11,40 @@ body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Search before asking
|
||||
description: >
|
||||
Please search the [issues](https://github.com/roboflow/supervision/issues) to see if a similar feature request already exists.
|
||||
description: Please search [issues](https://github.com/roboflow/supervision/issues) and [discussions](https://github.com/roboflow/supervision/discussions) first.
|
||||
options:
|
||||
- label: >
|
||||
I have searched the Supervision [issues](https://github.com/roboflow/supervision/issues) and found no similar feature requests.
|
||||
- label: I have searched the issues and discussions and found no similar feature request.
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: A short description of your feature.
|
||||
label: Feature Description
|
||||
description: What feature would you like and why?
|
||||
placeholder: |
|
||||
What new feature would you like to see in Supervision?
|
||||
**What:** Describe the feature...
|
||||
|
||||
**Why:** What problem does it solve? Who would benefit?
|
||||
|
||||
**How (optional):** Any ideas on implementation?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Use case
|
||||
description: |
|
||||
Describe the use case of your feature request. It will help us understand and prioritize the feature request.
|
||||
label: Example Usage
|
||||
description: (Optional) Show how you'd like to use this feature
|
||||
placeholder: |
|
||||
How would this feature be used, and who would use it?
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional
|
||||
description: Anything else you would like to share?
|
||||
# Your example usage here
|
||||
```
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Are you willing to submit a PR?
|
||||
description: >
|
||||
(Optional) We encourage you to submit a [Pull Request](https://github.com/roboflow/supervision/pulls) (PR) to help improve Supervision for everyone, especially if you have a good understanding of how to implement a fix or feature.
|
||||
description: (Optional) We encourage community contributions!
|
||||
options:
|
||||
- label: Yes I'd like to help by submitting a PR!
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
name: ❓ Question
|
||||
description: Ask a Supervision question
|
||||
# title: " "
|
||||
labels: [question]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for asking a Supervision ❓ Question!
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Search before asking
|
||||
description: >
|
||||
Please search the [issues](https://github.com/roboflow/supervision/issues) to see if a similar feature request already exists.
|
||||
options:
|
||||
- label: >
|
||||
I have searched the Supervision [issues](https://github.com/roboflow/supervision/issues) and found no similar feature requests.
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Question
|
||||
description: What is your question?
|
||||
placeholder: |
|
||||
💡 ProTip! Include as much information as possible (screenshots, logs, tracebacks etc.) to receive the most helpful response.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional
|
||||
description: Anything else you would like to share?
|
||||
@@ -0,0 +1,67 @@
|
||||
<details>
|
||||
<summary>Before submitting</summary>
|
||||
|
||||
- [ ] Self-reviewed the code
|
||||
- [ ] Updated documentation, follow [Google-style](https://google.github.io/styleguide/pyguide.html#383-functions-and-methods)
|
||||
- [ ] Added docs entry for autogeneration (if new functions/classes)
|
||||
- [ ] Added/updated tests
|
||||
- [ ] All tests pass locally
|
||||
|
||||
</details>
|
||||
|
||||
## Description
|
||||
|
||||
<!-- Provide a clear and concise description of your changes -->
|
||||
|
||||
## Type of Change
|
||||
|
||||
<!-- Mark the relevant option with an "x" and delete the others -->
|
||||
|
||||
- 🐛 Bug fix (non-breaking change which fixes an issue)
|
||||
- ✨ New feature (non-breaking change which adds functionality)
|
||||
- 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- 📝 Documentation update
|
||||
- 🧪 Test update
|
||||
- 🔨 Refactoring (no functional changes)
|
||||
- ⚡ Performance improvement
|
||||
- 🔧 Chore (dependencies, configs, etc.)
|
||||
|
||||
## Motivation and Context
|
||||
|
||||
<!-- Why is this change required? What problem does it solve? -->
|
||||
|
||||
<!-- If it fixes an open issue, please link to the issue here -->
|
||||
|
||||
Closes #(issue)
|
||||
|
||||
## Changes Made
|
||||
|
||||
<!-- List the main changes made in this PR -->
|
||||
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
## Testing
|
||||
|
||||
<!-- Describe the tests you ran and how to reproduce them -->
|
||||
|
||||
- [ ] I have tested this code locally
|
||||
- [ ] I have added unit tests that prove my fix is effective or that my feature works
|
||||
- [ ] All new and existing tests pass
|
||||
|
||||
## Google Colab (optional)
|
||||
|
||||
<!-- If applicable, provide a link to a Google Colab notebook demonstrating the feature/fix -->
|
||||
|
||||
<!-- Ensure the notebook is publicly accessible -->
|
||||
|
||||
Colab link:
|
||||
|
||||
## Screenshots/Videos (optional)
|
||||
|
||||
<!-- If applicable, add screenshots or videos to demonstrate the changes -->
|
||||
|
||||
## Additional Notes
|
||||
|
||||
<!-- Any additional information that reviewers should know -->
|
||||
@@ -0,0 +1,146 @@
|
||||
# GitHub Copilot Instructions for Supervision
|
||||
|
||||
This file provides context-aware guidance for GitHub Copilot when working in the Supervision repository.
|
||||
|
||||
---
|
||||
|
||||
## 📚 Repository Overview
|
||||
|
||||
**Supervision** is a Python library providing reusable computer vision utilities for working with object detection models (YOLO, SAM, etc.). It offers tools for detections processing, tracking, annotation, and dataset management.
|
||||
|
||||
- **Languages**: Python 3.9+
|
||||
- **Key Dependencies**: NumPy, OpenCV, SciPy
|
||||
- **License**: MIT
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Project Structure
|
||||
|
||||
```
|
||||
supervision/
|
||||
├── src/
|
||||
│ └── supervision/ # Main library code
|
||||
│ ├── detection/ # Detection utilities
|
||||
│ ├── draw/ # Annotation and visualization
|
||||
│ ├── tracker/ # Object tracking
|
||||
│ ├── dataset/ # Dataset management
|
||||
│ └── utils/ # Shared utilities
|
||||
├── tests/ # Test suite (mirrors src/supervision/)
|
||||
├── docs/ # MkDocs documentation
|
||||
└── examples/ # Usage examples
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Development Commands
|
||||
|
||||
**Setup:**
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
uv sync --group dev --group docs --extra metrics
|
||||
|
||||
# Install pre-commit hooks
|
||||
uv run pre-commit install
|
||||
```
|
||||
|
||||
**Quality Checks:**
|
||||
|
||||
```bash
|
||||
# Run all pre-commit hooks (formatting, linting, type checking)
|
||||
uv run pre-commit run --all-files
|
||||
|
||||
# Run tests with coverage
|
||||
uv run pytest --cov=supervision
|
||||
```
|
||||
|
||||
**Documentation:**
|
||||
|
||||
```bash
|
||||
# Serve docs locally at http://127.0.0.1:8000
|
||||
uv run mkdocs serve
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💻 Code Conventions
|
||||
|
||||
### General Guidelines
|
||||
|
||||
- Follow **[AGENTS.md](../AGENTS.md)** for task-based development workflows
|
||||
- Reference **[CONTRIBUTING.md](CONTRIBUTING.md)** for detailed contribution guidelines
|
||||
- All code must pass `pre-commit` hooks before committing
|
||||
|
||||
### Code Style
|
||||
|
||||
- **Formatting**: Enforced by `ruff-format`, `prettier` (pre-commit)
|
||||
- **Linting**: Enforced by `ruff-check` (pre-commit)
|
||||
- **Type Hints**: Required on all new code
|
||||
- **Docstrings**: Required using [Google Python style](https://google.github.io/styleguide/pyguide.html#383-functions-and-methods)
|
||||
- Must include usage examples with primitive values
|
||||
- Serve as runnable documentation
|
||||
|
||||
### Performance
|
||||
|
||||
- Avoid unnecessary NumPy array copies
|
||||
- Prefer vectorized operations over Python loops
|
||||
- Use OpenCV operations efficiently
|
||||
|
||||
### API Design
|
||||
|
||||
- Follow existing naming patterns for consistency
|
||||
- Maintain backward compatibility unless explicitly breaking
|
||||
- Prefer functional utilities over complex classes
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Testing Requirements
|
||||
|
||||
All new features must include:
|
||||
|
||||
- Unit tests covering happy path and edge cases
|
||||
- Tests for `None`, empty inputs, large arrays, boundary conditions
|
||||
- Clear test names describing what they validate
|
||||
- Proper assertions (not just "no exception raised")
|
||||
|
||||
---
|
||||
|
||||
## 📝 Documentation Requirements
|
||||
|
||||
For new public functions/classes:
|
||||
|
||||
- Google-style docstrings with parameters, returns, exceptions
|
||||
- Usage examples in docstrings
|
||||
- Entry in appropriate `docs/*.md` file
|
||||
- Reference in `mkdocs.yml` navigation
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Pull Request Reviews
|
||||
|
||||
**When reviewing PRs, follow the comprehensive [PR Review Guidelines](CONTRIBUTING.md#pr-review-guidelines).**
|
||||
|
||||
Quick checklist:
|
||||
|
||||
- Tests included and passing
|
||||
- Docstrings follow Google style with examples
|
||||
- Pre-commit hooks pass
|
||||
- Breaking changes documented
|
||||
- Score code quality, testing, docs (n/5 scale)
|
||||
- Use inline comments + GitHub suggestion format
|
||||
|
||||
---
|
||||
|
||||
## 🌿 Branching & Commits
|
||||
|
||||
- Branch from `develop` using prefixes: `feat/`, `fix/`, `docs/`, `refactor/`, `perf/`, `test/`, `chore/`
|
||||
- Use **conventional commits**: `feat:`, `fix:`, `docs:`, `refactor:`, `perf:`, `test:`, `chore:`
|
||||
- All PRs target `develop` branch
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Context-Aware Behavior
|
||||
|
||||
**For general development tasks**: Follow [AGENTS.md](../AGENTS.md)
|
||||
**For pull request reviews**: Follow [PR Review Guidelines](CONTRIBUTING.md#pr-review-guidelines)
|
||||
**For detailed processes**: Consult [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
@@ -8,6 +8,10 @@ updates:
|
||||
commit-message:
|
||||
prefix: ⬆️
|
||||
target-branch: "develop"
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
# Python
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
verbose = "info"
|
||||
no_progress = true
|
||||
include_verbatim = true
|
||||
retry_wait_time = 10
|
||||
max_retries = 3
|
||||
max_concurrency = 8
|
||||
cache = false
|
||||
accept = [
|
||||
200, # OK
|
||||
408, # Request Timeout
|
||||
# 429 means the server received the request and is actively rate-limiting — the URL is
|
||||
# reachable. Real dead links return 404, 410, 5xx, or fail to connect; none of those
|
||||
# produce a 429, so accepting it here does not hide broken links.
|
||||
429, # Too Many Requests (rate-limited but reachable; does not mask dead links)
|
||||
]
|
||||
|
||||
exclude = [
|
||||
"https://github.com/YOUR_USERNAME/supervision.git", # hint for forking contributors
|
||||
"http://127.0.0.1:8000", # hint for local docs server
|
||||
"https://sam2.metademolab.com/", # returns 403 Forbidden
|
||||
"https://snyk.io/advisor/python/supervision/badge.svg", # badge URL
|
||||
"https://universe.roboflow.com/",
|
||||
"https://universe.roboflow.com/model-examples/segmented-animals-basic",
|
||||
# fixme: this page returns 401 Unauthorized when accessed and 404 Not Found when accessed with browser,
|
||||
# which is weird and should be investigated
|
||||
"https://huggingface.co/spaces/Roboflow/Annotators",
|
||||
]
|
||||
Executable
+70
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script to augment relative links in markdown files to GitHub URLs.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
from re import Match
|
||||
|
||||
|
||||
def get_repo_root() -> str:
|
||||
"""Get the repository root path."""
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
return os.path.dirname(os.path.dirname(script_dir))
|
||||
|
||||
|
||||
def augment_links_in_file(file_path: str, branch: str = "main") -> None:
|
||||
"""
|
||||
Augment relative links in a markdown file to GitHub URLs.
|
||||
|
||||
Args:
|
||||
file_path: Path to the markdown file.
|
||||
branch: Branch name, default "main".
|
||||
"""
|
||||
repo_root = get_repo_root()
|
||||
|
||||
if not file_path.endswith(".md"):
|
||||
return
|
||||
|
||||
with open(file_path) as f:
|
||||
content = f.read()
|
||||
|
||||
def replace_link(match: Match[str]) -> str:
|
||||
full_match = match.group(0)
|
||||
text = match.group(2)
|
||||
url = match.group(3)
|
||||
if not url.startswith("http"):
|
||||
# Resolve relative to an absolute path
|
||||
abs_path = os.path.normpath(os.path.join(os.path.dirname(file_path), url))
|
||||
if os.path.exists(abs_path):
|
||||
# Use 'tree' for directories and 'blob' for files
|
||||
ref = "tree" if os.path.isdir(abs_path) else "blob"
|
||||
rel_to_root = os.path.relpath(abs_path, repo_root)
|
||||
new_url = f"https://github.com/roboflow/supervision/{ref}/{branch}/{rel_to_root}"
|
||||
if full_match.startswith("!"):
|
||||
return f""
|
||||
else:
|
||||
return f"[{text}]({new_url})"
|
||||
return full_match
|
||||
|
||||
new_content = re.sub(r"(!?)\[([^\]]+)\]\(([^)]+)\)", replace_link, content)
|
||||
with open(file_path, "w") as f:
|
||||
f.write(new_content)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Augment relative links to GitHub URLs."
|
||||
)
|
||||
parser.add_argument("--branch", default="main", help="Branch name")
|
||||
parser.add_argument("files", nargs="+", help="Files to process")
|
||||
args = parser.parse_args()
|
||||
|
||||
for file in args.files:
|
||||
augment_links_in_file(file, args.branch)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,56 @@
|
||||
name: Build Package
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
python-version:
|
||||
required: false
|
||||
type: string
|
||||
default: "3.10"
|
||||
link-check:
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 📥 Checkout the repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: 🐍 Install uv and set Python version ${{ inputs.python-version }}
|
||||
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
|
||||
with:
|
||||
python-version: ${{ inputs.python-version }}
|
||||
activate-environment: true
|
||||
|
||||
- name: 🎨 Augment paths in README
|
||||
run: |
|
||||
python .github/scripts/augment_links.py README.md --branch ${{ github.head_ref || github.ref_name }}
|
||||
cat README.md
|
||||
|
||||
- name: 🔗 Link Checker
|
||||
if: inputs.link-check
|
||||
uses: lycheeverse/lychee-action@v2
|
||||
with:
|
||||
lycheeVersion: v0.22.0
|
||||
args: |
|
||||
--config .github/lychee.toml
|
||||
README.md
|
||||
fail: true
|
||||
|
||||
- name: 🏗️ Build source and wheel distributions
|
||||
run: |
|
||||
uv sync --frozen --group build
|
||||
uv build
|
||||
twine check --strict dist/*
|
||||
|
||||
- name: 📤 Upload distribution artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
@@ -0,0 +1,37 @@
|
||||
name: Docs/Test Workflow
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
|
||||
concurrency:
|
||||
group: docs-test-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
# Restrict permissions by default
|
||||
permissions:
|
||||
contents: read # Required for checkout
|
||||
checks: write # Required for test reporting
|
||||
|
||||
jobs:
|
||||
docs-build:
|
||||
name: Test docs build
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: 📥 Checkout the repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: 🐍 Install uv and set Python
|
||||
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
|
||||
with:
|
||||
python-version: "3.10"
|
||||
activate-environment: true
|
||||
|
||||
- name: 🏗️ Install dependencies
|
||||
run: uv sync --frozen --group docs
|
||||
|
||||
- name: 🧪 Test Docs Build
|
||||
run: mkdocs build --verbose
|
||||
@@ -0,0 +1,37 @@
|
||||
name: Check links & references
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
pull_request:
|
||||
schedule:
|
||||
# Run once a week on Sundays
|
||||
- cron: "0 9 * * 0"
|
||||
|
||||
concurrency:
|
||||
group: ci-check-links-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
links-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: 🔗 Link Checker
|
||||
uses: lycheeverse/lychee-action@v2
|
||||
with:
|
||||
lycheeVersion: v0.22.0
|
||||
args: |
|
||||
--config .github/lychee.toml
|
||||
'./**/*.md'
|
||||
# TODO: enable also following file types
|
||||
# './**/*.toml'
|
||||
# './**/*.yml'
|
||||
# './**/*.yaml'
|
||||
# './**/*.py'
|
||||
fail: true
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,90 @@
|
||||
name: Pytest/Test Workflow
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: pytest-test-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
build-pkg:
|
||||
name: Build this Package
|
||||
uses: ./.github/workflows/build-package.yml
|
||||
with:
|
||||
# only run link check on PRs from the same repo
|
||||
link-check: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
|
||||
|
||||
run-tests:
|
||||
name: Import Test and Pytest Run
|
||||
# needs: build # todo: consider using this build package for testing
|
||||
timeout-minutes: 10
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
|
||||
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: 📥 Checkout the repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: 🐍 Install uv and set Python version ${{ matrix.python-version }}
|
||||
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
activate-environment: true
|
||||
|
||||
- name: 🚀 Install Packages
|
||||
run: uv sync --frozen --group dev --group docs --extra metrics
|
||||
|
||||
- name: 📦 Run the Import test
|
||||
run: python -c "import supervision; from supervision import assets; from supervision import metrics; print(supervision.__version__)"
|
||||
|
||||
- name: 🧪 Run the Test
|
||||
run: pytest src/ tests/ --cov=supervision --cov-report=xml
|
||||
|
||||
- name: Generate Coverage Report
|
||||
run: |
|
||||
coverage xml
|
||||
coverage report
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v6
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: "coverage.xml"
|
||||
flags: cpu,${{ runner.os }},python${{ matrix.python-version }}
|
||||
env_vars: OS,PYTHON
|
||||
name: codecov-umbrella
|
||||
fail_ci_if_error: false
|
||||
|
||||
- name: Minimize uv cache
|
||||
run: uv cache prune --ci
|
||||
|
||||
testing-guardian:
|
||||
runs-on: ubuntu-latest
|
||||
needs: run-tests
|
||||
if: always()
|
||||
steps:
|
||||
- name: 📋 Display test result
|
||||
run: echo "${{ needs.run-tests.result }}"
|
||||
- name: ❌ Fail guardian on test failure
|
||||
if: needs.run-tests.result == 'failure'
|
||||
run: exit 1
|
||||
# Ensure that cancelled or skipped test runs still cause this guardian job to fail,
|
||||
# using an explicit exit code instead of relying on timeout behavior.
|
||||
- name: ⚠️ cancelled or skipped...
|
||||
if: contains(fromJSON('["cancelled", "skipped"]'), needs.run-tests.result)
|
||||
run: |
|
||||
echo "run-tests job result is '${{ needs.run-tests.result }}'; failing explicitly."
|
||||
exit 1
|
||||
- name: ✅ tests succeeded
|
||||
if: needs.run-tests.result == 'success'
|
||||
run: echo "All tests completed successfully in job 'run-tests'."
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Clear cache
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
console.log("Starting cache cleanup...")
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
name: Combine Dependabot PRs
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 1 * * 3" # Wednesday at 01:00
|
||||
workflow_dispatch: # allows you to manually trigger the workflow
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
checks: read
|
||||
|
||||
jobs:
|
||||
combine-prs:
|
||||
name: Combine
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: combine-prs
|
||||
id: combine-prs
|
||||
uses: github/combine-prs@2909f404763c3177a456e052bdb7f2e85d3a7cb3 # v5.2.0
|
||||
with:
|
||||
labels: combined-pr
|
||||
@@ -0,0 +1,26 @@
|
||||
name: PR Conflict Labeler
|
||||
|
||||
on:
|
||||
# So that PRs touching the same files as the push are updated
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
# So that the `dirtyLabel` is removed if conflicts are resolved
|
||||
# We recommend `pull_request_target` so that github secrets are available.
|
||||
# In `pull_request` we wouldn't be able to change labels of fork PRs
|
||||
pull_request_target:
|
||||
types: [synchronize]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: check if prs are dirty
|
||||
uses: eps1lon/actions-label-merge-conflict@v3
|
||||
with:
|
||||
dirtyLabel: "has conflicts"
|
||||
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Build and Publish Docs
|
||||
name: Docs/Build and Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -20,53 +20,41 @@ permissions:
|
||||
pull-requests: write # Required for PR comments
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
docs-build-deploy:
|
||||
name: Publish Docs
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.10"]
|
||||
steps:
|
||||
- name: 📥 Checkout the repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 🐍 Install uv and set Python ${{ matrix.python-version }}
|
||||
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6.3.1
|
||||
- name: 🐍 Install uv and set Python
|
||||
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
python-version: "3.10"
|
||||
activate-environment: true
|
||||
|
||||
|
||||
- name: 🔑 Create GitHub App token (mkdocs)
|
||||
id: mkdocs_token
|
||||
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
|
||||
with:
|
||||
app-id: ${{ secrets.MKDOCS_APP_ID }}
|
||||
private-key: ${{ secrets.MKDOCS_PEM }}
|
||||
owner: roboflow
|
||||
repositories: mkdocs-material-insiders
|
||||
|
||||
- name: 🏗️ Install dependencies
|
||||
run: |
|
||||
uv pip install -r pyproject.toml --group docs
|
||||
# Install mkdocs-material-insiders using the GitHub App token
|
||||
uv pip install "git+https://roboflow:${{ steps.mkdocs_token.outputs.token }}@github.com/roboflow/mkdocs-material-insiders.git@9.5.49-insiders-4.53.14#egg=mkdocs-material[imaging]"
|
||||
run: uv sync --frozen --group docs
|
||||
|
||||
- name: ⚙️ Configure git for github-actions
|
||||
run: |
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git config --global user.name "${{ github.actor }}"
|
||||
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
|
||||
|
||||
- name: 🚀 Deploy Development Docs
|
||||
if: (github.event_name == 'push' && github.ref == 'refs/heads/develop') || github.event_name == 'workflow_dispatch'
|
||||
env:
|
||||
MKDOCS_GIT_COMMITTERS_APIKEY: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
MKDOCS_GIT_COMMITTERS_APIKEY=${{ secrets.GITHUB_TOKEN }} uv run mike deploy --push develop
|
||||
mike deploy --push develop
|
||||
|
||||
- name: 🚀 Deploy Release Docs
|
||||
if: github.event_name == 'release' && github.event.action == 'published'
|
||||
env:
|
||||
MKDOCS_GIT_COMMITTERS_APIKEY: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
latest_tag=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
MKDOCS_GIT_COMMITTERS_APIKEY=${{ secrets.GITHUB_TOKEN }} uv run mike deploy --push --update-aliases $latest_tag latest
|
||||
mike deploy --push --update-aliases $latest_tag latest
|
||||
|
||||
@@ -7,12 +7,23 @@ on:
|
||||
- "[0-9]+.[0-9]+[0-9]+.[0-9]+b[0-9]"
|
||||
- "[0-9]+.[0-9]+[0-9]+.[0-9]+rc[0-9]"
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
paths:
|
||||
- ".github/workflows/build-package.yml"
|
||||
- ".github/workflows/publish-pre-release.yml"
|
||||
|
||||
permissions: {} # Explicitly remove all permissions by default
|
||||
|
||||
jobs:
|
||||
build:
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/build-package.yml
|
||||
|
||||
publish-pre-release:
|
||||
name: Publish Pre-release Package
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: test
|
||||
@@ -21,27 +32,17 @@ jobs:
|
||||
permissions:
|
||||
id-token: write # Required for PyPI publishing
|
||||
contents: read # Required for checkout
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.10"]
|
||||
steps:
|
||||
- name: 📥 Checkout the repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: 🐍 Install uv and set Python version ${{ matrix.python-version }}
|
||||
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6.3.1
|
||||
- name: 📥 Download distribution artifacts
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
activate-environment: true
|
||||
|
||||
|
||||
- name: 🏗️ Build source and wheel distributions
|
||||
run: |
|
||||
uv pip install -r pyproject.toml --group build
|
||||
uv build
|
||||
uv run twine check --strict dist/*
|
||||
name: dist
|
||||
path: dist/
|
||||
- name: List distribution artifacts
|
||||
run: ls -lh dist/
|
||||
|
||||
- name: 🚀 Publish to PyPi
|
||||
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
|
||||
with:
|
||||
attestations: true
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
name: Publish Supervision Releases to PyPI
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "[0-9]+.[0-9]+[0-9]+.[0-9]"
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
paths:
|
||||
- ".github/workflows/build-package.yml"
|
||||
- ".github/workflows/publish-release.yml"
|
||||
|
||||
permissions: {} # Explicitly remove all permissions by default
|
||||
|
||||
jobs:
|
||||
build:
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/build-package.yml
|
||||
|
||||
publish-release:
|
||||
name: Publish Release Package
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: release
|
||||
@@ -18,28 +28,26 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
id-token: write # Required for PyPI publishing
|
||||
contents: read # Required for checkout
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.10"]
|
||||
contents: write # Required for checkout and upload assets
|
||||
steps:
|
||||
- name: 📥 Checkout the repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: 🐍 Install uv and set Python version ${{ matrix.python-version }}
|
||||
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6.3.1
|
||||
- name: 📥 Download distribution artifacts
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
activate-environment: true
|
||||
name: dist
|
||||
path: dist/
|
||||
- name: List distribution artifacts
|
||||
run: ls -lh dist/
|
||||
|
||||
|
||||
- name: 🏗️ Build source and wheel distributions
|
||||
run: |
|
||||
uv pip install -r pyproject.toml --group build
|
||||
uv build
|
||||
uv run twine check --strict dist/*
|
||||
- name: 📦 Upload assets to Release
|
||||
if: github.event_name == 'release'
|
||||
uses: AButler/upload-release-assets@v3.0
|
||||
with:
|
||||
files: "dist/*"
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: 🚀 Publish to PyPi
|
||||
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
|
||||
# We only want to publish to PyPi if the event is a release and it's not a pre-release.
|
||||
if: (github.event_name == 'release' && github.event.release.prerelease != true) || github.event_name == 'workflow_dispatch'
|
||||
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
|
||||
with:
|
||||
attestations: true
|
||||
|
||||
@@ -2,12 +2,23 @@ name: Publish Supervision Releases to TestPyPI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
paths:
|
||||
- ".github/workflows/build-package.yml"
|
||||
- ".github/workflows/publish-testpypi.yml"
|
||||
|
||||
permissions: {} # Explicitly remove all permissions by default
|
||||
|
||||
jobs:
|
||||
build:
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/build-package.yml
|
||||
|
||||
publish-testpypi:
|
||||
name: Publish Release Package
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: release
|
||||
@@ -16,28 +27,18 @@ jobs:
|
||||
permissions:
|
||||
id-token: write # Required for PyPI publishing
|
||||
contents: read # Required for checkout
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.10"]
|
||||
steps:
|
||||
- name: 📥 Checkout the repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: 🐍 Install uv and set Python version ${{ matrix.python-version }}
|
||||
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6.3.1
|
||||
- name: 📥 Download distribution artifacts
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
activate-environment: true
|
||||
|
||||
|
||||
- name: 🏗️ Build source and wheel distributions
|
||||
run: |
|
||||
uv pip install -r pyproject.toml --group build
|
||||
uv build
|
||||
uv run twine check --strict dist/*
|
||||
name: dist
|
||||
path: dist/
|
||||
- name: List distribution artifacts
|
||||
run: ls -lh dist/
|
||||
|
||||
- name: 🚀 Publish to Test-PyPi
|
||||
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
|
||||
with:
|
||||
repository-url: https://test.pypi.org/legacy/
|
||||
attestations: true
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
name: 🧪 Docs Test WorkFlow 📚
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
|
||||
# Restrict permissions by default
|
||||
permissions:
|
||||
contents: read # Required for checkout
|
||||
checks: write # Required for test reporting
|
||||
|
||||
jobs:
|
||||
docs-build-test:
|
||||
name: Test docs build
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.10"]
|
||||
steps:
|
||||
- name: 📥 Checkout the repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 🐍 Install uv and set Python ${{ matrix.python-version }}
|
||||
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6.3.1
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
activate-environment: true
|
||||
|
||||
- name: 🏗️ Install dependencies
|
||||
run: |
|
||||
uv pip install -r pyproject.toml --group docs
|
||||
|
||||
- name: 🧪 Test Docs Build
|
||||
run: uv run mkdocs build --verbose
|
||||
@@ -1,35 +0,0 @@
|
||||
name: 🔧 Pytest/Test Workflow
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
|
||||
jobs:
|
||||
run-tests:
|
||||
name: Import Test and Pytest Run
|
||||
timeout-minutes: 10
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: 📥 Checkout the repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: 🐍 Install uv and set Python version ${{ matrix.python-version }}
|
||||
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6.3.1
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
activate-environment: true
|
||||
|
||||
|
||||
- name: 🚀 Install Packages
|
||||
run: uv pip install -r pyproject.toml --group dev --group docs --extra metrics
|
||||
|
||||
- name: 🧪 Run the Import test
|
||||
run: uv run python -c "import supervision; from supervision import assets; from supervision import metrics; print(supervision.__version__)"
|
||||
|
||||
- name: 🧪 Run the Test
|
||||
run: uv run pytest
|
||||
@@ -153,3 +153,10 @@ Desktop.ini
|
||||
*~
|
||||
.directory
|
||||
.Trash-*
|
||||
|
||||
# local data
|
||||
data/
|
||||
|
||||
# Claude working scratchpad (plans, lessons, ephemeral artefacts)
|
||||
tasks/
|
||||
*.local.md
|
||||
|
||||
+48
-33
@@ -1,3 +1,5 @@
|
||||
default_language_version:
|
||||
python: python3
|
||||
|
||||
ci:
|
||||
autofix_prs: true
|
||||
@@ -7,10 +9,9 @@ ci:
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
exclude: test/.*\.py
|
||||
- id: check-yaml
|
||||
exclude: mkdocs.yml
|
||||
- id: check-executables-have-shebangs
|
||||
@@ -20,44 +21,58 @@ repos:
|
||||
- id: detect-private-key
|
||||
- id: pretty-format-json
|
||||
exclude: demo.ipynb
|
||||
args: ['--autofix', '--no-sort-keys', '--indent=4']
|
||||
args: ["--autofix", "--no-sort-keys", "--indent=4"]
|
||||
- id: end-of-file-fixer
|
||||
- id: mixed-line-ending
|
||||
|
||||
- repo: https://github.com/PyCQA/bandit
|
||||
rev: '1.8.6'
|
||||
hooks:
|
||||
- id: bandit
|
||||
args: ["-c", "pyproject.toml"]
|
||||
additional_dependencies: ["bandit[toml]"]
|
||||
- repo: https://github.com/JoC0de/pre-commit-prettier
|
||||
rev: v3.8.2 # using tag; previously pinned SHA when tags were not persistent
|
||||
hooks:
|
||||
- id: prettier
|
||||
files: \.(ya?ml|toml)$
|
||||
# https://prettier.io/docs/en/options.html#print-width
|
||||
args: ["--print-width=120"]
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.12.3
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix, --exit-non-zero-on-fix]
|
||||
- id: ruff-format
|
||||
types_or: [ python, pyi, jupyter ]
|
||||
- repo: https://github.com/tox-dev/pyproject-fmt
|
||||
rev: v2.21.1
|
||||
hooks:
|
||||
- id: pyproject-fmt
|
||||
|
||||
# - repo: https://github.com/executablebooks/mdformat
|
||||
# rev: 0.7.17
|
||||
# hooks:
|
||||
# - id: mdformat
|
||||
# additional_dependencies:
|
||||
# - "mdformat-mkdocs[recommended]>=2.1.0"
|
||||
# args: ["--number"]
|
||||
- repo: https://github.com/abravalheri/validate-pyproject
|
||||
rev: v0.25
|
||||
hooks:
|
||||
- id: validate-pyproject
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.15.10
|
||||
hooks:
|
||||
- id: ruff-check
|
||||
args: [--fix]
|
||||
- id: ruff-format
|
||||
types_or: [python, pyi, jupyter]
|
||||
|
||||
- repo: https://github.com/executablebooks/mdformat
|
||||
rev: 1.0.0
|
||||
hooks:
|
||||
- id: mdformat
|
||||
additional_dependencies:
|
||||
- "mdformat-mkdocs[recommended]>=2.1.0"
|
||||
- "mdformat-ruff"
|
||||
args: ["--number"]
|
||||
exclude: ^(docs/changelog\.md|docs/deprecated\.md)$
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v1.20.1
|
||||
hooks:
|
||||
- id: mypy
|
||||
additional_dependencies:
|
||||
- "numpy>=2.0"
|
||||
- "types-PyYAML"
|
||||
- "types-requests"
|
||||
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.4.1
|
||||
rev: v2.4.2
|
||||
hooks:
|
||||
- id: codespell
|
||||
additional_dependencies:
|
||||
- tomli
|
||||
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.20.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: ["--py310-plus"]
|
||||
additional_dependencies:
|
||||
- tomli
|
||||
- "tomli>=2.0.1"
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
# Agent Guidelines for `supervision`
|
||||
|
||||
These instructions define how AI agents (GitHub Copilot, Claude, etc.) should behave when
|
||||
assigned an issue, task, or multi-step problem in this repository.
|
||||
|
||||
Behave like a senior contributor: precise, efficient, aligned with the project's
|
||||
philosophy, and focused on maintainability and clarity.
|
||||
|
||||
---
|
||||
|
||||
## 1. Before You Code
|
||||
|
||||
- Read the task/issue thoroughly before acting.
|
||||
- Identify missing information; ask **one targeted clarification question** if needed.
|
||||
- Outline a step-by-step plan before making changes.
|
||||
- Check whether the feature or fix already exists under a different name.
|
||||
- Confirm alignment with the repository's architecture (`src/supervision/`).
|
||||
|
||||
---
|
||||
|
||||
## 2. Repository Conventions
|
||||
|
||||
All work must follow the conventions of the `supervision` library
|
||||
(see [CONTRIBUTING.md](.github/CONTRIBUTING.md) for full details).
|
||||
|
||||
### Branching & Commits
|
||||
|
||||
- Branch from `develop` using prefixes: `feat/`, `fix/`, `docs/`, `refactor/`, `test/`, `chore/`.
|
||||
- Use **conventional commits**: `feat:`, `fix:`, `docs:`, `refactor:`, `perf:`, `test:`, `chore:`.
|
||||
- PRs must target the `develop` branch.
|
||||
|
||||
### Code Style
|
||||
|
||||
- **Formatting and linting** are enforced by **pre-commit**.
|
||||
The hook chain typically includes: ruff-check, ruff-format, codespell, mdformat,
|
||||
prettier, pyproject-fmt, and standard pre-commit-hooks (trailing whitespace, YAML, TOML, etc.).
|
||||
- **Type hints**: required on all new code. Type checking with mypy is encouraged but not
|
||||
currently enforced systematically by pre-commit; see [.github/CONTRIBUTING.md](.github/CONTRIBUTING.md)
|
||||
for the latest type-checking expectations.
|
||||
- **Docstrings**: Google Python docstring style. Required for all new functions and classes.
|
||||
Docstrings should include usage examples demonstrating the function with primitive values
|
||||
so they serve as runnable documentation.
|
||||
|
||||
### API Consistency
|
||||
|
||||
- Follow existing naming patterns.
|
||||
- Maintain backward compatibility unless explicitly allowed.
|
||||
- Prefer functional utilities over complex classes unless justified.
|
||||
|
||||
### Performance
|
||||
|
||||
- Avoid unnecessary copies of NumPy arrays.
|
||||
- Prefer vectorized operations over Python loops in hot paths.
|
||||
- Use OpenCV operations efficiently.
|
||||
|
||||
---
|
||||
|
||||
## 3. Implementing Features
|
||||
|
||||
- Provide a minimal, clean implementation.
|
||||
- Include type hints and Google-style docstrings with usage examples.
|
||||
- All new functionality must be covered with tests, including edge cases.
|
||||
- Add or update documentation (docstrings + mkdocs entries if applicable).
|
||||
- Ensure compatibility with core dependencies: NumPy, OpenCV, SciPy.
|
||||
|
||||
---
|
||||
|
||||
## 4. Fixing Bugs
|
||||
|
||||
1. Reproduce and understand the root cause.
|
||||
2. Write a test that reproduces the bug (it should fail before the fix).
|
||||
3. Apply a minimal, targeted fix.
|
||||
4. Verify the test passes and no other components break.
|
||||
|
||||
---
|
||||
|
||||
## 5. Refactoring
|
||||
|
||||
- Preserve behavior and API stability.
|
||||
- Improve readability or performance.
|
||||
- Reduce duplication.
|
||||
- Avoid large, sweeping refactors unless explicitly requested.
|
||||
|
||||
---
|
||||
|
||||
## 6. Before You Commit
|
||||
|
||||
Always run these before committing:
|
||||
|
||||
```bash
|
||||
uv run pytest --cov=supervision
|
||||
uv run pre-commit run --all-files
|
||||
```
|
||||
|
||||
- All pre-commit hooks must pass (formatting, linting, type checking, spell check, etc.).
|
||||
- All tests must pass before opening a PR. Note: some existing tests in the repo may
|
||||
already be failing — your changes must not introduce new failures.
|
||||
- Fix any issues reported and re-run until clean.
|
||||
@@ -0,0 +1,5 @@
|
||||
# Claude Code Project Instructions
|
||||
|
||||
<!-- Imports AGENTS.md, which contains agent roles, behavioral rules, and coding constraints for this project. -->
|
||||
|
||||
@AGENTS.md
|
||||
@@ -16,15 +16,16 @@
|
||||
|
||||
[](https://badge.fury.io/py/supervision)
|
||||
[](https://pypistats.org/packages/supervision)
|
||||
[](https://snyk.io/advisor/python/supervision)
|
||||
[](https://github.com/roboflow/supervision/blob/main/LICENSE.md)
|
||||
[](LICENSE.md)
|
||||
[](https://badge.fury.io/py/supervision)
|
||||
[](https://codecov.io/gh/roboflow/supervision)
|
||||
|
||||
[](https://snyk.io/advisor/python/supervision)
|
||||
[](https://colab.research.google.com/github/roboflow/supervision/blob/main/demo.ipynb)
|
||||
[](https://huggingface.co/spaces/Roboflow/Annotators)
|
||||
[](https://discord.gg/GbfgXGJ8Bk)
|
||||
[](https://squidfunk.github.io/mkdocs-material/)
|
||||
|
||||
<div align="center">
|
||||
<div align="center">
|
||||
<a href="https://trendshift.io/repositories/124" target="_blank"><img src="https://trendshift.io/api/badge/repositories/124" alt="roboflow%2Fsupervision | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
</div>
|
||||
|
||||
@@ -49,17 +50,18 @@ Read more about conda, mamba, and installing from source in our [guide](https://
|
||||
|
||||
### models
|
||||
|
||||
Supervision was designed to be model agnostic. Just plug in any classification, detection, or segmentation model. For your convenience, we have created [connectors](https://supervision.roboflow.com/latest/detection/core/#detections) for the most popular libraries like Ultralytics, Transformers, or MMDetection.
|
||||
Supervision was designed to be model agnostic. Just plug in any classification, detection, or segmentation model. For your convenience, we have created [connectors](https://supervision.roboflow.com/latest/detection/core/#detections) for the most popular libraries like Ultralytics, Transformers, MMDetection, or Inference. Other integrations, like `rfdetr`, already return `sv.Detections` directly.
|
||||
|
||||
Install the optional dependencies for this example with `pip install pillow rfdetr`.
|
||||
|
||||
```python
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
from PIL import Image
|
||||
from rfdetr import RFDETRSmall
|
||||
|
||||
image = cv2.imread(...)
|
||||
model = YOLO("yolov8s.pt")
|
||||
result = model(image)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
image = Image.open(...)
|
||||
model = RFDETRSmall()
|
||||
detections = model.predict(image, threshold=0.5)
|
||||
|
||||
len(detections)
|
||||
# 5
|
||||
@@ -70,21 +72,21 @@ len(detections)
|
||||
|
||||
- inference
|
||||
|
||||
Running with [Inference](https://github.com/roboflow/inference) requires a [Roboflow API KEY](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key).
|
||||
Running with [Inference](https://github.com/roboflow/inference) requires a [Roboflow API KEY](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key).
|
||||
|
||||
```python
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from inference import get_model
|
||||
```python
|
||||
import supervision as sv
|
||||
from PIL import Image
|
||||
from inference import get_model
|
||||
|
||||
image = cv2.imread(...)
|
||||
model = get_model(model_id="yolov8s-640", api_key=<ROBOFLOW API KEY>)
|
||||
result = model.infer(image)[0]
|
||||
detections = sv.Detections.from_inference(result)
|
||||
image = Image.open(...)
|
||||
model = get_model(model_id="rfdetr-small", api_key="ROBOFLOW_API_KEY")
|
||||
result = model.infer(image)[0]
|
||||
detections = sv.Detections.from_inference(result)
|
||||
|
||||
len(detections)
|
||||
# 5
|
||||
```
|
||||
len(detections)
|
||||
# 5
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
@@ -100,9 +102,7 @@ image = cv2.imread(...)
|
||||
detections = sv.Detections(...)
|
||||
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
annotated_frame = box_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections)
|
||||
annotated_frame = box_annotator.annotate(scene=image.copy(), detections=detections)
|
||||
```
|
||||
|
||||
https://github.com/roboflow/supervision/assets/26109316/691e219c-0565-4403-9218-ab5644f39bce
|
||||
@@ -115,8 +115,8 @@ Supervision provides a set of [utils](https://supervision.roboflow.com/latest/da
|
||||
import supervision as sv
|
||||
from roboflow import Roboflow
|
||||
|
||||
project = Roboflow().workspace(<WORKSPACE_ID>).project(<PROJECT_ID>)
|
||||
dataset = project.version(<PROJECT_VERSION>).download("coco")
|
||||
project = Roboflow().workspace("WORKSPACE_ID").project("PROJECT_ID")
|
||||
dataset = project.version("PROJECT_VERSION").download("coco")
|
||||
|
||||
ds = sv.DetectionDataset.from_coco(
|
||||
images_directory_path=f"{dataset.location}/train",
|
||||
@@ -124,10 +124,11 @@ ds = sv.DetectionDataset.from_coco(
|
||||
)
|
||||
|
||||
path, image, annotation = ds[0]
|
||||
# loads image on demand
|
||||
# loads image on demand
|
||||
|
||||
for path, image, annotation in ds:
|
||||
# loads image on demand
|
||||
pass
|
||||
```
|
||||
|
||||
<details close>
|
||||
@@ -135,94 +136,94 @@ for path, image, annotation in ds:
|
||||
|
||||
- load
|
||||
|
||||
```python
|
||||
dataset = sv.DetectionDataset.from_yolo(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...,
|
||||
data_yaml_path=...
|
||||
)
|
||||
```python
|
||||
dataset = sv.DetectionDataset.from_yolo(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...,
|
||||
data_yaml_path=...,
|
||||
)
|
||||
|
||||
dataset = sv.DetectionDataset.from_pascal_voc(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...
|
||||
)
|
||||
dataset = sv.DetectionDataset.from_pascal_voc(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...,
|
||||
)
|
||||
|
||||
dataset = sv.DetectionDataset.from_coco(
|
||||
images_directory_path=...,
|
||||
annotations_path=...
|
||||
)
|
||||
```
|
||||
dataset = sv.DetectionDataset.from_coco(
|
||||
images_directory_path=...,
|
||||
annotations_path=...,
|
||||
)
|
||||
```
|
||||
|
||||
- split
|
||||
|
||||
```python
|
||||
train_dataset, test_dataset = dataset.split(split_ratio=0.7)
|
||||
test_dataset, valid_dataset = test_dataset.split(split_ratio=0.5)
|
||||
```python
|
||||
train_dataset, test_dataset = dataset.split(split_ratio=0.7)
|
||||
test_dataset, valid_dataset = test_dataset.split(split_ratio=0.5)
|
||||
|
||||
len(train_dataset), len(test_dataset), len(valid_dataset)
|
||||
# (700, 150, 150)
|
||||
```
|
||||
len(train_dataset), len(test_dataset), len(valid_dataset)
|
||||
# (700, 150, 150)
|
||||
```
|
||||
|
||||
- merge
|
||||
|
||||
```python
|
||||
ds_1 = sv.DetectionDataset(...)
|
||||
len(ds_1)
|
||||
# 100
|
||||
ds_1.classes
|
||||
# ['dog', 'person']
|
||||
```python
|
||||
ds_1 = sv.DetectionDataset(...)
|
||||
len(ds_1)
|
||||
# 100
|
||||
ds_1.classes
|
||||
# ['dog', 'person']
|
||||
|
||||
ds_2 = sv.DetectionDataset(...)
|
||||
len(ds_2)
|
||||
# 200
|
||||
ds_2.classes
|
||||
# ['cat']
|
||||
ds_2 = sv.DetectionDataset(...)
|
||||
len(ds_2)
|
||||
# 200
|
||||
ds_2.classes
|
||||
# ['cat']
|
||||
|
||||
ds_merged = sv.DetectionDataset.merge([ds_1, ds_2])
|
||||
len(ds_merged)
|
||||
# 300
|
||||
ds_merged.classes
|
||||
# ['cat', 'dog', 'person']
|
||||
```
|
||||
ds_merged = sv.DetectionDataset.merge([ds_1, ds_2])
|
||||
len(ds_merged)
|
||||
# 300
|
||||
ds_merged.classes
|
||||
# ['cat', 'dog', 'person']
|
||||
```
|
||||
|
||||
- save
|
||||
|
||||
```python
|
||||
dataset.as_yolo(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...,
|
||||
data_yaml_path=...
|
||||
)
|
||||
```python
|
||||
dataset.as_yolo(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...,
|
||||
data_yaml_path=...,
|
||||
)
|
||||
|
||||
dataset.as_pascal_voc(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...
|
||||
)
|
||||
dataset.as_pascal_voc(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...,
|
||||
)
|
||||
|
||||
dataset.as_coco(
|
||||
images_directory_path=...,
|
||||
annotations_path=...
|
||||
)
|
||||
```
|
||||
dataset.as_coco(
|
||||
images_directory_path=...,
|
||||
annotations_path=...,
|
||||
)
|
||||
```
|
||||
|
||||
- convert
|
||||
|
||||
```python
|
||||
sv.DetectionDataset.from_yolo(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...,
|
||||
data_yaml_path=...
|
||||
).as_pascal_voc(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...
|
||||
)
|
||||
```
|
||||
```python
|
||||
sv.DetectionDataset.from_yolo(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...,
|
||||
data_yaml_path=...,
|
||||
).as_pascal_voc(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...,
|
||||
)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## 🎬 tutorials
|
||||
|
||||
Want to learn how to use Supervision? Explore our [how-to guides](https://supervision.roboflow.com/develop/how_to/detect_and_annotate/), [end-to-end examples](https://github.com/roboflow/supervision/tree/develop/examples), [cheatsheet](https://roboflow.github.io/cheatsheet-supervision/), and [cookbooks](https://supervision.roboflow.com/develop/cookbooks/)!
|
||||
Want to learn how to use Supervision? Explore our [how-to guides](https://supervision.roboflow.com/develop/how_to/detect_and_annotate/), [end-to-end examples](./examples), [cheatsheet](https://roboflow.github.io/cheatsheet-supervision/), and [cookbooks](https://supervision.roboflow.com/develop/cookbooks/)!
|
||||
|
||||
<br/>
|
||||
|
||||
@@ -256,7 +257,7 @@ Visit our [documentation](https://roboflow.github.io/supervision) page to learn
|
||||
|
||||
## 🏆 contribution
|
||||
|
||||
We love your input! Please see our [contributing guide](https://github.com/roboflow/supervision/blob/main/CONTRIBUTING.md) to get started. Thank you 🙏 to all our contributors!
|
||||
We love your input! Please see our [contributing guide](.github/CONTRIBUTING.md) to get started. Thank you 🙏 to all our contributors!
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/roboflow/supervision/graphs/contributors">
|
||||
|
||||
+7
-1
@@ -4,7 +4,7 @@ comments: true
|
||||
|
||||
# Assets
|
||||
|
||||
Supervision offers an assets download utility that allows you to download video files
|
||||
Supervision offers an assets download utility that allows you to download image and video files
|
||||
that you can use in your demos.
|
||||
|
||||
<div class="md-typeset">
|
||||
@@ -18,3 +18,9 @@ that you can use in your demos.
|
||||
</div>
|
||||
|
||||
:::supervision.assets.list.VideoAssets
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.assets.list.ImageAssets">ImageAssets</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.assets.list.ImageAssets
|
||||
|
||||
+193
-103
@@ -1,5 +1,95 @@
|
||||
# Changelog
|
||||
|
||||
### 0.28.0 <small>Unreleased</small>
|
||||
|
||||
- Changed [#2178](https://github.com/roboflow/supervision/pull/2178): [`sv.Detections.from_inference`](https://supervision.roboflow.com/latest/detection/core/#supervision.detection.core.Detections.from_inference) now supports compressed COCO RLE masks. Inference responses with `rle` or `rle_mask` fields containing a compressed counts string (as produced by `pycocotools`) are decoded directly into binary masks, avoiding a lossy polygon round-trip.
|
||||
|
||||
- Changed [#2178](https://github.com/roboflow/supervision/pull/2178): [`sv.rle_to_mask`](https://supervision.roboflow.com/latest/detection/utils/converters/#supervision.detection.utils.converters.rle_to_mask) and [`sv.mask_to_rle`](https://supervision.roboflow.com/latest/detection/utils/converters/#supervision.detection.utils.converters.mask_to_rle) moved to `supervision.detection.utils.converters`. The old import path `supervision.dataset.utils` continues to work but is deprecated.
|
||||
|
||||
- Fixed [#2178](https://github.com/roboflow/supervision/pull/2178): [`sv.rle_to_mask`](https://supervision.roboflow.com/latest/detection/utils/converters/#supervision.detection.utils.converters.rle_to_mask) now returns `NDArray[bool]` as declared in its signature. Previously the implementation returned `uint8` despite the `bool` annotation; code that relied on the undocumented `uint8` output (e.g. `mask * 255` producing `uint8`) should wrap the result with `.astype(np.uint8)`.
|
||||
|
||||
- Fixed [#2210](https://github.com/roboflow/supervision/pull/2210): [`sv.VideoInfo.fps`](https://supervision.roboflow.com/latest/utils/video/#supervision.utils.video.VideoInfo) now returns a `float` instead of a truncated `int`. Previously, frame rates like 23.976, 29.97, and 59.94 were silently truncated, causing frame-timing drift that accumulates over long videos. The type of `VideoInfo.fps` has changed from `int` to `float`; callers that pass `fps` to APIs requiring an integer (such as `deque(maxlen=...)` or `TraceAnnotator(trace_length=...)`) should wrap the value with `int()`.
|
||||
|
||||
### 0.27.0 <small>Nov 16, 2025</small>
|
||||
|
||||
- Added [#2008](https://github.com/roboflow/supervision/pull/2008): [`sv.filter_segments_by_distance`](https://supervision.roboflow.com/0.27.0/detection/utils/masks/#supervision.detection.utils.masks.filter_segments_by_distance) to keep the largest connected component and nearby components within an absolute or relative distance threshold. Useful for cleaning segmentation predictions from models such as SAM, SAM2, YOLO segmentation, and RF-DETR segmentation.
|
||||
|
||||
- Added [#2006](https://github.com/roboflow/supervision/pull/2006): [`sv.xyxy_to_mask`](https://supervision.roboflow.com/0.27.0/detection/utils/converters/#supervision.detection.utils.converters.xyxy_to_mask) to convert bounding boxes into 2D boolean masks, where each mask corresponds to a single box.
|
||||
|
||||
- Added [#1943](https://github.com/roboflow/supervision/pull/1943): [`sv.tint_image`](https://supervision.roboflow.com/0.27.0/utils/image/#supervision.utils.image.tint_image) to apply a solid color overlay to an image at a given opacity. Works with both NumPy and PIL inputs.
|
||||
|
||||
- Added [#1943](https://github.com/roboflow/supervision/pull/1943): [`sv.grayscale_image`](https://supervision.roboflow.com/0.27.0/utils/image/#supervision.utils.image.tint_image) to convert an image to 3 channel grayscale for compatibility with color based drawing utilities.
|
||||
|
||||
- Added [#2014](https://github.com/roboflow/supervision/pull/2014): [`sv.get_image_resolution_wh`](https://supervision.roboflow.com/0.27.0/utils/image/#supervision.utils.image.get_image_resolution_wh) as a unified way to read image width and height from NumPy and PIL inputs.
|
||||
|
||||
- Added [#1912](https://github.com/roboflow/supervision/pull/1912): [`sv.edit_distance`](https://supervision.roboflow.com/0.27.0/detection/utils/vlms/#supervision.detection.utils.vlms.edit_distance) for Levenshtein distance between two strings. Supports insert, delete, and substitute operations.
|
||||
|
||||
- Added [#1912](https://github.com/roboflow/supervision/pull/1912): [`sv.fuzzy_match_index`](https://supervision.roboflow.com/0.27.0/detection/utils/vlms/#supervision.detection.utils.vlms.fuzzy_match_index) to find the first close match in a list using edit distance.
|
||||
|
||||
- Changed [#2015](https://github.com/roboflow/supervision/pull/2015): [`sv.Detections.from_vlm`](https://supervision.roboflow.com/0.27.0/detection/core/#supervision.detection.core.Detections.from_vlm) and legacy `from_lmm` now support Qwen3 VL via `vlm=sv.VLM.QWEN_3_VL`.
|
||||
|
||||
- Changed [#1884](https://github.com/roboflow/supervision/pull/1884): [`sv.Detections.from_vlm`](https://supervision.roboflow.com/0.27.0/detection/core/#supervision.detection.core.Detections.from_vlm) and legacy `from_lmm` now support DeepSeek VL 2 via `vlm=sv.VLM.DEEPSEEK_VL_2`.
|
||||
|
||||
- Changed [#2015](https://github.com/roboflow/supervision/pull/2015): [`sv.Detections.from_vlm`](https://supervision.roboflow.com/0.27.0/detection/core/#supervision.detection.core.Detections.from_vlm) now parses Qwen 2.5 VL outputs more robustly and handles incomplete or truncated JSON responses.
|
||||
|
||||
- Changed [#2014](https://github.com/roboflow/supervision/pull/2014): [`sv.InferenceSlicer`](https://supervision.roboflow.com/0.27.0/detection/tools/inference_slicer/#supervision.detection.tools.inference_slicer.InferenceSlicer) now uses a new offset generation logic that removes redundant tiles and aligns borders cleanly. This reduces the number of processed tiles and shortens inference time without hurting detection quality.
|
||||
|
||||
- Changed [#2016](https://github.com/roboflow/supervision/pull/2016): [`sv.Detections`](https://supervision.roboflow.com/0.27.0/detection/core/#supervision.detection.core.Detections) now includes a `box_aspect_ratio` property for vectorized aspect ratio computation, useful for filtering detections based on box shape.
|
||||
|
||||
- Changed [#2001](https://github.com/roboflow/supervision/pull/2001): Significantly improved the performance of [`sv.box_iou_batch`](https://supervision.roboflow.com/0.27.0/detection/utils/iou_and_nms/#supervision.detection.utils.iou_and_nms.box_iou_batch). On internal benchmarks, processing runs approximately 2x to 5x faster.
|
||||
|
||||
- Changed [#1997](https://github.com/roboflow/supervision/pull/1997): [`sv.process_video`](https://supervision.roboflow.com/0.27.0/utils/video/#supervision.utils.video.process_video) now uses a threaded reader, processor, and writer pipeline. This removes I/O stalls and improves throughput while keeping the callback single threaded and safe for stateful models.
|
||||
|
||||
- Changed: [`sv.denormalize_boxes`](https://supervision.roboflow.com/0.27.0/detection/utils/boxes/#supervision.detection.utils.boxes.denormalize_boxes) now supports batch conversion of bounding boxes. The function accepts arrays of shape `(N, 4)` and returns a batch of absolute pixel coordinates.
|
||||
|
||||
- Changed [#1917](https://github.com/roboflow/supervision/pull/1917): [`sv.LabelAnnotator`](https://supervision.roboflow.com/0.27.0/detection/annotators/#supervision.annotators.core.LabelAnnotator) and [`sv.RichLabelAnnotator`](https://supervision.roboflow.com/0.27.0/detection/annotators/#supervision.annotators.core.RichLabelAnnotator) now accept `text_offset=(x, y)` to shift the label relative to `text_position`. Works with smart label position and line wrapping.
|
||||
|
||||
!!! failure "Removed"
|
||||
Removed the deprecated `overlap_ratio_wh` argument from `sv.InferenceSlicer`. Use the pixel based `overlap_wh` argument to control slice overlap.
|
||||
|
||||
!!! info "Tip"
|
||||
Convert your old ratio based overlap to pixel based overlap by multiplying each ratio by the slice dimensions.
|
||||
|
||||
```python
|
||||
# before
|
||||
|
||||
slice_wh = (640, 640)
|
||||
overlap_ratio_wh = (0.25, 0.25)
|
||||
|
||||
slicer = sv.InferenceSlicer(
|
||||
callback=callback,
|
||||
slice_wh=slice_wh,
|
||||
overlap_ratio_wh=overlap_ratio_wh,
|
||||
overlap_filter=sv.OverlapFilter.NON_MAX_SUPPRESSION,
|
||||
)
|
||||
|
||||
# after
|
||||
|
||||
overlap_wh = (
|
||||
int(overlap_ratio_wh[0] * slice_wh[0]),
|
||||
int(overlap_ratio_wh[1] * slice_wh[1]),
|
||||
)
|
||||
|
||||
slicer = sv.InferenceSlicer(
|
||||
callback=callback,
|
||||
slice_wh=slice_wh,
|
||||
overlap_wh=overlap_wh,
|
||||
overlap_filter=sv.OverlapFilter.NON_MAX_SUPPRESSION,
|
||||
)
|
||||
```
|
||||
|
||||
### 0.26.1 <small>Jul 22, 2025</small>
|
||||
|
||||
- Fixed [1894](https://github.com/roboflow/supervision/pull/1894): Error in [`sv.MeanAveragePrecision`](https://supervision.roboflow.com/0.26.1/metrics/mean_average_precision/#supervision.metrics.mean_average_precision.MeanAveragePrecision) where the area used for size-specific evaluation (small / medium / large) was always zero unless explicitly provided in `sv.Detections.data`.
|
||||
|
||||
- Fixed [1895](https://github.com/roboflow/supervision/pull/1895): `ID=0` bug in [`sv.MeanAveragePrecision`](https://supervision.roboflow.com/0.26.1/metrics/mean_average_precision/#supervision.metrics.mean_average_precision.MeanAveragePrecision) where objects were getting `0.0` mAP despite perfect IoU matches due to a bug in annotation ID assignment.
|
||||
|
||||
- Fixed [1898](https://github.com/roboflow/supervision/pull/1898): Issue where [`sv.MeanAveragePrecision`](https://supervision.roboflow.com/0.26.1/metrics/mean_average_precision/#supervision.metrics.mean_average_precision.MeanAveragePrecision) could return negative values when certain object size categories have no data.
|
||||
|
||||
- Fixed [1901](https://github.com/roboflow/supervision/pull/1901): `match_metric` support for [`sv.Detections.with_nms`](https://supervision.roboflow.com/0.26.1/metrics/mean_average_precision/#supervision.detection.core.Detections.with_nms).
|
||||
|
||||
- Fixed [1906](https://github.com/roboflow/supervision/pull/1906): `border_thickness` parameter usage for [`sv.PercentageBarAnnotator`](https://supervision.roboflow.com/0.26.1/metrics/mean_average_precision/#supervision.annotators.core.PercentageBarAnnotator).
|
||||
|
||||
### 0.26.0 <small>Jul 16, 2025</small>
|
||||
|
||||
!!! failure "Removed"
|
||||
@@ -118,7 +208,7 @@
|
||||
# [1908.01, 1346.67, 2585.99, 2024.11]])
|
||||
```
|
||||
|
||||
- Changed [#1709](https://github.com/roboflow/supervision/pull/1790): [`sv.Detections.from_vlm`](https://supervision.roboflow.com/0.26.0/detection/core/#supervision.detection.core.Detections.from_vlm) now supports parsing bounding boxes from responses generated by [Qwen-2.5 VL](https://github.com/QwenLM/Qwen2.5-VL).
|
||||
- Changed [#1709](https://github.com/roboflow/supervision/pull/1709): [`sv.Detections.from_vlm`](https://supervision.roboflow.com/0.26.0/detection/core/#supervision.detection.core.Detections.from_vlm) now supports parsing bounding boxes from responses generated by [Qwen-2.5 VL](https://github.com/QwenLM/Qwen2.5-VL).
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
@@ -153,7 +243,7 @@
|
||||
|
||||
- Changed [#1786](https://github.com/roboflow/supervision/pull/1786): Significantly improved the speed of HSV color mapping in [`sv.HeatMapAnnotator`](https://supervision.roboflow.com/0.26.0/detection/annotators/#supervision.annotators.core.HeatMapAnnotator), achieving approximately 28x faster performance on 1920x1080 frames.
|
||||
|
||||
- Fix [#1834](https://github.com/roboflow/supervision/pull/1834): Supervision’s [`sv.MeanAveragePrecision`](https://supervision.roboflow.com/0.26.0/metrics/mean_average_precision/#supervision.metrics.mean_average_precision.MeanAveragePrecision) is now fully aligned with [pycocotools](https://github.com/ppwwyyxx/cocoapi), the official COCO evaluation tool, ensuring accurate and standardized metrics. This update enabled us to launch a new version of the [Computer Vision Model Leaderboard](https://leaderboard.roboflow.com/).
|
||||
- Fixed [#1834](https://github.com/roboflow/supervision/pull/1834): Supervision’s [`sv.MeanAveragePrecision`](https://supervision.roboflow.com/0.26.0/metrics/mean_average_precision/#supervision.metrics.mean_average_precision.MeanAveragePrecision) is now fully aligned with [pycocotools](https://github.com/ppwwyyxx/cocoapi), the official COCO evaluation tool, ensuring accurate and standardized metrics. This update enabled us to launch a new version of the [Computer Vision Model Leaderboard](https://leaderboard.roboflow.com/).
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
@@ -173,7 +263,7 @@
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.629
|
||||
```
|
||||
|
||||
- Fix [#1767](https://github.com/roboflow/supervision/pull/1767): Fixed losing `sv.Detections.data` when detections filtering.
|
||||
- Fixed [#1767](https://github.com/roboflow/supervision/pull/1767): Fixed losing `sv.Detections.data` when detections filtering.
|
||||
|
||||
### 0.25.0 <small>Nov 12, 2024</small>
|
||||
|
||||
@@ -557,9 +647,9 @@ detections = sv.Detections.from_sam(sam_result=sam_result)
|
||||
|
||||
- Changed [#1434](https://github.com/roboflow/supervision/pull/1434): [`InferenceSlicer`](https://supervision.roboflow.com/0.23.0/detection/tools/inference_slicer/) now features an `overlap_wh` parameter, making it easier to compute slice sizes when handling overlapping slices.
|
||||
|
||||
- Fix [#1448](https://github.com/roboflow/supervision/pull/1448): Various annotator type issues have been resolved, supporting expanded error handling.
|
||||
- Fixed [#1448](https://github.com/roboflow/supervision/pull/1448): Various annotator type issues have been resolved, supporting expanded error handling.
|
||||
|
||||
- Fix [#1348](https://github.com/roboflow/supervision/pull/1348): Introduced a new method for [seeking to a specific video frame](https://supervision.roboflow.com/0.23.0/utils/video/#supervision.utils.video.get_video_frames_generator), addressing cases where traditional seek methods were failing. It can be enabled with `iterative_seek=True`.
|
||||
- Fixed [#1348](https://github.com/roboflow/supervision/pull/1348): Introduced a new method for [seeking to a specific video frame](https://supervision.roboflow.com/0.23.0/utils/video/#supervision.utils.video.get_video_frames_generator), addressing cases where traditional seek methods were failing. It can be enabled with `iterative_seek=True`.
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
@@ -572,7 +662,7 @@ for frame in sv.get_video_frames_generator(
|
||||
...
|
||||
```
|
||||
|
||||
- Fix [#1424](https://github.com/roboflow/supervision/pull/1424): `plot_image` function now clearly indicates that the size is in inches.
|
||||
- Fixed [#1424](https://github.com/roboflow/supervision/pull/1424): `plot_image` function now clearly indicates that the size is in inches.
|
||||
|
||||
!!! failure "Removed"
|
||||
|
||||
@@ -821,9 +911,9 @@ annotated_image = label_annotator.annotate(
|
||||
|
||||
### 0.20.0 <small>April 24, 2024</small>
|
||||
|
||||
- Added [#1128](https://github.com/roboflow/supervision/pull/1128): [`sv.KeyPoints`](/0.20.0/keypoint/core/#supervision.keypoint.core.KeyPoints) to provide initial support for pose estimation and broader keypoint detection models.
|
||||
- Added [#1128](https://github.com/roboflow/supervision/pull/1128): [`sv.KeyPoints`](https://supervision.roboflow.com/0.20.0/keypoint/core/#supervision.keypoint.core.KeyPoints) to provide initial support for pose estimation and broader keypoint detection models.
|
||||
|
||||
- Added [#1128](https://github.com/roboflow/supervision/pull/1128): [`sv.EdgeAnnotator`](/0.20.0/keypoint/annotators/#supervision.keypoint.annotators.EdgeAnnotator) and [`sv.VertexAnnotator`](/0.20.0/keypoint/annotators/#supervision.keypoint.annotators.VertexAnnotator) to enable rendering of results from keypoint detection models.
|
||||
- Added [#1128](https://github.com/roboflow/supervision/pull/1128): [`sv.EdgeAnnotator`](https://supervision.roboflow.com/0.20.0/keypoint/annotators/#supervision.keypoint.annotators.EdgeAnnotator) and [`sv.VertexAnnotator`](https://supervision.roboflow.com/0.20.0/keypoint/annotators/#supervision.keypoint.annotators.VertexAnnotator) to enable rendering of results from keypoint detection models.
|
||||
|
||||
```python
|
||||
import cv2
|
||||
@@ -840,17 +930,17 @@ edge_annotators = sv.EdgeAnnotator(color=sv.Color.GREEN, thickness=5)
|
||||
annotated_image = edge_annotators.annotate(image.copy(), keypoints)
|
||||
```
|
||||
|
||||
- Changed [#1037](https://github.com/roboflow/supervision/pull/1037): [`sv.LabelAnnotator`](/0.20.0/annotators/#supervision.annotators.core.LabelAnnotator) by adding an additional `corner_radius` argument that allows for rounding the corners of the bounding box.
|
||||
- Changed [#1037](https://github.com/roboflow/supervision/pull/1037): [`sv.LabelAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.LabelAnnotator) by adding an additional `corner_radius` argument that allows for rounding the corners of the bounding box.
|
||||
|
||||
- Changed [#1109](https://github.com/roboflow/supervision/pull/1109): [`sv.PolygonZone`](/0.20.0/detection/tools/polygon_zone/#supervision.detection.tools.polygon_zone.PolygonZone) such that the `frame_resolution_wh` argument is no longer required to initialize `sv.PolygonZone`.
|
||||
- Changed [#1109](https://github.com/roboflow/supervision/pull/1109): [`sv.PolygonZone`](https://supervision.roboflow.com/0.20.0/detection/tools/polygon_zone/#supervision.detection.tools.polygon_zone.PolygonZone) such that the `frame_resolution_wh` argument is no longer required to initialize `sv.PolygonZone`.
|
||||
|
||||
!!! failure "Deprecated"
|
||||
|
||||
The `frame_resolution_wh` parameter in `sv.PolygonZone` is deprecated and will be removed in `supervision-0.24.0`.
|
||||
|
||||
- Changed [#1084](https://github.com/roboflow/supervision/pull/1084): [`sv.get_polygon_center`](/0.20.0/utils/geometry/#supervision.geometry.core.utils.get_polygon_center) to calculate a more accurate polygon centroid.
|
||||
- Changed [#1084](https://github.com/roboflow/supervision/pull/1084): [`sv.get_polygon_center`](https://supervision.roboflow.com/0.20.0/utils/geometry/#supervision.geometry.core.utils.get_polygon_center) to calculate a more accurate polygon centroid.
|
||||
|
||||
- Changed [#1069](https://github.com/roboflow/supervision/pull/1069): [`sv.Detections.from_transformers`](/0.20.0/detection/core/#supervision.detection.core.Detections.from_transformers) by adding support for Transformers segmentation models and extract class names values.
|
||||
- Changed [#1069](https://github.com/roboflow/supervision/pull/1069): [`sv.Detections.from_transformers`](https://supervision.roboflow.com/0.20.0/detection/core/#supervision.detection.core.Detections.from_transformers) by adding support for Transformers segmentation models and extract class names values.
|
||||
|
||||
```python
|
||||
import torch
|
||||
@@ -882,11 +972,11 @@ annotated_image = label_annotator.annotate(
|
||||
scene=annotated_image, detections=detections)
|
||||
```
|
||||
|
||||
- Fixed [#787](https://github.com/roboflow/supervision/pull/787): [`sv.ByteTrack.update_with_detections`](/0.20.0/trackers/#supervision.tracker.byte_tracker.core.ByteTrack.update_with_detections) which was removing segmentation masks while tracking. Now, `ByteTrack` can be used alongside segmentation models.
|
||||
- Fixed [#787](https://github.com/roboflow/supervision/pull/787): [`sv.ByteTrack.update_with_detections`](https://supervision.roboflow.com/0.20.0/trackers/#supervision.tracker.byte_tracker.core.ByteTrack.update_with_detections) which was removing segmentation masks while tracking. Now, `ByteTrack` can be used alongside segmentation models.
|
||||
|
||||
### 0.19.0 <small>March 15, 2024</small>
|
||||
|
||||
- Added [#818](https://github.com/roboflow/supervision/pull/818): [`sv.CSVSink`](/0.19.0/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink) allowing for the straightforward saving of image, video, or stream inference results in a `.csv` file.
|
||||
- Added [#818](https://github.com/roboflow/supervision/pull/818): [`sv.CSVSink`](https://supervision.roboflow.com/0.19.0/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink) allowing for the straightforward saving of image, video, or stream inference results in a `.csv` file.
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
@@ -903,7 +993,7 @@ with csv_sink:
|
||||
csv_sink.append(detections, custom_data={<CUSTOM_LABEL>:<CUSTOM_DATA>})
|
||||
```
|
||||
|
||||
- Added [#819](https://github.com/roboflow/supervision/pull/819): [`sv.JSONSink`](/0.19.0/detection/tools/save_detections/#supervision.detection.tools.csv_sink.JSONSink) allowing for the straightforward saving of image, video, or stream inference results in a `.json` file.
|
||||
- Added [#819](https://github.com/roboflow/supervision/pull/819): [`sv.JSONSink`](https://supervision.roboflow.com/0.19.0/detection/tools/save_detections/#supervision.detection.tools.csv_sink.JSONSink) allowing for the straightforward saving of image, video, or stream inference results in a `.json` file.
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
@@ -920,11 +1010,11 @@ with json_sink:
|
||||
json_sink.append(detections, custom_data={<CUSTOM_LABEL>:<CUSTOM_DATA>})
|
||||
```
|
||||
|
||||
- Added [#847](https://github.com/roboflow/supervision/pull/847): [`sv.mask_iou_batch`](/0.19.0/detection/utils/#supervision.detection.utils.mask_iou_batch) allowing to compute Intersection over Union (IoU) of two sets of masks.
|
||||
- Added [#847](https://github.com/roboflow/supervision/pull/847): [`sv.mask_iou_batch`](https://supervision.roboflow.com/0.19.0/detection/utils/#supervision.detection.utils.mask_iou_batch) allowing to compute Intersection over Union (IoU) of two sets of masks.
|
||||
|
||||
- Added [#847](https://github.com/roboflow/supervision/pull/847): [`sv.mask_non_max_suppression`](/0.19.0/detection/utils/#supervision.detection.utils.mask_non_max_suppression) allowing to perform Non-Maximum Suppression (NMS) on segmentation predictions.
|
||||
- Added [#847](https://github.com/roboflow/supervision/pull/847): [`sv.mask_non_max_suppression`](https://supervision.roboflow.com/0.19.0/detection/utils/#supervision.detection.utils.mask_non_max_suppression) allowing to perform Non-Maximum Suppression (NMS) on segmentation predictions.
|
||||
|
||||
- Added [#888](https://github.com/roboflow/supervision/pull/888): [`sv.CropAnnotator`](/0.19.0/annotators/#supervision.annotators.core.CropAnnotator) allowing users to annotate the scene with scaled-up crops of detections.
|
||||
- Added [#888](https://github.com/roboflow/supervision/pull/888): [`sv.CropAnnotator`](https://supervision.roboflow.com/0.19.0/annotators/#supervision.annotators.core.CropAnnotator) allowing users to annotate the scene with scaled-up crops of detections.
|
||||
|
||||
```python
|
||||
import cv2
|
||||
@@ -944,17 +1034,17 @@ annotated_frame = crop_annotator.annotate(
|
||||
)
|
||||
```
|
||||
|
||||
- Changed [#827](https://github.com/roboflow/supervision/pull/827): [`sv.ByteTrack.reset`](/0.19.0/trackers/#supervision.tracker.ByteTrack.reset) allowing users to clear trackers state, enabling the processing of multiple video files in sequence.
|
||||
- Changed [#827](https://github.com/roboflow/supervision/pull/827): [`sv.ByteTrack.reset`](https://supervision.roboflow.com/0.19.0/trackers/#supervision.tracker.ByteTrack.reset) allowing users to clear trackers state, enabling the processing of multiple video files in sequence.
|
||||
|
||||
- Changed [#802](https://github.com/roboflow/supervision/pull/802): [`sv.LineZoneAnnotator`](/0.19.0/detection/tools/line_zone/#supervision.detection.line_zone.LineZone) allowing to hide in/out count using `display_in_count` and `display_out_count` properties.
|
||||
- Changed [#802](https://github.com/roboflow/supervision/pull/802): [`sv.LineZoneAnnotator`](https://supervision.roboflow.com/0.19.0/detection/tools/line_zone/#supervision.detection.line_zone.LineZone) allowing to hide in/out count using `display_in_count` and `display_out_count` properties.
|
||||
|
||||
- Changed [#787](https://github.com/roboflow/supervision/pull/787): [`sv.ByteTrack`](/0.19.0/trackers/#supervision.tracker.ByteTrack) input arguments and docstrings updated to improve readability and ease of use.
|
||||
- Changed [#787](https://github.com/roboflow/supervision/pull/787): [`sv.ByteTrack`](https://supervision.roboflow.com/0.19.0/trackers/#supervision.tracker.ByteTrack) input arguments and docstrings updated to improve readability and ease of use.
|
||||
|
||||
!!! failure "Deprecated"
|
||||
|
||||
The `track_buffer`, `track_thresh`, and `match_thresh` parameters in `sv.ByteTrack` are deprecated and will be removed in `supervision-0.23.0`. Use `lost_track_buffer,` `track_activation_threshold`, and `minimum_matching_threshold` instead.
|
||||
|
||||
- Changed [#910](https://github.com/roboflow/supervision/pull/910): [`sv.PolygonZone`](/0.19.0/detection/tools/polygon_zone/#supervision.detection.tools.polygon_zone.PolygonZone) to now accept a list of specific box anchors that must be in zone for a detection to be counted.
|
||||
- Changed [#910](https://github.com/roboflow/supervision/pull/910): [`sv.PolygonZone`](https://supervision.roboflow.com/0.19.0/detection/tools/polygon_zone/#supervision.detection.tools.polygon_zone.PolygonZone) to now accept a list of specific box anchors that must be in zone for a detection to be counted.
|
||||
|
||||
!!! failure "Deprecated"
|
||||
|
||||
@@ -962,11 +1052,11 @@ annotated_frame = crop_annotator.annotate(
|
||||
|
||||
- Changed [#875](https://github.com/roboflow/supervision/pull/875): annotators adding support for Pillow images. All supervision Annotators can now accept an image as either a numpy array or a Pillow Image. They automatically detect its type, draw annotations, and return the output in the same format as the input.
|
||||
|
||||
- Fixed [#944](https://github.com/roboflow/supervision/pull/944): [`sv.DetectionsSmoother`](/0.19.0/detection/tools/smoother/#supervision.detection.tools.smoother.DetectionsSmoother) removing `tracking_id` from `sv.Detections`.
|
||||
- Fixed [#944](https://github.com/roboflow/supervision/pull/944): [`sv.DetectionsSmoother`](https://supervision.roboflow.com/0.19.0/detection/tools/smoother/#supervision.detection.tools.smoother.DetectionsSmoother) removing `tracking_id` from `sv.Detections`.
|
||||
|
||||
### 0.18.0 <small>January 25, 2024</small>
|
||||
|
||||
- Added [#720](https://github.com/roboflow/supervision/pull/720): [`sv.PercentageBarAnnotator`](/0.18.0/annotators/#percentagebarannotator) allowing to annotate images and videos with percentage values representing confidence or other custom property.
|
||||
- Added [#720](https://github.com/roboflow/supervision/pull/720): [`sv.PercentageBarAnnotator`](https://supervision.roboflow.com/0.18.0/annotators/#percentagebarannotator) allowing to annotate images and videos with percentage values representing confidence or other custom property.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -981,9 +1071,9 @@ annotated_frame = crop_annotator.annotate(
|
||||
... )
|
||||
```
|
||||
|
||||
- Added [#702](https://github.com/roboflow/supervision/pull/702): [`sv.RoundBoxAnnotator`](/0.18.0/annotators/#roundboxannotator) allowing to annotate images and videos with rounded corners bounding boxes.
|
||||
- Added [#702](https://github.com/roboflow/supervision/pull/702): [`sv.RoundBoxAnnotator`](https://supervision.roboflow.com/0.18.0/annotators/#roundboxannotator) allowing to annotate images and videos with rounded corners bounding boxes.
|
||||
|
||||
- Added [#770](https://github.com/roboflow/supervision/pull/770): [`sv.OrientedBoxAnnotator`](/0.18.0/annotators/#orientedboxannotator) allowing to annotate images and videos with OBB (Oriented Bounding Boxes).
|
||||
- Added [#770](https://github.com/roboflow/supervision/pull/770): [`sv.OrientedBoxAnnotator`](https://supervision.roboflow.com/0.18.0/annotators/#orientedboxannotator) allowing to annotate images and videos with OBB (Oriented Bounding Boxes).
|
||||
|
||||
```python
|
||||
import cv2
|
||||
@@ -1003,9 +1093,9 @@ annotated_frame = oriented_box_annotator.annotate(
|
||||
)
|
||||
```
|
||||
|
||||
- Added [#696](https://github.com/roboflow/supervision/pull/696): [`sv.DetectionsSmoother`](/0.18.0/detection/tools/smoother/#detection-smoother) allowing for smoothing detections over multiple frames in video tracking.
|
||||
- Added [#696](https://github.com/roboflow/supervision/pull/696): [`sv.DetectionsSmoother`](https://supervision.roboflow.com/0.18.0/detection/tools/smoother/#detection-smoother) allowing for smoothing detections over multiple frames in video tracking.
|
||||
|
||||
- Added [#769](https://github.com/roboflow/supervision/pull/769): [`sv.ColorPalette.from_matplotlib`](/0.18.0/draw/color/#supervision.draw.color.ColorPalette.from_matplotlib) allowing users to create a `sv.ColorPalette` instance from a Matplotlib color palette.
|
||||
- Added [#769](https://github.com/roboflow/supervision/pull/769): [`sv.ColorPalette.from_matplotlib`](https://supervision.roboflow.com/0.18.0/draw/color/#supervision.draw.color.ColorPalette.from_matplotlib) allowing users to create a `sv.ColorPalette` instance from a Matplotlib color palette.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -1014,33 +1104,33 @@ annotated_frame = oriented_box_annotator.annotate(
|
||||
ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
|
||||
```
|
||||
|
||||
- Changed [#770](https://github.com/roboflow/supervision/pull/770): [`sv.Detections.from_ultralytics`](/0.18.0/detection/core/#supervision.detection.core.Detections.from_ultralytics) adding support for OBB (Oriented Bounding Boxes).
|
||||
- Changed [#770](https://github.com/roboflow/supervision/pull/770): [`sv.Detections.from_ultralytics`](https://supervision.roboflow.com/0.18.0/detection/core/#supervision.detection.core.Detections.from_ultralytics) adding support for OBB (Oriented Bounding Boxes).
|
||||
|
||||
- Changed [#735](https://github.com/roboflow/supervision/pull/735): [`sv.LineZone`](/0.18.0/detection/tools/line_zone/#linezone) to now accept a list of specific box anchors that must cross the line for a detection to be counted. This update marks a significant improvement from the previous requirement, where all four box corners were necessary. Users can now specify a single anchor, such as `sv.Position.BOTTOM_CENTER`, or any other combination of anchors defined as `List[sv.Position]`.
|
||||
- Changed [#735](https://github.com/roboflow/supervision/pull/735): [`sv.LineZone`](https://supervision.roboflow.com/0.18.0/detection/tools/line_zone/#linezone) to now accept a list of specific box anchors that must cross the line for a detection to be counted. This update marks a significant improvement from the previous requirement, where all four box corners were necessary. Users can now specify a single anchor, such as `sv.Position.BOTTOM_CENTER`, or any other combination of anchors defined as `List[sv.Position]`.
|
||||
|
||||
- Changed [#756](https://github.com/roboflow/supervision/pull/756): [`sv.Color`](/0.18.0/draw/color/#color)'s and [`sv.ColorPalette`](/0.18.0/draw/color/#colorpalette)'s method of accessing predefined colors, transitioning from a function-based approach (`sv.Color.red()`) to a more intuitive and conventional property-based method (`sv.Color.RED`).
|
||||
- Changed [#756](https://github.com/roboflow/supervision/pull/756): [`sv.Color`](https://supervision.roboflow.com/0.18.0/draw/color/#color)'s and [`sv.ColorPalette`](https://supervision.roboflow.com/0.18.0/draw/color/#colorpalette)'s method of accessing predefined colors, transitioning from a function-based approach (`sv.Color.red()`) to a more intuitive and conventional property-based method (`sv.Color.RED`).
|
||||
|
||||
!!! failure "Deprecated"
|
||||
|
||||
`sv.ColorPalette.default()` is deprecated and will be removed in `supervision-0.22.0`. Use `sv.ColorPalette.DEFAULT` instead.
|
||||
|
||||
- Changed [#769](https://github.com/roboflow/supervision/pull/769): [`sv.ColorPalette.DEFAULT`](/0.18.0/draw/color/#colorpalette) value, giving users a more extensive set of annotation colors.
|
||||
- Changed [#769](https://github.com/roboflow/supervision/pull/769): [`sv.ColorPalette.DEFAULT`](https://supervision.roboflow.com/0.18.0/draw/color/#colorpalette) value, giving users a more extensive set of annotation colors.
|
||||
|
||||
- Changed [#677](https://github.com/roboflow/supervision/pull/677): `sv.Detections.from_roboflow` to [`sv.Detections.from_inference`](/0.18.0/detection/core/#supervision.detection.core.Detections.from_inference) streamlining its functionality to be compatible with both the both [inference](https://github.com/roboflow/inference) pip package and the Robloflow [hosted API](https://docs.roboflow.com/deploy/hosted-api).
|
||||
- Changed [#677](https://github.com/roboflow/supervision/pull/677): `sv.Detections.from_roboflow` to [`sv.Detections.from_inference`](https://supervision.roboflow.com/0.18.0/detection/core/#supervision.detection.core.Detections.from_inference) streamlining its functionality to be compatible with both the both [inference](https://github.com/roboflow/inference) pip package and the Robloflow [hosted API](https://docs.roboflow.com/deploy/hosted-api).
|
||||
|
||||
!!! failure "Deprecated"
|
||||
|
||||
`Detections.from_roboflow()` is deprecated and will be removed in `supervision-0.22.0`. Use `Detections.from_inference` instead.
|
||||
|
||||
- Fixed [#735](https://github.com/roboflow/supervision/pull/735): [`sv.LineZone`](/0.18.0/detection/tools/line_zone/#linezone) functionality to accurately update the counter when an object crosses a line from any direction, including from the side. This enhancement enables more precise tracking and analytics, such as calculating individual in/out counts for each lane on the road.
|
||||
- Fixed [#735](https://github.com/roboflow/supervision/pull/735): [`sv.LineZone`](https://supervision.roboflow.com/0.18.0/detection/tools/line_zone/#linezone) functionality to accurately update the counter when an object crosses a line from any direction, including from the side. This enhancement enables more precise tracking and analytics, such as calculating individual in/out counts for each lane on the road.
|
||||
|
||||
### 0.17.0 <small>December 06, 2023</small>
|
||||
|
||||
- Added [#633](https://github.com/roboflow/supervision/pull/633): [`sv.PixelateAnnotator`](/0.17.0/annotators/#supervision.annotators.core.PixelateAnnotator) allowing to pixelate objects on images and videos.
|
||||
- Added [#633](https://github.com/roboflow/supervision/pull/633): [`sv.PixelateAnnotator`](https://supervision.roboflow.com/0.17.0/annotators/#supervision.annotators.core.PixelateAnnotator) allowing to pixelate objects on images and videos.
|
||||
|
||||
- Added [#652](https://github.com/roboflow/supervision/pull/652): [`sv.TriangleAnnotator`](/0.17.0/annotators/#supervision.annotators.core.TriangleAnnotator) allowing to annotate images and videos with triangle markers.
|
||||
- Added [#652](https://github.com/roboflow/supervision/pull/652): [`sv.TriangleAnnotator`](https://supervision.roboflow.com/0.17.0/annotators/#supervision.annotators.core.TriangleAnnotator) allowing to annotate images and videos with triangle markers.
|
||||
|
||||
- Added [#602](https://github.com/roboflow/supervision/pull/602): [`sv.PolygonAnnotator`](/0.17.0/annotators/#supervision.annotators.core.PolygonAnnotator) allowing to annotate images and videos with segmentation mask outline.
|
||||
- Added [#602](https://github.com/roboflow/supervision/pull/602): [`sv.PolygonAnnotator`](https://supervision.roboflow.com/0.17.0/annotators/#supervision.annotators.core.PolygonAnnotator) allowing to annotate images and videos with segmentation mask outline.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -1055,7 +1145,7 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
|
||||
... )
|
||||
```
|
||||
|
||||
- Added [#476](https://github.com/roboflow/supervision/pull/476): [`sv.assets`](/0.18.0/assets/) allowing download of video files that you can use in your demos.
|
||||
- Added [#476](https://github.com/roboflow/supervision/pull/476): [`sv.assets`](https://supervision.roboflow.com/0.18.0/assets/) allowing download of video files that you can use in your demos.
|
||||
|
||||
```python
|
||||
>>> from supervision.assets import download_assets, VideoAssets
|
||||
@@ -1063,35 +1153,35 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
|
||||
"vehicles.mp4"
|
||||
```
|
||||
|
||||
- Added [#605](https://github.com/roboflow/supervision/pull/605): [`Position.CENTER_OF_MASS`](/0.17.0/geometry/core/#position) allowing to place labels in center of mass of segmentation masks.
|
||||
- Added [#605](https://github.com/roboflow/supervision/pull/605): [`Position.CENTER_OF_MASS`](https://supervision.roboflow.com/0.17.0/geometry/core/#position) allowing to place labels in center of mass of segmentation masks.
|
||||
|
||||
- Added [#651](https://github.com/roboflow/supervision/pull/651): [`sv.scale_boxes`](/0.17.0/detection/utils/#supervision.detection.utils.scale_boxes) allowing to scale [`sv.Detections.xyxy`](/0.17.0/detection/core/#supervision.detection.core.Detections) values.
|
||||
- Added [#651](https://github.com/roboflow/supervision/pull/651): [`sv.scale_boxes`](https://supervision.roboflow.com/0.17.0/detection/utils/#supervision.detection.utils.scale_boxes) allowing to scale [`sv.Detections.xyxy`](https://supervision.roboflow.com/0.17.0/detection/core/#supervision.detection.core.Detections) values.
|
||||
|
||||
- Added [#637](https://github.com/roboflow/supervision/pull/637): [`sv.calculate_dynamic_text_scale`](/0.17.0/draw/utils/#supervision.draw.utils.calculate_dynamic_text_scale) and [`sv.calculate_dynamic_line_thickness`](/0.17.0/draw/utils/#supervision.draw.utils.calculate_dynamic_line_thickness) allowing text scale and line thickness to match image resolution.
|
||||
- Added [#637](https://github.com/roboflow/supervision/pull/637): [`sv.calculate_dynamic_text_scale`](https://supervision.roboflow.com/0.17.0/draw/utils/#supervision.draw.utils.calculate_dynamic_text_scale) and [`sv.calculate_dynamic_line_thickness`](https://supervision.roboflow.com/0.17.0/draw/utils/#supervision.draw.utils.calculate_dynamic_line_thickness) allowing text scale and line thickness to match image resolution.
|
||||
|
||||
- Added [#620](https://github.com/roboflow/supervision/pull/620): [`sv.Color.as_hex`](/0.17.0/draw/color/#supervision.draw.color.Color.as_hex) allowing to extract color value in HEX format.
|
||||
- Added [#620](https://github.com/roboflow/supervision/pull/620): [`sv.Color.as_hex`](https://supervision.roboflow.com/0.17.0/draw/color/#supervision.draw.color.Color.as_hex) allowing to extract color value in HEX format.
|
||||
|
||||
- Added [#572](https://github.com/roboflow/supervision/pull/572): [`sv.Classifications.from_timm`](/0.17.0/classification/core/#supervision.classification.core.Classifications.from_timm) allowing to load classification result from [timm](https://huggingface.co/docs/hub/timm) models.
|
||||
- Added [#572](https://github.com/roboflow/supervision/pull/572): [`sv.Classifications.from_timm`](https://supervision.roboflow.com/0.17.0/classification/core/#supervision.classification.core.Classifications.from_timm) allowing to load classification result from [timm](https://huggingface.co/docs/hub/timm) models.
|
||||
|
||||
- Added [#478](https://github.com/roboflow/supervision/pull/478): [`sv.Classifications.from_clip`](/0.17.0/classification/core/#supervision.classification.core.Classifications.from_clip) allowing to load classification result from [clip](https://github.com/openai/clip) model.
|
||||
- Added [#478](https://github.com/roboflow/supervision/pull/478): [`sv.Classifications.from_clip`](https://supervision.roboflow.com/0.17.0/classification/core/#supervision.classification.core.Classifications.from_clip) allowing to load classification result from [clip](https://github.com/openai/clip) model.
|
||||
|
||||
- Added [#571](https://github.com/roboflow/supervision/pull/571): [`sv.Detections.from_azure_analyze_image`](/0.17.0/detection/core/#supervision.detection.core.Detections.from_azure_analyze_image) allowing to load detection results from [Azure Image Analysis](https://learn.microsoft.com/en-us/azure/ai-services/computer-vision/concept-object-detection-40).
|
||||
- Added [#571](https://github.com/roboflow/supervision/pull/571): [`sv.Detections.from_azure_analyze_image`](https://supervision.roboflow.com/0.17.0/detection/core/#supervision.detection.core.Detections.from_azure_analyze_image) allowing to load detection results from [Azure Image Analysis](https://learn.microsoft.com/en-us/azure/ai-services/computer-vision/concept-object-detection-40).
|
||||
|
||||
- Changed [#646](https://github.com/roboflow/supervision/pull/646): `sv.BoxMaskAnnotator` renaming it to [`sv.ColorAnnotator`](/0.17.0/annotators/#supervision.annotators.core.ColorAnnotator).
|
||||
- Changed [#646](https://github.com/roboflow/supervision/pull/646): `sv.BoxMaskAnnotator` renaming it to [`sv.ColorAnnotator`](https://supervision.roboflow.com/0.17.0/annotators/#supervision.annotators.core.ColorAnnotator).
|
||||
|
||||
- Changed [#606](https://github.com/roboflow/supervision/pull/606): [`sv.MaskAnnotator`](/0.17.0/annotators/#supervision.annotators.core.MaskAnnotator) to make it **5x faster**.
|
||||
- Changed [#606](https://github.com/roboflow/supervision/pull/606): [`sv.MaskAnnotator`](https://supervision.roboflow.com/0.17.0/annotators/#supervision.annotators.core.MaskAnnotator) to make it **5x faster**.
|
||||
|
||||
- Fixed [#584](https://github.com/roboflow/supervision/pull/584): [`sv.DetectionDataset.from_yolo`](/0.17.0/datasets/#supervision.dataset.core.DetectionDataset.from_yolo) to ignore empty lines in annotation files.
|
||||
- Fixed [#584](https://github.com/roboflow/supervision/pull/584): [`sv.DetectionDataset.from_yolo`](https://supervision.roboflow.com/0.17.0/datasets/#supervision.dataset.core.DetectionDataset.from_yolo) to ignore empty lines in annotation files.
|
||||
|
||||
- Fixed [#555](https://github.com/roboflow/supervision/pull/555): [`sv.BlurAnnotator`](/0.17.0/annotators/#supervision.annotators.core.BlurAnnotator) to trim negative coordinates before bluring detections.
|
||||
- Fixed [#555](https://github.com/roboflow/supervision/pull/555): [`sv.BlurAnnotator`](https://supervision.roboflow.com/0.17.0/annotators/#supervision.annotators.core.BlurAnnotator) to trim negative coordinates before bluring detections.
|
||||
|
||||
- Fixed [#511](https://github.com/roboflow/supervision/pull/511): [`sv.TraceAnnotator`](/0.17.0/annotators/#supervision.annotators.core.TraceAnnotator) to respect trace position.
|
||||
- Fixed [#511](https://github.com/roboflow/supervision/pull/511): [`sv.TraceAnnotator`](https://supervision.roboflow.com/0.17.0/annotators/#supervision.annotators.core.TraceAnnotator) to respect trace position.
|
||||
|
||||
### 0.16.0 <small>October 19, 2023</small>
|
||||
|
||||
- Added [#422](https://github.com/roboflow/supervision/pull/422): [`sv.BoxMaskAnnotator`](/0.16.0/annotators/#supervision.annotators.core.BoxMaskAnnotator) allowing to annotate images and videos with mox masks.
|
||||
- Added [#422](https://github.com/roboflow/supervision/pull/422): [`sv.BoxMaskAnnotator`](https://supervision.roboflow.com/0.16.0/annotators/#supervision.annotators.core.BoxMaskAnnotator) allowing to annotate images and videos with mox masks.
|
||||
|
||||
- Added [#433](https://github.com/roboflow/supervision/pull/433): [`sv.HaloAnnotator`](/0.16.0/annotators/#supervision.annotators.core.HaloAnnotator) allowing to annotate images and videos with halo effect.
|
||||
- Added [#433](https://github.com/roboflow/supervision/pull/433): [`sv.HaloAnnotator`](https://supervision.roboflow.com/0.16.0/annotators/#supervision.annotators.core.HaloAnnotator) allowing to annotate images and videos with halo effect.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -1106,45 +1196,45 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
|
||||
... )
|
||||
```
|
||||
|
||||
- Added [#466](https://github.com/roboflow/supervision/pull/466): [`sv.HeatMapAnnotator`](/0.16.0/annotators/#supervision.annotators.core.HeatMapAnnotator) allowing to annotate videos with heat maps.
|
||||
- Added [#466](https://github.com/roboflow/supervision/pull/466): [`sv.HeatMapAnnotator`](https://supervision.roboflow.com/0.16.0/annotators/#supervision.annotators.core.HeatMapAnnotator) allowing to annotate videos with heat maps.
|
||||
|
||||
- Added [#492](https://github.com/roboflow/supervision/pull/492): [`sv.DotAnnotator`](/0.16.0/annotators/#supervision.annotators.core.DotAnnotator) allowing to annotate images and videos with dots.
|
||||
- Added [#492](https://github.com/roboflow/supervision/pull/492): [`sv.DotAnnotator`](https://supervision.roboflow.com/0.16.0/annotators/#supervision.annotators.core.DotAnnotator) allowing to annotate images and videos with dots.
|
||||
|
||||
- Added [#449](https://github.com/roboflow/supervision/pull/449): [`sv.draw_image`](/0.16.0/draw/utils/#supervision.draw.utils.draw_image) allowing to draw an image onto a given scene with specified opacity and dimensions.
|
||||
- Added [#449](https://github.com/roboflow/supervision/pull/449): [`sv.draw_image`](https://supervision.roboflow.com/0.16.0/draw/utils/#supervision.draw.utils.draw_image) allowing to draw an image onto a given scene with specified opacity and dimensions.
|
||||
|
||||
- Added [#280](https://github.com/roboflow/supervision/pull/280): [`sv.FPSMonitor`](/0.16.0/utils/video/#supervision.utils.video.FPSMonitor) for monitoring frames per second (FPS) to benchmark latency.
|
||||
- Added [#280](https://github.com/roboflow/supervision/pull/280): [`sv.FPSMonitor`](https://supervision.roboflow.com/0.16.0/utils/video/#supervision.utils.video.FPSMonitor) for monitoring frames per second (FPS) to benchmark latency.
|
||||
|
||||
- Added [#454](https://github.com/roboflow/supervision/pull/454): 🤗 Hugging Face Annotators [space](https://huggingface.co/spaces/Roboflow/Annotators).
|
||||
|
||||
- Changed [#482](https://github.com/roboflow/supervision/pull/482): [`sv.LineZone.trigger`](/0.16.0/detection/tools/line_zone/#supervision.detection.line_counter.LineZone.trigger) now return `Tuple[np.ndarray, np.ndarray]`. The first array indicates which detections have crossed the line from outside to inside. The second array indicates which detections have crossed the line from inside to outside.
|
||||
- Changed [#482](https://github.com/roboflow/supervision/pull/482): [`sv.LineZone.trigger`](https://supervision.roboflow.com/0.16.0/detection/tools/line_zone/#supervision.detection.line_counter.LineZone.trigger) now return `Tuple[np.ndarray, np.ndarray]`. The first array indicates which detections have crossed the line from outside to inside. The second array indicates which detections have crossed the line from inside to outside.
|
||||
|
||||
- Changed [#465](https://github.com/roboflow/supervision/pull/465): Annotator argument name from `color_map: str` to `color_lookup: ColorLookup` enum to increase type safety.
|
||||
|
||||
- Changed [#426](https://github.com/roboflow/supervision/pull/426): [`sv.MaskAnnotator`](/0.16.0/annotators/#supervision.annotators.core.MaskAnnotator) allowing 2x faster annotation.
|
||||
- Changed [#426](https://github.com/roboflow/supervision/pull/426): [`sv.MaskAnnotator`](https://supervision.roboflow.com/0.16.0/annotators/#supervision.annotators.core.MaskAnnotator) allowing 2x faster annotation.
|
||||
|
||||
- Fixed [#477](https://github.com/roboflow/supervision/pull/477): Poetry env definition allowing proper local installation.
|
||||
|
||||
- Fixed [#430](https://github.com/roboflow/supervision/pull/430): [`sv.ByteTrack`](/0.16.0/trackers/#supervision.tracker.byte_tracker.core.ByteTrack) to return `np.array([], dtype=int)` when `svDetections` is empty.
|
||||
- Fixed [#430](https://github.com/roboflow/supervision/pull/430): [`sv.ByteTrack`](https://supervision.roboflow.com/0.16.0/trackers/#supervision.tracker.byte_tracker.core.ByteTrack) to return `np.array([], dtype=int)` when `svDetections` is empty.
|
||||
|
||||
!!! failure "Deprecated"
|
||||
|
||||
`sv.Detections.from_yolov8` and `sv.Classifications.from_yolov8` as those are now replaced by [`sv.Detections.from_ultralytics`](/0.16.0/detection/core/#supervision.detection.core.Detections.from_ultralytics) and [`sv.Classifications.from_ultralytics`](/0.16.0/classification/core/#supervision.classification.core.Classifications.from_ultralytics).
|
||||
`sv.Detections.from_yolov8` and `sv.Classifications.from_yolov8` as those are now replaced by [`sv.Detections.from_ultralytics`](https://supervision.roboflow.com/0.16.0/detection/core/#supervision.detection.core.Detections.from_ultralytics) and [`sv.Classifications.from_ultralytics`](https://supervision.roboflow.com/0.16.0/classification/core/#supervision.classification.core.Classifications.from_ultralytics).
|
||||
|
||||
### 0.15.0 <small>October 5, 2023</small>
|
||||
|
||||
- Added [#170](https://github.com/roboflow/supervision/pull/170): [`sv.BoundingBoxAnnotator`](/0.15.0/annotators/#supervision.annotators.core.BoundingBoxAnnotator) allowing to annotate images and videos with bounding boxes.
|
||||
- Added [#170](https://github.com/roboflow/supervision/pull/170): [`sv.BoundingBoxAnnotator`](https://supervision.roboflow.com/0.15.0/annotators/#supervision.annotators.core.BoundingBoxAnnotator) allowing to annotate images and videos with bounding boxes.
|
||||
|
||||
- Added [#170](https://github.com/roboflow/supervision/pull/170): [`sv.BoxCornerAnnotator `](/0.15.0/annotators/#supervision.annotators.core.BoxCornerAnnotator) allowing to annotate images and videos with just bounding box corners.
|
||||
- Added [#170](https://github.com/roboflow/supervision/pull/170): [`sv.BoxCornerAnnotator `](https://supervision.roboflow.com/0.15.0/annotators/#supervision.annotators.core.BoxCornerAnnotator) allowing to annotate images and videos with just bounding box corners.
|
||||
|
||||
- Added [#170](https://github.com/roboflow/supervision/pull/170): [`sv.MaskAnnotator`](/0.15.0/annotators/#supervision.annotators.core.MaskAnnotator) allowing to annotate images and videos with segmentation masks.
|
||||
- Added [#170](https://github.com/roboflow/supervision/pull/170): [`sv.MaskAnnotator`](https://supervision.roboflow.com/0.15.0/annotators/#supervision.annotators.core.MaskAnnotator) allowing to annotate images and videos with segmentation masks.
|
||||
|
||||
- Added [#170](https://github.com/roboflow/supervision/pull/170): [`sv.EllipseAnnotator`](/0.15.0/annotators/#supervision.annotators.core.EllipseAnnotator) allowing to annotate images and videos with ellipses (sports game style).
|
||||
- Added [#170](https://github.com/roboflow/supervision/pull/170): [`sv.EllipseAnnotator`](https://supervision.roboflow.com/0.15.0/annotators/#supervision.annotators.core.EllipseAnnotator) allowing to annotate images and videos with ellipses (sports game style).
|
||||
|
||||
- Added [#386](https://github.com/roboflow/supervision/pull/386): [`sv.CircleAnnotator`](/0.15.0/annotators/#supervision.annotators.core.CircleAnnotator) allowing to annotate images and videos with circles.
|
||||
- Added [#386](https://github.com/roboflow/supervision/pull/386): [`sv.CircleAnnotator`](https://supervision.roboflow.com/0.15.0/annotators/#supervision.annotators.core.CircleAnnotator) allowing to annotate images and videos with circles.
|
||||
|
||||
- Added [#354](https://github.com/roboflow/supervision/pull/354): [`sv.TraceAnnotator`](/0.15.0/annotators/#supervision.annotators.core.TraceAnnotator) allowing to draw path of moving objects on videos.
|
||||
- Added [#354](https://github.com/roboflow/supervision/pull/354): [`sv.TraceAnnotator`](https://supervision.roboflow.com/0.15.0/annotators/#supervision.annotators.core.TraceAnnotator) allowing to draw path of moving objects on videos.
|
||||
|
||||
- Added [#405](https://github.com/roboflow/supervision/pull/405): [`sv.BlurAnnotator`](/0.15.0/annotators/#supervision.annotators.core.BlurAnnotator) allowing to blur objects on images and videos.
|
||||
- Added [#405](https://github.com/roboflow/supervision/pull/405): [`sv.BlurAnnotator`](https://supervision.roboflow.com/0.15.0/annotators/#supervision.annotators.core.BlurAnnotator) allowing to blur objects on images and videos.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -1161,17 +1251,17 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
|
||||
|
||||
- Added [#354](https://github.com/roboflow/supervision/pull/354): Supervision usage [example](https://github.com/roboflow/supervision/tree/develop/examples/traffic_analysis). You can now learn how to perform traffic flow analysis with Supervision.
|
||||
|
||||
- Changed [#399](https://github.com/roboflow/supervision/pull/399): [`sv.Detections.from_roboflow`](/0.15.0/detection/core/#supervision.detection.core.Detections.from_roboflow) now does not require `class_list` to be specified. The `class_id` value can be extracted directly from the [inference](https://github.com/roboflow/inference) response.
|
||||
- Changed [#399](https://github.com/roboflow/supervision/pull/399): [`sv.Detections.from_roboflow`](https://supervision.roboflow.com/0.15.0/detection/core/#supervision.detection.core.Detections.from_roboflow) now does not require `class_list` to be specified. The `class_id` value can be extracted directly from the [inference](https://github.com/roboflow/inference) response.
|
||||
|
||||
- Changed [#381](https://github.com/roboflow/supervision/pull/381): [`sv.VideoSink`](/0.15.0/utils/video/#videosink) now allows to customize the output codec.
|
||||
- Changed [#381](https://github.com/roboflow/supervision/pull/381): [`sv.VideoSink`](https://supervision.roboflow.com/0.15.0/utils/video/#videosink) now allows to customize the output codec.
|
||||
|
||||
- Changed [#361](https://github.com/roboflow/supervision/pull/361): [`sv.InferenceSlicer`](/0.15.0/detection/tools/inference_slicer/#supervision.detection.tools.inference_slicer.InferenceSlicer) can now operate in multithreading mode.
|
||||
- Changed [#361](https://github.com/roboflow/supervision/pull/361): [`sv.InferenceSlicer`](https://supervision.roboflow.com/0.15.0/detection/tools/inference_slicer/#supervision.detection.tools.inference_slicer.InferenceSlicer) can now operate in multithreading mode.
|
||||
|
||||
- Fixed [#348](https://github.com/roboflow/supervision/pull/348): [`sv.Detections.from_deepsparse`](/0.15.0/detection/core/#supervision.detection.core.Detections.from_deepsparse) to allow processing empty [deepsparse](https://github.com/neuralmagic/deepsparse) result object.
|
||||
- Fixed [#348](https://github.com/roboflow/supervision/pull/348): [`sv.Detections.from_deepsparse`](https://supervision.roboflow.com/0.15.0/detection/core/#supervision.detection.core.Detections.from_deepsparse) to allow processing empty [deepsparse](https://github.com/neuralmagic/deepsparse) result object.
|
||||
|
||||
### 0.14.0 <small>August 31, 2023</small>
|
||||
|
||||
- Added [#282](https://github.com/roboflow/supervision/pull/282): support for SAHI inference technique with [`sv.InferenceSlicer`](/0.14.0/detection/tools/inference_slicer).
|
||||
- Added [#282](https://github.com/roboflow/supervision/pull/282): support for SAHI inference technique with [`sv.InferenceSlicer`](https://supervision.roboflow.com/0.14.0/detection/tools/inference_slicer).
|
||||
|
||||
```python
|
||||
>>> import cv2
|
||||
@@ -1190,23 +1280,23 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
|
||||
>>> detections = slicer(image)
|
||||
```
|
||||
|
||||
- Added [#297](https://github.com/roboflow/supervision/pull/297): [`Detections.from_deepsparse`](/0.14.0/detection/core/#supervision.detection.core.Detections.from_deepsparse) to enable seamless integration with [DeepSparse](https://github.com/neuralmagic/deepsparse) framework.
|
||||
- Added [#297](https://github.com/roboflow/supervision/pull/297): [`Detections.from_deepsparse`](https://supervision.roboflow.com/0.14.0/detection/core/#supervision.detection.core.Detections.from_deepsparse) to enable seamless integration with [DeepSparse](https://github.com/neuralmagic/deepsparse) framework.
|
||||
|
||||
- Added [#281](https://github.com/roboflow/supervision/pull/281): [`sv.Classifications.from_ultralytics`](/0.14.0/classification/core/#supervision.classification.core.Classifications.from_ultralytics) to enable seamless integration with [Ultralytics](https://github.com/ultralytics/ultralytics) framework. This will enable you to use supervision with all [models](https://docs.ultralytics.com/models/) that Ultralytics supports.
|
||||
- Added [#281](https://github.com/roboflow/supervision/pull/281): [`sv.Classifications.from_ultralytics`](https://supervision.roboflow.com/0.14.0/classification/core/#supervision.classification.core.Classifications.from_ultralytics) to enable seamless integration with [Ultralytics](https://github.com/ultralytics/ultralytics) framework. This will enable you to use supervision with all [models](https://docs.ultralytics.com/models/) that Ultralytics supports.
|
||||
|
||||
!!! failure "Deprecated"
|
||||
|
||||
[sv.Detections.from_yolov8](/0.14.0/detection/core/#supervision.detection.core.Detections.from_yolov8) and [sv.Classifications.from_yolov8](/0.14.0/classification/core/#supervision.classification.core.Classifications.from_yolov8) are now deprecated and will be removed with `supervision-0.16.0` release.
|
||||
[sv.Detections.from_yolov8](https://supervision.roboflow.com/0.14.0/detection/core/#supervision.detection.core.Detections.from_yolov8) and [sv.Classifications.from_yolov8](https://supervision.roboflow.com/0.14.0/classification/core/#supervision.classification.core.Classifications.from_yolov8) are now deprecated and will be removed with `supervision-0.16.0` release.
|
||||
|
||||
- Added [#341](https://github.com/roboflow/supervision/pull/341): First supervision usage example script showing how to detect and track objects on video using YOLOv8 + Supervision.
|
||||
|
||||
- Changed [#296](https://github.com/roboflow/supervision/pull/296): [`sv.ClassificationDataset`](/0.14.0/dataset/core/#supervision.dataset.core.ClassificationDataset) and [`sv.DetectionDataset`](/0.14.0/dataset/core/#supervision.dataset.core.DetectionDataset) now use image path (not image name) as dataset keys.
|
||||
- Changed [#296](https://github.com/roboflow/supervision/pull/296): [`sv.ClassificationDataset`](https://supervision.roboflow.com/0.14.0/dataset/core/#supervision.dataset.core.ClassificationDataset) and [`sv.DetectionDataset`](https://supervision.roboflow.com/0.14.0/dataset/core/#supervision.dataset.core.DetectionDataset) now use image path (not image name) as dataset keys.
|
||||
|
||||
- Fixed [#300](https://github.com/roboflow/supervision/pull/300): [`Detections.from_roboflow`](/0.14.0/detection/core/#supervision.detection.core.Detections.from_roboflow) to filter out polygons with less than 3 points.
|
||||
- Fixed [#300](https://github.com/roboflow/supervision/pull/300): [`Detections.from_roboflow`](https://supervision.roboflow.com/0.14.0/detection/core/#supervision.detection.core.Detections.from_roboflow) to filter out polygons with less than 3 points.
|
||||
|
||||
### 0.13.0 <small>August 8, 2023</small>
|
||||
|
||||
- Added [#236](https://github.com/roboflow/supervision/pull/236): support for mean average precision (mAP) for object detection models with [`sv.MeanAveragePrecision`](/0.13.0/metrics/detection/#meanaverageprecision).
|
||||
- Added [#236](https://github.com/roboflow/supervision/pull/236): support for mean average precision (mAP) for object detection models with [`sv.MeanAveragePrecision`](https://supervision.roboflow.com/0.13.0/metrics/detection/#meanaverageprecision).
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -1228,17 +1318,17 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
|
||||
0.433
|
||||
```
|
||||
|
||||
- Added [#256](https://github.com/roboflow/supervision/pull/256): support for ByteTrack for object tracking with [`sv.ByteTrack`](/0.13.0/tracker/core/#bytetrack).
|
||||
- Added [#256](https://github.com/roboflow/supervision/pull/256): support for ByteTrack for object tracking with [`sv.ByteTrack`](https://supervision.roboflow.com/0.13.0/tracker/core/#bytetrack).
|
||||
|
||||
- Added [#222](https://github.com/roboflow/supervision/pull/222): [`sv.Detections.from_ultralytics`](/0.13.0/detection/core/#supervision.detection.core.Detections.from_ultralytics) to enable seamless integration with [Ultralytics](https://github.com/ultralytics/ultralytics) framework. This will enable you to use `supervision` with all [models](https://docs.ultralytics.com/models/) that Ultralytics supports.
|
||||
- Added [#222](https://github.com/roboflow/supervision/pull/222): [`sv.Detections.from_ultralytics`](https://supervision.roboflow.com/0.13.0/detection/core/#supervision.detection.core.Detections.from_ultralytics) to enable seamless integration with [Ultralytics](https://github.com/ultralytics/ultralytics) framework. This will enable you to use `supervision` with all [models](https://docs.ultralytics.com/models/) that Ultralytics supports.
|
||||
|
||||
!!! failure "Deprecated"
|
||||
|
||||
[`sv.Detections.from_yolov8`](/0.13.0/detection/core/#supervision.detection.core.Detections.from_yolov8) is now deprecated and will be removed with `supervision-0.15.0` release.
|
||||
[`sv.Detections.from_yolov8`](https://supervision.roboflow.com/0.13.0/detection/core/#supervision.detection.core.Detections.from_yolov8) is now deprecated and will be removed with `supervision-0.15.0` release.
|
||||
|
||||
- Added [#191](https://github.com/roboflow/supervision/pull/191): [`sv.Detections.from_paddledet`](/0.13.0/detection/core/#supervision.detection.core.Detections.from_paddledet) to enable seamless integration with [PaddleDetection](https://github.com/PaddlePaddle/PaddleDetection) framework.
|
||||
- Added [#191](https://github.com/roboflow/supervision/pull/191): [`sv.Detections.from_paddledet`](https://supervision.roboflow.com/0.13.0/detection/core/#supervision.detection.core.Detections.from_paddledet) to enable seamless integration with [PaddleDetection](https://github.com/PaddlePaddle/PaddleDetection) framework.
|
||||
|
||||
- Added [#245](https://github.com/roboflow/supervision/pull/245): support for loading PASCAL VOC segmentation datasets with [`sv.DetectionDataset.`](/0.13.0/dataset/core/#supervision.dataset.core.DetectionDataset.from_pascal_voc).
|
||||
- Added [#245](https://github.com/roboflow/supervision/pull/245): support for loading PASCAL VOC segmentation datasets with [`sv.DetectionDataset.`](https://supervision.roboflow.com/0.13.0/dataset/core/#supervision.dataset.core.DetectionDataset.from_pascal_voc).
|
||||
|
||||
### 0.12.0 <small>July 24, 2023</small>
|
||||
|
||||
@@ -1246,7 +1336,7 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
|
||||
|
||||
With the `supervision-0.12.0` release, we are terminating official support for Python 3.7.
|
||||
|
||||
- Added [#177](https://github.com/roboflow/supervision/pull/177): initial support for object detection model benchmarking with [`sv.ConfusionMatrix`](/0.12.0/metrics/detection/#confusionmatrix).
|
||||
- Added [#177](https://github.com/roboflow/supervision/pull/177): initial support for object detection model benchmarking with [`sv.ConfusionMatrix`](https://supervision.roboflow.com/0.12.0/metrics/detection/#confusionmatrix).
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -1273,23 +1363,23 @@ array([
|
||||
])
|
||||
```
|
||||
|
||||
- Added [#173](https://github.com/roboflow/supervision/pull/173): [`Detections.from_mmdetection`](/0.12.0/detection/core/#supervision.detection.core.Detections.from_mmdetection) to enable seamless integration with [MMDetection](https://github.com/open-mmlab/mmdetection) framework.
|
||||
- Added [#173](https://github.com/roboflow/supervision/pull/173): [`Detections.from_mmdetection`](https://supervision.roboflow.com/0.12.0/detection/core/#supervision.detection.core.Detections.from_mmdetection) to enable seamless integration with [MMDetection](https://github.com/open-mmlab/mmdetection) framework.
|
||||
|
||||
- Added [#130](https://github.com/roboflow/supervision/issues/130): ability to [install](https://supervision.roboflow.com/) package in `headless` or `desktop` mode.
|
||||
|
||||
- Changed [#180](https://github.com/roboflow/supervision/pull/180): packing method from `setup.py` to `pyproject.toml`.
|
||||
|
||||
- Fixed [#188](https://github.com/roboflow/supervision/issues/188): [`sv.DetectionDataset.from_cooc`](/0.12.0/dataset/core/#supervision.dataset.core.DetectionDataset.from_coco) can't be loaded when there are images without annotations.
|
||||
- Fixed [#188](https://github.com/roboflow/supervision/issues/188): [`sv.DetectionDataset.from_cooc`](https://supervision.roboflow.com/0.12.0/dataset/core/#supervision.dataset.core.DetectionDataset.from_coco) can't be loaded when there are images without annotations.
|
||||
|
||||
- Fixed [#226](https://github.com/roboflow/supervision/issues/226): [`sv.DetectionDataset.from_yolo`](/0.12.0/dataset/core/#supervision.dataset.core.DetectionDataset.from_yolo) can't load background instances.
|
||||
- Fixed [#226](https://github.com/roboflow/supervision/issues/226): [`sv.DetectionDataset.from_yolo`](https://supervision.roboflow.com/0.12.0/dataset/core/#supervision.dataset.core.DetectionDataset.from_yolo) can't load background instances.
|
||||
|
||||
### 0.11.1 <small>June 29, 2023</small>
|
||||
|
||||
- Fix [#165](https://github.com/roboflow/supervision/pull/165): [`as_folder_structure`](/0.11.1/dataset/core/#supervision.dataset.core.ClassificationDataset.as_folder_structure) fails to save [`sv.ClassificationDataset`](/0.11.1/dataset/core/#classificationdataset) when it is result of inference.
|
||||
- Fixed [#165](https://github.com/roboflow/supervision/pull/165): [`as_folder_structure`](https://supervision.roboflow.com/0.11.1/dataset/core/#supervision.dataset.core.ClassificationDataset.as_folder_structure) fails to save [`sv.ClassificationDataset`](https://supervision.roboflow.com/0.11.1/dataset/core/#classificationdataset) when it is result of inference.
|
||||
|
||||
### 0.11.0 <small>June 28, 2023</small>
|
||||
|
||||
- Added [#150](https://github.com/roboflow/supervision/pull/150): ability to load and save [`sv.DetectionDataset`](/0.11.0/dataset/core/#detectiondataset) in COCO format using [`as_coco`](/0.11.0/dataset/core/#supervision.dataset.core.DetectionDataset.as_coco) and [`from_coco`](/0.11.0/dataset/core/#supervision.dataset.core.DetectionDataset.from_coco) methods.
|
||||
- Added [#150](https://github.com/roboflow/supervision/pull/150): ability to load and save [`sv.DetectionDataset`](https://supervision.roboflow.com/0.11.0/dataset/core/#detectiondataset) in COCO format using [`as_coco`](https://supervision.roboflow.com/0.11.0/dataset/core/#supervision.dataset.core.DetectionDataset.as_coco) and [`from_coco`](https://supervision.roboflow.com/0.11.0/dataset/core/#supervision.dataset.core.DetectionDataset.from_coco) methods.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -1305,7 +1395,7 @@ array([
|
||||
... )
|
||||
```
|
||||
|
||||
- Added [#158](https://github.com/roboflow/supervision/pull/158): ability to merge multiple [`sv.DetectionDataset`](/0.11.0/dataset/core/#detectiondataset) together using [`merge`](/0.11.0/dataset/core/#supervision.dataset.core.DetectionDataset.merge) method.
|
||||
- Added [#158](https://github.com/roboflow/supervision/pull/158): ability to merge multiple [`sv.DetectionDataset`](https://supervision.roboflow.com/0.11.0/dataset/core/#detectiondataset) together using [`merge`](https://supervision.roboflow.com/0.11.0/dataset/core/#supervision.dataset.core.DetectionDataset.merge) method.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -1329,13 +1419,13 @@ array([
|
||||
['cat', 'dog', 'person']
|
||||
```
|
||||
|
||||
- Added [#162](https://github.com/roboflow/supervision/pull/162): additional `start` and `end` arguments to [`sv.get_video_frames_generator`](/0.11.0/utils/video/#get_video_frames_generator) allowing to generate frames only for a selected part of the video.
|
||||
- Added [#162](https://github.com/roboflow/supervision/pull/162): additional `start` and `end` arguments to [`sv.get_video_frames_generator`](https://supervision.roboflow.com/0.11.0/utils/video/#get_video_frames_generator) allowing to generate frames only for a selected part of the video.
|
||||
|
||||
- Fix [#157](https://github.com/roboflow/supervision/pull/157): incorrect loading of YOLO dataset class names from `data.yaml`.
|
||||
- Fixed [#157](https://github.com/roboflow/supervision/pull/157): incorrect loading of YOLO dataset class names from `data.yaml`.
|
||||
|
||||
### 0.10.0 <small>June 14, 2023</small>
|
||||
|
||||
- Added [#125](https://github.com/roboflow/supervision/pull/125): ability to load and save [`sv.ClassificationDataset`](/0.10.0/dataset/core/#classificationdataset) in a folder structure format.
|
||||
- Added [#125](https://github.com/roboflow/supervision/pull/125): ability to load and save [`sv.ClassificationDataset`](https://supervision.roboflow.com/0.10.0/dataset/core/#classificationdataset) in a folder structure format.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -1349,9 +1439,9 @@ array([
|
||||
... )
|
||||
```
|
||||
|
||||
- Added [#125](https://github.com/roboflow/supervision/pull/125): support for [`sv.ClassificationDataset.split`](/0.10.0/dataset/core/#supervision.dataset.core.ClassificationDataset.split) allowing to divide `sv.ClassificationDataset` into two parts.
|
||||
- Added [#125](https://github.com/roboflow/supervision/pull/125): support for [`sv.ClassificationDataset.split`](https://supervision.roboflow.com/0.10.0/dataset/core/#supervision.dataset.core.ClassificationDataset.split) allowing to divide `sv.ClassificationDataset` into two parts.
|
||||
|
||||
- Added [#110](https://github.com/roboflow/supervision/pull/110): ability to extract masks from Roboflow API results using [`sv.Detections.from_roboflow`](/0.10.0/detection/core/#supervision.detection.core.Detections.from_roboflow).
|
||||
- Added [#110](https://github.com/roboflow/supervision/pull/110): ability to extract masks from Roboflow API results using [`sv.Detections.from_roboflow`](https://supervision.roboflow.com/0.10.0/detection/core/#supervision.detection.core.Detections.from_roboflow).
|
||||
|
||||
- Added [commit hash](https://github.com/roboflow/supervision/commit/d000292eb2f2342544e0947b65528082e60fb8d6): Supervision Quickstart [notebook](https://colab.research.google.com/github/roboflow/supervision/blob/main/demo.ipynb) where you can learn more about Detection, Dataset and Video APIs.
|
||||
|
||||
@@ -1359,7 +1449,7 @@ array([
|
||||
|
||||
### 0.9.0 <small>June 7, 2023</small>
|
||||
|
||||
- Added [#118](https://github.com/roboflow/supervision/pull/118): ability to select [`sv.Detections`](/0.9.0/detection/core/#supervision.detection.core.Detections.__getitem__) by index, list of indexes or slice. Here is an example illustrating the new selection methods.
|
||||
- Added [#118](https://github.com/roboflow/supervision/pull/118): ability to select [`sv.Detections`](https://supervision.roboflow.com/0.9.0/detection/core/#supervision.detection.core.Detections.__getitem__) by index, list of indexes or slice. Here is an example illustrating the new selection methods.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -1373,11 +1463,11 @@ array([
|
||||
2
|
||||
```
|
||||
|
||||
- Added [#101](https://github.com/roboflow/supervision/pull/101): ability to extract masks from YOLOv8 result using [`sv.Detections.from_yolov8`](/0.8.0/detection/core/#supervision.detection.core.Detections.from_yolov8). Here is an example illustrating how to extract boolean masks from the result of the YOLOv8 model inference.
|
||||
- Added [#101](https://github.com/roboflow/supervision/pull/101): ability to extract masks from YOLOv8 result using [`sv.Detections.from_yolov8`](https://supervision.roboflow.com/0.8.0/detection/core/#supervision.detection.core.Detections.from_yolov8). Here is an example illustrating how to extract boolean masks from the result of the YOLOv8 model inference.
|
||||
|
||||
- Added [#122](https://github.com/roboflow/supervision/pull/122): ability to crop image using [`sv.crop`](/0.9.0/utils/image/#crop). Here is an example showing how to get a separate crop for each detection in `sv.Detections`.
|
||||
- Added [#122](https://github.com/roboflow/supervision/pull/122): ability to crop image using [`sv.crop`](https://supervision.roboflow.com/0.9.0/utils/image/#crop). Here is an example showing how to get a separate crop for each detection in `sv.Detections`.
|
||||
|
||||
- Added [#120](https://github.com/roboflow/supervision/pull/120): ability to conveniently save multiple images into directory using [`sv.ImageSink`](/0.9.0/utils/image/#imagesink). Here is an example showing how to save every tenth video frame as a separate image.
|
||||
- Added [#120](https://github.com/roboflow/supervision/pull/120): ability to conveniently save multiple images into directory using [`sv.ImageSink`](https://supervision.roboflow.com/0.9.0/utils/image/#imagesink). Here is an example showing how to save every tenth video frame as a separate image.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -1387,12 +1477,12 @@ array([
|
||||
... sink.save_image(image=image)
|
||||
```
|
||||
|
||||
- Fixed [#106](https://github.com/roboflow/supervision/issues/106): inconvenient handling of [`sv.PolygonZone`](/0.8.0/detection/tools/polygon_zone/#polygonzone) coordinates. Now `sv.PolygonZone` accepts coordinates in the form of `[[x1, y1], [x2, y2], ...]` that can be both integers and floats.
|
||||
- Fixed [#106](https://github.com/roboflow/supervision/issues/106): inconvenient handling of [`sv.PolygonZone`](https://supervision.roboflow.com/0.8.0/detection/tools/polygon_zone/#polygonzone) coordinates. Now `sv.PolygonZone` accepts coordinates in the form of `[[x1, y1], [x2, y2], ...]` that can be both integers and floats.
|
||||
|
||||
### 0.8.0 <small>May 17, 2023</small>
|
||||
|
||||
- Added [#100](https://github.com/roboflow/supervision/pull/100): support for dataset inheritance. The current `Dataset` got renamed to `DetectionDataset`. Now [`DetectionDataset`](/0.8.0/dataset/core/#detectiondataset) inherits from `BaseDataset`. This change was made to enforce the future consistency of APIs of different types of computer vision datasets.
|
||||
- Added [#100](https://github.com/roboflow/supervision/pull/100): ability to save datasets in YOLO format using [`DetectionDataset.as_yolo`](/0.8.0/dataset/core/#supervision.dataset.core.DetectionDataset.as_yolo).
|
||||
- Added [#100](https://github.com/roboflow/supervision/pull/100): support for dataset inheritance. The current `Dataset` got renamed to `DetectionDataset`. Now [`DetectionDataset`](https://supervision.roboflow.com/0.8.0/dataset/core/#detectiondataset) inherits from `BaseDataset`. This change was made to enforce the future consistency of APIs of different types of computer vision datasets.
|
||||
- Added [#100](https://github.com/roboflow/supervision/pull/100): ability to save datasets in YOLO format using [`DetectionDataset.as_yolo`](https://supervision.roboflow.com/0.8.0/dataset/core/#supervision.dataset.core.DetectionDataset.as_yolo).
|
||||
|
||||
```python
|
||||
>>> import roboflow
|
||||
@@ -1416,7 +1506,7 @@ array([
|
||||
['dog', 'person']
|
||||
```
|
||||
|
||||
- Added [#102](https://github.com/roboflow/supervision/pull/103): support for [`DetectionDataset.split`](/0.8.0/dataset/core/#supervision.dataset.core.DetectionDataset.split) allowing to divide `DetectionDataset` into two parts.
|
||||
- Added [#103](https://github.com/roboflow/supervision/pull/103): support for [`DetectionDataset.split`](https://supervision.roboflow.com/0.8.0/dataset/core/#supervision.dataset.core.DetectionDataset.split) allowing to divide `DetectionDataset` into two parts.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
|
||||
@@ -1,5 +1 @@
|
||||
# Code of conduct
|
||||
|
||||
```
|
||||
--8<-- "CODE_OF_CONDUCT.md"
|
||||
```
|
||||
--8<-- ".github/CODE_OF_CONDUCT.md"
|
||||
|
||||
@@ -1 +1 @@
|
||||
--8<-- "CONTRIBUTING.md"
|
||||
--8<-- ".github/CONTRIBUTING.md"
|
||||
|
||||
@@ -5,6 +5,7 @@ comments: true
|
||||
# Datasets
|
||||
|
||||
!!! warning
|
||||
|
||||
Dataset API is still fluid and may change. If you use Dataset API in your project until further notice, freeze the
|
||||
`supervision` version in your `requirements.txt` or `setup.py`.
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# Datasets Utils
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.dataset.utils.rle_to_mask">rle_to_mask</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.dataset.utils.rle_to_mask
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.dataset.utils.mask_to_rle">mask_to_rle</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.dataset.utils.mask_to_rle
|
||||
+2
-4
@@ -7,9 +7,7 @@ status: deprecated
|
||||
|
||||
These features are phased out due to better alternatives or potential issues in future versions. Deprecated functionalities are supported for **five subsequent releases**, providing time for users to transition to updated methods.
|
||||
|
||||
|
||||
- `overlap_filter_strategy` in [`InferenceSlicer.__init__`](https://supervision.roboflow.com/latest/detection/tools/inference_slicer/) is deprecated and will be removed in `supervision-0.27.0`. Use `overlap_strategy` instead.
|
||||
- `overlap_ratio_wh` in [`InferenceSlicer.__init__`](https://supervision.roboflow.com/latest/detection/tools/inference_slicer/) is deprecated and will be removed in `supervision-0.27.0`. Use `overlap_wh` instead.
|
||||
- `overlap_ratio_wh` in [`InferenceSlicer.__init__`](https://supervision.roboflow.com/latest/detection/tools/inference_slicer/) is deprecated and will be removed in `supervision-0.27.0`. Please set it to `None` and use `overlap_wh` instead.
|
||||
- `sv.LMM` enum is deprecated and will be removed in `supervision-0.31.0`. Use `sv.VLM` instead.
|
||||
- [`sv.Detections.from_lmm`](https://supervision.roboflow.com/0.26.0/detection/core/#supervision.detection.core.Detections.from_lmm) property is deprecated and will be removed in `supervision-0.31.0`. Use [`sv.Detections.from_vlm`](https://supervision.roboflow.com/0.26.0/detection/core/#supervision.detection.core.Detections.from_vlm) instead.
|
||||
|
||||
@@ -39,6 +37,6 @@ These features are phased out due to better alternatives or potential issues in
|
||||
- The method `sv.Color.red()` was removed as of `supervision-0.22.0`. Use the constant `sv.Color.RED` instead.
|
||||
- The method `sv.Color.green()` was removed as of `supervision-0.22.0`. Use the constant `sv.Color.GREEN` instead.
|
||||
- The method `sv.Color.blue()` was removed as of `supervision-0.22.0`. Use the constant `sv.Color.BLUE` instead.
|
||||
- The method `sv.ColorPalette.default()` was removed as of `supervision-0.22.0`. Use the constant [`ColorPalette.DEFAULT`](/utils/draw/#supervision.draw.color.ColorPalette.DEFAULT) instead.
|
||||
- The method `sv.ColorPalette.default()` was removed as of `supervision-0.22.0`. Use the constant [`ColorPalette.DEFAULT`](utils/draw.md/#supervision.draw.color.ColorPalette.DEFAULT) instead.
|
||||
- `sv.BoxAnnotator` was removed as of `supervision-0.22.0`, however `sv.BoundingBoxAnnotator` was immediately renamed to `sv.BoxAnnotator`. Use [`BoxAnnotator`](detection/annotators.md/#supervision.annotators.core.BoxAnnotator) and [`LabelAnnotator`](detection/annotators.md/#supervision.annotators.core.LabelAnnotator) instead of the old `sv.BoxAnnotator`.
|
||||
- The method `sv.FPSMonitor.__call__` was removed as of `supervision-0.22.0`. Use the attribute [`sv.FPSMonitor.fps`](utils/video.md/#supervision.utils.video.FPSMonitor.fps) instead.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Annotators
|
||||
@@ -18,7 +17,7 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
annotated_frame = box_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
@@ -39,7 +38,7 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
round_box_annotator = sv.RoundBoxAnnotator()
|
||||
annotated_frame = round_box_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
@@ -60,7 +59,7 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
corner_annotator = sv.BoxCornerAnnotator()
|
||||
annotated_frame = corner_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
@@ -81,7 +80,7 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
color_annotator = sv.ColorAnnotator()
|
||||
annotated_frame = color_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
@@ -102,7 +101,7 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
circle_annotator = sv.CircleAnnotator()
|
||||
annotated_frame = circle_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
@@ -123,7 +122,7 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
dot_annotator = sv.DotAnnotator()
|
||||
annotated_frame = dot_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
@@ -144,7 +143,7 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
triangle_annotator = sv.TriangleAnnotator()
|
||||
annotated_frame = triangle_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
@@ -165,7 +164,7 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
ellipse_annotator = sv.EllipseAnnotator()
|
||||
annotated_frame = ellipse_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
@@ -186,7 +185,7 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
halo_annotator = sv.HaloAnnotator()
|
||||
annotated_frame = halo_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
@@ -207,7 +206,7 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
percentage_bar_annotator = sv.PercentageBarAnnotator()
|
||||
annotated_frame = percentage_bar_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
@@ -228,14 +227,13 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
mask_annotator = sv.MaskAnnotator()
|
||||
annotated_frame = mask_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -250,14 +248,13 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
polygon_annotator = sv.PolygonAnnotator()
|
||||
annotated_frame = polygon_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -271,22 +268,21 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
|
||||
labels = [
|
||||
f"{class_name} {confidence:.2f}"
|
||||
for class_name, confidence
|
||||
in zip(detections['class_name'], detections.confidence)
|
||||
for class_name, confidence in zip(
|
||||
detections["class_name"],
|
||||
detections.confidence,
|
||||
)
|
||||
]
|
||||
|
||||
label_annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER)
|
||||
annotated_frame = label_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
labels=labels
|
||||
scene=image.copy(), detections=detections, labels=labels
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -300,25 +296,26 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
|
||||
labels = [
|
||||
f"{class_name} {confidence:.2f}"
|
||||
for class_name, confidence
|
||||
in zip(detections['class_name'], detections.confidence)
|
||||
for class_name, confidence in zip(
|
||||
detections["class_name"],
|
||||
detections.confidence,
|
||||
)
|
||||
]
|
||||
|
||||
rich_label_annotator = sv.RichLabelAnnotator(
|
||||
font_path="<TTF_FONT_PATH>",
|
||||
text_position=sv.Position.CENTER
|
||||
font_path="TTF_FONT_PATH",
|
||||
text_position=sv.Position.CENTER,
|
||||
)
|
||||
annotated_frame = rich_label_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
labels=labels
|
||||
labels=labels,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -330,46 +327,43 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
icon_paths = [
|
||||
"<ICON_PATH>"
|
||||
for _ in detections
|
||||
]
|
||||
icon_paths = ["<ICON_PATH>" for _ in detections]
|
||||
|
||||
icon_annotator = sv.IconAnnotator()
|
||||
annotated_frame = icon_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
icon_path=icon_paths
|
||||
icon_path=icon_paths,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
<!-- === "Crop"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
crop_annotator = sv.CropAnnotator()
|
||||
annotated_frame = crop_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
<div class="result" markdown>
|
||||
crop_annotator = sv.CropAnnotator()
|
||||
annotated_frame = crop_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
{ align=center width="800" }
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
-->
|
||||
|
||||
=== "Blur"
|
||||
@@ -381,16 +375,12 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
detections = sv.Detections(...)
|
||||
|
||||
blur_annotator = sv.BlurAnnotator()
|
||||
annotated_frame = blur_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
annotated_frame = (blur_annotator.annotate(scene=image.copy(), detections=detections),)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -405,14 +395,13 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
pixelate_annotator = sv.PixelateAnnotator()
|
||||
annotated_frame = pixelate_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -422,29 +411,29 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO('yolov8x.pt')
|
||||
model = YOLO("yolov8x.pt")
|
||||
|
||||
trace_annotator = sv.TraceAnnotator()
|
||||
|
||||
video_info = sv.VideoInfo.from_video_path(video_path='...')
|
||||
frames_generator = sv.get_video_frames_generator(source_path='...')
|
||||
video_info = sv.VideoInfo.from_video_path(video_path="...")
|
||||
frames_generator = sv.get_video_frames_generator(source_path="...")
|
||||
tracker = sv.ByteTrack()
|
||||
|
||||
with sv.VideoSink(target_path='...', video_info=video_info) as sink:
|
||||
with sv.VideoSink(target_path="...", video_info=video_info) as sink:
|
||||
for frame in frames_generator:
|
||||
result = model(frame)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
detections = tracker.update_with_detections(detections)
|
||||
annotated_frame = trace_annotator.annotate(
|
||||
scene=frame.copy(),
|
||||
detections=detections)
|
||||
detections=detections,
|
||||
)
|
||||
sink.write_frame(frame=annotated_frame)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -454,27 +443,27 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO('yolov8x.pt')
|
||||
model = YOLO("yolov8x.pt")
|
||||
|
||||
heat_map_annotator = sv.HeatMapAnnotator()
|
||||
|
||||
video_info = sv.VideoInfo.from_video_path(video_path='...')
|
||||
frames_generator = sv.get_video_frames_generator(source_path='...')
|
||||
video_info = sv.VideoInfo.from_video_path(video_path="...")
|
||||
frames_generator = sv.get_video_frames_generator(source_path="...")
|
||||
|
||||
with sv.VideoSink(target_path='...', video_info=video_info) as sink:
|
||||
with sv.VideoSink(target_path="...", video_info=video_info) as sink:
|
||||
for frame in frames_generator:
|
||||
result = model(frame)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
annotated_frame = heat_map_annotator.annotate(
|
||||
scene=frame.copy(),
|
||||
detections=detections)
|
||||
detections=detections,
|
||||
)
|
||||
sink.write_frame(frame=annotated_frame)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -489,7 +478,7 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
background_overlay_annotator = sv.BackgroundOverlayAnnotator()
|
||||
annotated_frame = background_overlay_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
@@ -512,14 +501,13 @@ Annotators accept detections and apply box or mask visualizations to the detecti
|
||||
annotated_frame = comparison_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections_1=detections_1,
|
||||
detections_2=detections_2
|
||||
detections_2=detections_2,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Detections
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Boxes Utils
|
||||
@@ -18,7 +17,7 @@ status: new
|
||||
:::supervision.detection.utils.boxes.scale_boxes
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.detection.boxes.utils.clip_boxes">clip_boxes</a></h2>
|
||||
<h2><a href="#supervision.detection.utils.boxes.clip_boxes">clip_boxes</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.boxes.clip_boxes
|
||||
|
||||
@@ -58,3 +58,21 @@ status: new
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.converters.polygon_to_xyxy
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.detection.utils.converters.xyxy_to_mask">xyxy_to_mask</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.converters.xyxy_to_mask
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.detection.utils.converters.rle_to_mask">rle_to_mask</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.converters.rle_to_mask
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.detection.utils.converters.mask_to_rle">mask_to_rle</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.converters.mask_to_rle
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# IoU and NMS Utils
|
||||
|
||||
@@ -22,3 +22,9 @@ status: new
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.masks.contains_multiple_segments
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.detection.utils.masks.filter_segments_by_distance">filter_segments_by_distance</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.masks.filter_segments_by_distance
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Polygons Utils
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# VLMs Utils
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.detection.utils.vlms.edit_distance">edit_distance</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.vlms.edit_distance
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.detection.utils.vlms.fuzzy_match_index">fuzzy_match_index</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.vlms.fuzzy_match_index
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||

|
||||
@@ -52,7 +51,7 @@ from roboflow import Roboflow
|
||||
|
||||
rf = Roboflow(api_key="<YOUR_API_KEY>")
|
||||
project = rf.workspace("<WORKSPACE_NAME>").project("<PROJECT_NAME>")
|
||||
dataset = project.version(<DATASET_VERSION_NUMBER>).download("<FORMAT>")
|
||||
dataset = project.version("<DATASET_VERSION_NUMBER>").download("<FORMAT>")
|
||||
```
|
||||
|
||||
If your dataset is from Universe, go to `Dataset` > `Download Dataset` > select the format (e.g. `YOLOv11`) > `Show download code`.
|
||||
@@ -149,7 +148,7 @@ We'll use `supervision` to create a dataset iterator, and then run the model on
|
||||
test_set = sv.DetectionDataset.from_yolo(
|
||||
images_directory_path=f"{dataset.location}/test/images",
|
||||
annotations_directory_path=f"{dataset.location}/test/labels",
|
||||
data_yaml_path=f"{dataset.location}/data.yaml"
|
||||
data_yaml_path=f"{dataset.location}/data.yaml",
|
||||
)
|
||||
|
||||
image_paths = []
|
||||
@@ -173,7 +172,7 @@ We'll use `supervision` to create a dataset iterator, and then run the model on
|
||||
test_set = sv.DetectionDataset.from_yolo(
|
||||
images_directory_path=f"{dataset.location}/test/images",
|
||||
annotations_directory_path=f"{dataset.location}/test/labels",
|
||||
data_yaml_path=f"{dataset.location}/data.yaml"
|
||||
data_yaml_path=f"{dataset.location}/data.yaml",
|
||||
)
|
||||
|
||||
image_paths = []
|
||||
@@ -200,14 +199,16 @@ We need to remap them to match the dataset classes. Here's how to do it:
|
||||
def remap_classes(
|
||||
detections: sv.Detections,
|
||||
class_ids_from_to: dict[int, int],
|
||||
class_names_from_to: dict[str, str]
|
||||
class_names_from_to: dict[str, str],
|
||||
) -> None:
|
||||
new_class_ids = [
|
||||
class_ids_from_to.get(class_id, class_id) for class_id in detections.class_id]
|
||||
class_ids_from_to.get(class_id, class_id) for class_id in detections.class_id
|
||||
]
|
||||
detections.class_id = np.array(new_class_ids)
|
||||
|
||||
new_class_names = [
|
||||
class_names_from_to.get(name, name) for name in detections["class_name"]]
|
||||
class_names_from_to.get(name, name) for name in detections["class_name"]
|
||||
]
|
||||
predictions["class_name"] = np.array(new_class_names)
|
||||
```
|
||||
|
||||
@@ -223,7 +224,7 @@ Let's also remove the predictions that are not in the dataset classes.
|
||||
test_set = sv.DetectionDataset.from_yolo(
|
||||
images_directory_path=f"{dataset.location}/test/images",
|
||||
annotations_directory_path=f"{dataset.location}/test/labels",
|
||||
data_yaml_path=f"{dataset.location}/data.yaml"
|
||||
data_yaml_path=f"{dataset.location}/data.yaml",
|
||||
)
|
||||
|
||||
image_paths = []
|
||||
@@ -237,11 +238,9 @@ Let's also remove the predictions that are not in the dataset classes.
|
||||
remap_classes(
|
||||
detections=predictions,
|
||||
class_ids_from_to={16: 0},
|
||||
class_names_from_to={"dog": "Corgi"}
|
||||
class_names_from_to={"dog": "Corgi"},
|
||||
)
|
||||
predictions = predictions[
|
||||
np.isin(predictions["class_name"], test_set.classes)
|
||||
]
|
||||
predictions = predictions[np.isin(predictions["class_name"], test_set.classes),]
|
||||
|
||||
image_paths.append(image_path)
|
||||
predictions_list.append(predictions)
|
||||
@@ -261,7 +260,7 @@ Let's also remove the predictions that are not in the dataset classes.
|
||||
test_set = sv.DetectionDataset.from_yolo(
|
||||
images_directory_path=f"{dataset.location}/test/images",
|
||||
annotations_directory_path=f"{dataset.location}/test/labels",
|
||||
data_yaml_path=f"{dataset.location}/data.yaml"
|
||||
data_yaml_path=f"{dataset.location}/data.yaml",
|
||||
)
|
||||
|
||||
image_paths = []
|
||||
@@ -275,11 +274,9 @@ Let's also remove the predictions that are not in the dataset classes.
|
||||
remap_classes(
|
||||
detections=predictions,
|
||||
class_ids_from_to={16: 0},
|
||||
class_names_from_to={"dog": "Corgi"}
|
||||
class_names_from_to={"dog": "Corgi"},
|
||||
)
|
||||
predictions = predictions[
|
||||
np.isin(predictions["class_name"], test_set.classes)
|
||||
]
|
||||
predictions = predictions[np.isin(predictions["class_name"], test_set.classes),]
|
||||
|
||||
image_paths.append(image_path)
|
||||
predictions_list.append(predictions)
|
||||
@@ -298,16 +295,22 @@ N = 9
|
||||
GRID_SIZE = (3, 3)
|
||||
|
||||
target_annotator = sv.PolygonAnnotator(color=sv.Color.from_hex("#8315f9"), thickness=8)
|
||||
prediction_annotator = sv.PolygonAnnotator(color=sv.Color.from_hex("#00cfc6"), thickness=6)
|
||||
prediction_annotator = sv.PolygonAnnotator(
|
||||
color=sv.Color.from_hex("#00cfc6"), thickness=6
|
||||
)
|
||||
|
||||
|
||||
annotated_images = []
|
||||
for image_path, predictions, targets in zip(
|
||||
image_paths[:N], predictions_list[:N], targets_list[:N]
|
||||
image_paths[:N], predictions_list[:N], targets_list[:N]
|
||||
):
|
||||
annotated_image = cv2.imread(image_path)
|
||||
annotated_image = target_annotator.annotate(scene=annotated_image, detections=targets)
|
||||
annotated_image = prediction_annotator.annotate(scene=annotated_image, detections=prediction)
|
||||
annotated_image = target_annotator.annotate(
|
||||
scene=annotated_image, detections=targets
|
||||
)
|
||||
annotated_image = prediction_annotator.annotate(
|
||||
scene=annotated_image, detections=prediction
|
||||
)
|
||||
annotated_images.append(annotated_image)
|
||||
|
||||
sv.plot_images_grid(images=annotated_images, grid_size=GRID_SIZE)
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
With supervision, you can count the number of objects in a zone in an image or video. In this guide, we will show how to count the number of cars in a traffic video.
|
||||
|
||||
[View the notebook that accompanies this tutorial](https://github.com/roboflow/notebooks/blob/main/notebooks/how-to-use-polygonzone-annotate-and-supervision.ipynb).
|
||||
|
||||
To make it easier for you to follow our tutorial download the video we will use as an example. You can do this using the `supervision.assets` module:
|
||||
|
||||
```python
|
||||
from supervision.assets import download_assets, VideoAssets
|
||||
|
||||
download_assets(VideoAssets.VEHICLES_2)
|
||||
```
|
||||
|
||||
## Initialize a Model and Load Video
|
||||
|
||||
First, we need to initialize a model. Let's use a YOLOv8 model with the default COCO checkpoint. We also need to load a video on which to run inference.
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
import supervision as sv
|
||||
import cv2
|
||||
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8s.pt")
|
||||
|
||||
VIDEO = str(VideoAssets.VEHICLES_2)
|
||||
|
||||
colors = sv.ColorPalette.default()
|
||||
video_info = sv.VideoInfo.from_video_path(VIDEO)
|
||||
```
|
||||
|
||||
## Calculate Coordinates
|
||||
|
||||
To count objects in a zone, you need to know the coordinates where you want to draw the zone.
|
||||
|
||||
You can calculate coordinates using the [PolygonZone web utility](https://roboflow.github.io/polygonzone/).
|
||||
|
||||
To use the PolygonZone website, you will need to upload an image or frame from a video. You can retrieve a frame using this code:
|
||||
|
||||
```python
|
||||
generator = sv.get_video_frames_generator(VIDEO)
|
||||
iterator = iter(generator)
|
||||
|
||||
frame = next(iterator)
|
||||
|
||||
cv2.imwrite("first_frame.png", frame)
|
||||
```
|
||||
|
||||
PolygonZone will give you NumPy arrays that you can use with supervision to count objects in zones.
|
||||
|
||||
<video width="100%" loop muted autoplay>
|
||||
<source src="https://media.roboflow.com/polygonzone.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
Save the coordinates in an array:
|
||||
|
||||
```python
|
||||
polygons = [
|
||||
np.array([[718, 595], [927, 592], [851, 1062], [42, 1059]]),
|
||||
np.array([[987, 595], [1199, 595], [1893, 1056], [1015, 1062]]),
|
||||
]
|
||||
```
|
||||
|
||||
## Define Zones
|
||||
|
||||
With the coordinates of the zones to draw ready, we can set up our zones:
|
||||
|
||||
```python
|
||||
zones = [
|
||||
sv.PolygonZone(polygon=polygon, frame_resolution_wh=video_info.resolution_wh)
|
||||
for polygon in polygons
|
||||
]
|
||||
zone_annotators = [
|
||||
sv.PolygonZoneAnnotator(
|
||||
zone=zone,
|
||||
color=colors.by_idx(index),
|
||||
thickness=4,
|
||||
text_thickness=8,
|
||||
text_scale=4,
|
||||
)
|
||||
for index, zone in enumerate(zones)
|
||||
]
|
||||
box_annotators = [
|
||||
sv.BoxAnnotator(
|
||||
color=colors.by_idx(index),
|
||||
thickness=4,
|
||||
text_thickness=4,
|
||||
text_scale=2,
|
||||
)
|
||||
for index in range(len(polygons))
|
||||
]
|
||||
```
|
||||
|
||||
## Run Inference
|
||||
|
||||
We can run inference on a video using the [sv.process_video](https://supervision.roboflow.com/utils/video/#process_video) function. This function accepts a callback that runs inference on each frame and compiles the results into a video.
|
||||
|
||||
Below, we can call our YOLOv8 model, annotate predictions and zones, then save the results to a file called `result.mp4`.
|
||||
|
||||
```python
|
||||
def process_frame(frame: np.ndarray, i) -> np.ndarray:
|
||||
results = model(frame, imgsz=1280, verbose=False)[0]
|
||||
detections = sv.Detections.from_ultralytics(results)
|
||||
|
||||
for zone, zone_annotator, box_annotator in zip(
|
||||
zones, zone_annotators, box_annotators
|
||||
):
|
||||
mask = zone.trigger(detections=detections)
|
||||
detections_filtered = detections[mask]
|
||||
frame = box_annotator.annotate(
|
||||
scene=frame, detections=detections_filtered, skip_label=True
|
||||
)
|
||||
frame = zone_annotator.annotate(scene=frame)
|
||||
|
||||
return frame
|
||||
|
||||
|
||||
sv.process_video(source_path=VIDEO, target_path="result.mp4", callback=process_frame)
|
||||
```
|
||||
|
||||
Here is an example of inference run on the video:
|
||||
|
||||
<video width="100%" loop muted autoplay>
|
||||
<source src="https://blog.roboflow.com/content/media/2023/03/trim-counting.mp4" type="video/mp4">
|
||||
</video>
|
||||
@@ -20,26 +20,29 @@ First, you'll need to obtain predictions from your object detection or segmentat
|
||||
model.
|
||||
|
||||
=== "Inference"
|
||||
|
||||
```python
|
||||
import cv2
|
||||
from inference import get_model
|
||||
|
||||
model = get_model(model_id="yolov8n-640")
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
results = model.infer(image)[0]
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```python
|
||||
import cv2
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8n.pt")
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
results = model(image)[0]
|
||||
```
|
||||
|
||||
=== "Transformers"
|
||||
|
||||
```python
|
||||
import torch
|
||||
from PIL import Image
|
||||
@@ -48,7 +51,7 @@ model.
|
||||
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
|
||||
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
|
||||
|
||||
image = Image.open(<SOURCE_IMAGE_PATH>)
|
||||
image = Image.open("<SOURCE_IMAGE_PATH>")
|
||||
inputs = processor(images=image, return_tensors="pt")
|
||||
|
||||
with torch.no_grad():
|
||||
@@ -57,7 +60,8 @@ model.
|
||||
width, height = image.size
|
||||
target_size = torch.tensor([[height, width]])
|
||||
results = processor.post_process_object_detection(
|
||||
outputs=outputs, target_sizes=target_size)[0]
|
||||
outputs=outputs, target_sizes=target_size
|
||||
)[0]
|
||||
```
|
||||
|
||||
## Load Predictions into Supervision
|
||||
@@ -65,7 +69,8 @@ model.
|
||||
Now that we have predictions from a model, we can load them into Supervision.
|
||||
|
||||
=== "Inference"
|
||||
We can do so using the [`sv.Detections.from_inference`](/latest/detection/core/#supervision.detection.core.Detections.from_inference) method, which accepts model results from both detection and segmentation models.
|
||||
|
||||
We can do so using the [`sv.Detections.from_inference`](https://supervision.roboflow.com/latest/detection/core/#supervision.detection.core.Detections.from_inference) method, which accepts model results from both detection and segmentation models.
|
||||
|
||||
```{ .py hl_lines="2 8" }
|
||||
import cv2
|
||||
@@ -73,13 +78,14 @@ Now that we have predictions from a model, we can load them into Supervision.
|
||||
from inference import get_model
|
||||
|
||||
model = get_model(model_id="yolov8n-640")
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
results = model.infer(image)[0]
|
||||
detections = sv.Detections.from_inference(results)
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
We can do so using the [`sv.Detections.from_ultralytics`](/latest/detection/core/#supervision.detection.core.Detections.from_ultralytics) method, which accepts model results from both detection and segmentation models.
|
||||
|
||||
We can do so using the [`sv.Detections.from_ultralytics`](https://supervision.roboflow.com/latest/detection/core/#supervision.detection.core.Detections.from_ultralytics) method, which accepts model results from both detection and segmentation models.
|
||||
|
||||
```{ .py hl_lines="2 8" }
|
||||
import cv2
|
||||
@@ -87,13 +93,14 @@ Now that we have predictions from a model, we can load them into Supervision.
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8n.pt")
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
results = model(image)[0]
|
||||
detections = sv.Detections.from_ultralytics(results)
|
||||
```
|
||||
|
||||
=== "Transformers"
|
||||
We can do so using the [`sv.Detections.from_transformers`](/latest/detection/core/#supervision.detection.core.Detections.from_transformers) method, which accepts model results from both detection and segmentation models.
|
||||
|
||||
We can do so using the [`sv.Detections.from_transformers`](https://supervision.roboflow.com/latest/detection/core/#supervision.detection.core.Detections.from_transformers) method, which accepts model results from both detection and segmentation models.
|
||||
|
||||
```{ .py hl_lines="2 19-21" }
|
||||
import torch
|
||||
@@ -104,7 +111,7 @@ Now that we have predictions from a model, we can load them into Supervision.
|
||||
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
|
||||
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
|
||||
|
||||
image = Image.open(<SOURCE_IMAGE_PATH>)
|
||||
image = Image.open("<SOURCE_IMAGE_PATH>")
|
||||
inputs = processor(images=image, return_tensors="pt")
|
||||
|
||||
with torch.no_grad():
|
||||
@@ -121,24 +128,25 @@ Now that we have predictions from a model, we can load them into Supervision.
|
||||
|
||||
You can load predictions from other computer vision frameworks and libraries using:
|
||||
|
||||
- [`from_deepsparse`](/latest/detection/core/#supervision.detection.core.Detections.from_deepsparse) ([Deepsparse](https://github.com/neuralmagic/deepsparse))
|
||||
- [`from_detectron2`](/latest/detection/core/#supervision.detection.core.Detections.from_detectron2) ([Detectron2](https://github.com/facebookresearch/detectron2))
|
||||
- [`from_mmdetection`](/latest/detection/core/#supervision.detection.core.Detections.from_mmdetection) ([MMDetection](https://github.com/open-mmlab/mmdetection))
|
||||
- [`from_sam`](/latest/detection/core/#supervision.detection.core.Detections.from_sam) ([Segment Anything Model](https://github.com/facebookresearch/segment-anything))
|
||||
- [`from_yolo_nas`](/latest/detection/core/#supervision.detection.core.Detections.from_yolo_nas) ([YOLO-NAS](https://github.com/Deci-AI/super-gradients/blob/master/YOLONAS.md))
|
||||
- [`from_deepsparse`](https://supervision.roboflow.com/latest/detection/core/#supervision.detection.core.Detections.from_deepsparse) ([Deepsparse](https://github.com/neuralmagic/deepsparse))
|
||||
- [`from_detectron2`](https://supervision.roboflow.com/latest/detection/core/#supervision.detection.core.Detections.from_detectron2) ([Detectron2](https://github.com/facebookresearch/detectron2))
|
||||
- [`from_mmdetection`](https://supervision.roboflow.com/latest/detection/core/#supervision.detection.core.Detections.from_mmdetection) ([MMDetection](https://github.com/open-mmlab/mmdetection))
|
||||
- [`from_sam`](https://supervision.roboflow.com/latest/detection/core/#supervision.detection.core.Detections.from_sam) ([Segment Anything Model](https://github.com/facebookresearch/segment-anything))
|
||||
- [`from_yolo_nas`](https://supervision.roboflow.com/latest/detection/core/#supervision.detection.core.Detections.from_yolo_nas) ([YOLO-NAS](https://github.com/Deci-AI/super-gradients/blob/master/YOLONAS.md))
|
||||
|
||||
## Annotate Image with Detections
|
||||
|
||||
Finally, we can annotate the image with the predictions. Since we are working with an object detection model, we will use the [`sv.BoxAnnotator`](/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator) and [`sv.LabelAnnotator`](/latest/detection/annotators/#supervision.annotators.core.LabelAnnotator) classes.
|
||||
Finally, we can annotate the image with the predictions. Since we are working with an object detection model, we will use the [`sv.BoxAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator) and [`sv.LabelAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.LabelAnnotator) classes.
|
||||
|
||||
=== "Inference"
|
||||
|
||||
```{ .py hl_lines="10-16" }
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from inference import get_model
|
||||
|
||||
model = get_model(model_id="yolov8n-640")
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
results = model.infer(image)[0]
|
||||
detections = sv.Detections.from_inference(results)
|
||||
|
||||
@@ -152,13 +160,14 @@ Finally, we can annotate the image with the predictions. Since we are working wi
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```{ .py hl_lines="10-16" }
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8n.pt")
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
results = model(image)[0]
|
||||
detections = sv.Detections.from_ultralytics(results)
|
||||
|
||||
@@ -172,6 +181,7 @@ Finally, we can annotate the image with the predictions. Since we are working wi
|
||||
```
|
||||
|
||||
=== "Transformers"
|
||||
|
||||
```{ .py hl_lines="23-30" }
|
||||
import torch
|
||||
import supervision as sv
|
||||
@@ -181,7 +191,7 @@ Finally, we can annotate the image with the predictions. Since we are working wi
|
||||
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
|
||||
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
|
||||
|
||||
image = Image.open(<SOURCE_IMAGE_PATH>)
|
||||
image = Image.open("<SOURCE_IMAGE_PATH>")
|
||||
inputs = processor(images=image, return_tensors="pt")
|
||||
|
||||
with torch.no_grad():
|
||||
@@ -208,18 +218,19 @@ Finally, we can annotate the image with the predictions. Since we are working wi
|
||||
|
||||
## Display Custom Labels
|
||||
|
||||
By default, [`sv.LabelAnnotator`](/latest/detection/annotators/#supervision.annotators.core.LabelAnnotator)
|
||||
By default, [`sv.LabelAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.LabelAnnotator)
|
||||
will label each detection with its `class_name` (if possible) or `class_id`. You can
|
||||
override this behavior by passing a list of custom `labels` to the `annotate` method.
|
||||
|
||||
=== "Inference"
|
||||
|
||||
```{ .py hl_lines="13-17 22" }
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from inference import get_model
|
||||
|
||||
model = get_model(model_id="yolov8n-640")
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
results = model.infer(image)[0]
|
||||
detections = sv.Detections.from_inference(results)
|
||||
|
||||
@@ -239,13 +250,14 @@ override this behavior by passing a list of custom `labels` to the `annotate` me
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```{ .py hl_lines="13-17 22" }
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8n.pt")
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
results = model(image)[0]
|
||||
detections = sv.Detections.from_ultralytics(results)
|
||||
|
||||
@@ -265,6 +277,7 @@ override this behavior by passing a list of custom `labels` to the `annotate` me
|
||||
```
|
||||
|
||||
=== "Transformers"
|
||||
|
||||
```{ .py hl_lines="26-30 35" }
|
||||
import torch
|
||||
import supervision as sv
|
||||
@@ -274,7 +287,7 @@ override this behavior by passing a list of custom `labels` to the `annotate` me
|
||||
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
|
||||
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
|
||||
|
||||
image = Image.open(<SOURCE_IMAGE_PATH>)
|
||||
image = Image.open("<SOURCE_IMAGE_PATH>")
|
||||
inputs = processor(images=image, return_tensors="pt")
|
||||
|
||||
with torch.no_grad():
|
||||
@@ -308,19 +321,20 @@ override this behavior by passing a list of custom `labels` to the `annotate` me
|
||||
## Annotate Image with Segmentations
|
||||
|
||||
If you are running the segmentation model
|
||||
[`sv.MaskAnnotator`](/latest/detection/annotators/#supervision.annotators.core.MaskAnnotator)
|
||||
[`sv.MaskAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.MaskAnnotator)
|
||||
is a drop-in replacement for
|
||||
[`sv.BoxAnnotator`](/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator)
|
||||
[`sv.BoxAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator)
|
||||
that will allow you to draw masks instead of boxes.
|
||||
|
||||
=== "Inference"
|
||||
|
||||
```python
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from inference import get_model
|
||||
|
||||
model = get_model(model_id="yolov8n-seg-640")
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
results = model.infer(image)[0]
|
||||
detections = sv.Detections.from_inference(results)
|
||||
|
||||
@@ -328,19 +342,24 @@ that will allow you to draw masks instead of boxes.
|
||||
label_annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER_OF_MASS)
|
||||
|
||||
annotated_image = mask_annotator.annotate(
|
||||
scene=image, detections=detections)
|
||||
scene=image,
|
||||
detections=detections,
|
||||
)
|
||||
annotated_image = label_annotator.annotate(
|
||||
scene=annotated_image, detections=detections)
|
||||
scene=annotated_image,
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```python
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8n-seg.pt")
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
results = model(image)[0]
|
||||
detections = sv.Detections.from_ultralytics(results)
|
||||
|
||||
@@ -348,12 +367,17 @@ that will allow you to draw masks instead of boxes.
|
||||
label_annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER_OF_MASS)
|
||||
|
||||
annotated_image = mask_annotator.annotate(
|
||||
scene=image, detections=detections)
|
||||
scene=image,
|
||||
detections=detections,
|
||||
)
|
||||
annotated_image = label_annotator.annotate(
|
||||
scene=annotated_image, detections=detections)
|
||||
scene=annotated_image,
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
=== "Transformers"
|
||||
|
||||
```python
|
||||
import torch
|
||||
import supervision as sv
|
||||
@@ -363,7 +387,7 @@ that will allow you to draw masks instead of boxes.
|
||||
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50-panoptic")
|
||||
model = DetrForSegmentation.from_pretrained("facebook/detr-resnet-50-panoptic")
|
||||
|
||||
image = Image.open(<SOURCE_IMAGE_PATH>)
|
||||
image = Image.open("<SOURCE_IMAGE_PATH>")
|
||||
inputs = processor(images=image, return_tensors="pt")
|
||||
|
||||
with torch.no_grad():
|
||||
@@ -372,24 +396,27 @@ that will allow you to draw masks instead of boxes.
|
||||
width, height = image.size
|
||||
target_size = torch.tensor([[height, width]])
|
||||
results = processor.post_process_segmentation(
|
||||
outputs=outputs, target_sizes=target_size)[0]
|
||||
outputs=outputs, target_sizes=target_size
|
||||
)[0]
|
||||
detections = sv.Detections.from_transformers(
|
||||
transformers_results=results,
|
||||
id2label=model.config.id2label)
|
||||
transformers_results=results, id2label=model.config.id2label
|
||||
)
|
||||
|
||||
mask_annotator = sv.MaskAnnotator()
|
||||
label_annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER_OF_MASS)
|
||||
|
||||
labels = [
|
||||
f"{class_name} {confidence:.2f}"
|
||||
for class_name, confidence
|
||||
in zip(detections['class_name'], detections.confidence)
|
||||
for class_name, confidence in zip(
|
||||
detections["class_name"],
|
||||
detections.confidence,
|
||||
)
|
||||
]
|
||||
|
||||
annotated_image = mask_annotator.annotate(
|
||||
scene=image, detections=detections)
|
||||
annotated_image = mask_annotator.annotate(scene=image, detections=detections)
|
||||
annotated_image = label_annotator.annotate(
|
||||
scene=annotated_image, detections=detections, labels=labels)
|
||||
scene=annotated_image, detections=detections, labels=labels
|
||||
)
|
||||
```
|
||||
|
||||

|
||||
|
||||
@@ -8,7 +8,7 @@ This guide shows how to detect small objects
|
||||
with the [Inference](https://github.com/roboflow/inference),
|
||||
[Ultralytics](https://github.com/ultralytics/ultralytics) or
|
||||
[Transformers](https://github.com/huggingface/transformers) packages using
|
||||
[`InferenceSlicer`](/latest/detection/tools/inference_slicer/#supervision.detection.tools.inference_slicer.InferenceSlicer).
|
||||
[`InferenceSlicer`](https://supervision.roboflow.com/latest/detection/tools/inference_slicer/#supervision.detection.tools.inference_slicer.InferenceSlicer).
|
||||
|
||||
<video controls>
|
||||
<source src="https://media.roboflow.com/supervision_detect_small_objects_example.mp4" type="video/mp4">
|
||||
@@ -20,13 +20,14 @@ Small object detection in high-resolution images presents challenges due to the
|
||||
size relative to the image resolution.
|
||||
|
||||
=== "Inference"
|
||||
|
||||
```python
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from inference import get_model
|
||||
|
||||
model = get_model(model_id="yolov8x-640")
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
results = model.infer(image)[0]
|
||||
detections = sv.Detections.from_inference(results)
|
||||
|
||||
@@ -34,19 +35,24 @@ size relative to the image resolution.
|
||||
label_annotator = sv.LabelAnnotator()
|
||||
|
||||
annotated_image = box_annotator.annotate(
|
||||
scene=image, detections=detections)
|
||||
scene=image,
|
||||
detections=detections,
|
||||
)
|
||||
annotated_image = label_annotator.annotate(
|
||||
scene=annotated_image, detections=detections)
|
||||
scene=annotated_image,
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```python
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8x.pt")
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
results = model(image)[0]
|
||||
detections = sv.Detections.from_ultralytics(results)
|
||||
|
||||
@@ -54,12 +60,17 @@ size relative to the image resolution.
|
||||
label_annotator = sv.LabelAnnotator()
|
||||
|
||||
annotated_image = box_annotator.annotate(
|
||||
scene=image, detections=detections)
|
||||
scene=image,
|
||||
detections=detections,
|
||||
)
|
||||
annotated_image = label_annotator.annotate(
|
||||
scene=annotated_image, detections=detections)
|
||||
scene=annotated_image,
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
=== "Transformers"
|
||||
|
||||
```python
|
||||
import torch
|
||||
import supervision as sv
|
||||
@@ -69,7 +80,7 @@ size relative to the image resolution.
|
||||
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
|
||||
model = DetrForSegmentation.from_pretrained("facebook/detr-resnet-50")
|
||||
|
||||
image = Image.open(<SOURCE_IMAGE_PATH>)
|
||||
image = Image.open("<SOURCE_IMAGE_PATH>")
|
||||
inputs = processor(images=image, return_tensors="pt")
|
||||
|
||||
with torch.no_grad():
|
||||
@@ -78,22 +89,19 @@ size relative to the image resolution.
|
||||
width, height = image_slice.size
|
||||
target_size = torch.tensor([[width, height]])
|
||||
results = processor.post_process_object_detection(
|
||||
outputs=outputs, target_sizes=target_size)[0]
|
||||
outputs=outputs, target_sizes=target_size
|
||||
)[0]
|
||||
detections = sv.Detections.from_transformers(results)
|
||||
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
label_annotator = sv.LabelAnnotator()
|
||||
|
||||
labels = [
|
||||
model.config.id2label[class_id]
|
||||
for class_id
|
||||
in detections.class_id
|
||||
]
|
||||
labels = [model.config.id2label[class_id] for class_id in detections.class_id]
|
||||
|
||||
annotated_image = box_annotator.annotate(
|
||||
scene=image, detections=detections)
|
||||
annotated_image = box_annotator.annotate(scene=image, detections=detections)
|
||||
annotated_image = label_annotator.annotate(
|
||||
scene=annotated_image, detections=detections, labels=labels)
|
||||
scene=annotated_image, detections=detections, labels=labels
|
||||
)
|
||||
```
|
||||
|
||||

|
||||
@@ -105,13 +113,14 @@ identification at the cost of processing speed and increased memory usage. This
|
||||
is less effective for ultra-high-resolution images (4K and above).
|
||||
|
||||
=== "Inference"
|
||||
|
||||
```{ .py hl_lines="5" }
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from inference import get_model
|
||||
|
||||
model = get_model(model_id="yolov8x-1280")
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
results = model.infer(image)[0]
|
||||
detections = sv.Detections.from_inference(results)
|
||||
|
||||
@@ -125,13 +134,14 @@ is less effective for ultra-high-resolution images (4K and above).
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```{ .py hl_lines="7" }
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8x.pt")
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
results = model(image, imgsz=1280)[0]
|
||||
detections = sv.Detections.from_ultralytics(results)
|
||||
|
||||
@@ -148,7 +158,7 @@ is less effective for ultra-high-resolution images (4K and above).
|
||||
|
||||
## Inference Slicer
|
||||
|
||||
[`InferenceSlicer`](/latest/detection/tools/inference_slicer/#supervision.detection.tools.inference_slicer.InferenceSlicer)
|
||||
[`InferenceSlicer`](https://supervision.roboflow.com/latest/detection/tools/inference_slicer/#supervision.detection.tools.inference_slicer.InferenceSlicer)
|
||||
processes high-resolution images by dividing them into smaller segments, detecting
|
||||
objects within each, and aggregating the results.
|
||||
|
||||
@@ -157,6 +167,7 @@ objects within each, and aggregating the results.
|
||||
</video>
|
||||
|
||||
=== "Inference"
|
||||
|
||||
```{ .py hl_lines="9-14" }
|
||||
import cv2
|
||||
import numpy as np
|
||||
@@ -164,7 +175,7 @@ objects within each, and aggregating the results.
|
||||
from inference import get_model
|
||||
|
||||
model = get_model(model_id="yolov8x-640")
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
|
||||
def callback(image_slice: np.ndarray) -> sv.Detections:
|
||||
results = model.infer(image_slice)[0]
|
||||
@@ -183,6 +194,7 @@ objects within each, and aggregating the results.
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```{ .py hl_lines="9-14" }
|
||||
import cv2
|
||||
import numpy as np
|
||||
@@ -190,7 +202,7 @@ objects within each, and aggregating the results.
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8x.pt")
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
|
||||
def callback(image_slice: np.ndarray) -> sv.Detections:
|
||||
result = model(image_slice)[0]
|
||||
@@ -209,6 +221,7 @@ objects within each, and aggregating the results.
|
||||
```
|
||||
|
||||
=== "Transformers"
|
||||
|
||||
```{ .py hl_lines="13-28" }
|
||||
import cv2
|
||||
import torch
|
||||
@@ -220,7 +233,7 @@ objects within each, and aggregating the results.
|
||||
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
|
||||
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
|
||||
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
|
||||
def callback(image_slice: np.ndarray) -> sv.Detections:
|
||||
image_slice = cv2.cvtColor(image_slice, cv2.COLOR_BGR2RGB)
|
||||
@@ -258,9 +271,10 @@ objects within each, and aggregating the results.
|
||||
|
||||
## Small Object Segmentation
|
||||
|
||||
[`InferenceSlicer`](/latest/detection/tools/inference_slicer/#supervision.detection.tools.inference_slicer.InferenceSlicer) can perform segmentation tasks too.
|
||||
[`InferenceSlicer`](https://supervision.roboflow.com/latest/detection/tools/inference_slicer/#supervision.detection.tools.inference_slicer.InferenceSlicer) can perform segmentation tasks too.
|
||||
|
||||
=== "Inference"
|
||||
|
||||
```{ .py hl_lines="6 16 19-20" }
|
||||
import cv2
|
||||
import numpy as np
|
||||
@@ -268,7 +282,7 @@ objects within each, and aggregating the results.
|
||||
from inference import get_model
|
||||
|
||||
model = get_model(model_id="yolov8x-seg-640")
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
|
||||
def callback(image_slice: np.ndarray) -> sv.Detections:
|
||||
results = model.infer(image_slice)[0]
|
||||
@@ -287,6 +301,7 @@ objects within each, and aggregating the results.
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```{ .py hl_lines="6 16 19-20" }
|
||||
import cv2
|
||||
import numpy as np
|
||||
@@ -294,7 +309,7 @@ objects within each, and aggregating the results.
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8x-seg.pt")
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
|
||||
def callback(image_slice: np.ndarray) -> sv.Detections:
|
||||
result = model(image_slice)[0]
|
||||
|
||||
@@ -15,6 +15,7 @@ the filters in their applications.
|
||||
Allows you to select detections that belong only to one selected class.
|
||||
|
||||
=== "After"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -29,6 +30,7 @@ Allows you to select detections that belong only to one selected class.
|
||||
</div>
|
||||
|
||||
=== "Before"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -47,6 +49,7 @@ Allows you to select detections that belong only to one selected class.
|
||||
Allows you to select detections that belong only to selected set of classes.
|
||||
|
||||
=== "After"
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
import supervision as sv
|
||||
@@ -63,6 +66,7 @@ Allows you to select detections that belong only to selected set of classes.
|
||||
</div>
|
||||
|
||||
=== "Before"
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
import supervision as sv
|
||||
@@ -83,6 +87,7 @@ Allows you to select detections that belong only to selected set of classes.
|
||||
Allows you to select detections with specific confidence value, for example higher than selected threshold.
|
||||
|
||||
=== "After"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -97,6 +102,7 @@ Allows you to select detections with specific confidence value, for example high
|
||||
</div>
|
||||
|
||||
=== "Before"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -116,6 +122,7 @@ Allows you to select detections based on their size. We define the area as the n
|
||||
detection in the image. In the example below, we have sifted out the detections that are too small.
|
||||
|
||||
=== "After"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -130,6 +137,7 @@ detection in the image. In the example below, we have sifted out the detections
|
||||
</div>
|
||||
|
||||
=== "Before"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -151,6 +159,7 @@ but small on a 3840x2160 image. In such cases, we can filter out detections base
|
||||
occupied by them. In the example below, we remove too large detections.
|
||||
|
||||
=== "After"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -169,6 +178,7 @@ occupied by them. In the example below, we remove too large detections.
|
||||
</div>
|
||||
|
||||
=== "Before"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -193,6 +203,7 @@ can be criteria for rejecting detection. Implementing such filtering requires a
|
||||
simple and fast.
|
||||
|
||||
=== "After"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -209,6 +220,7 @@ simple and fast.
|
||||
</div>
|
||||
|
||||
=== "Before"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -230,6 +242,7 @@ Allows you to use `Detections` in combination with `PolygonZone` to weed out bou
|
||||
zone. In the example below you can see how to filter out all detections located in the lower part of the image.
|
||||
|
||||
=== "After"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -246,6 +259,7 @@ zone. In the example below you can see how to filter out all detections located
|
||||
</div>
|
||||
|
||||
=== "Before"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -266,6 +280,7 @@ zone. In the example below you can see how to filter out all detections located
|
||||
`Detections`' greatest strength, however, is that you can build arbitrarily complex logical conditions by simply combining separate conditions using `&` or `|`.
|
||||
|
||||
=== "After"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -282,6 +297,7 @@ zone. In the example below you can see how to filter out all detections located
|
||||
</div>
|
||||
|
||||
=== "Before"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@ your workspace ID, project ID, and version number.
|
||||
roboflow.login()
|
||||
|
||||
rf = roboflow.Roboflow()
|
||||
project = rf.workspace('<WORKSPACE_ID>').project('<PROJECT_ID>')
|
||||
dataset = project.version('<PROJECT_VERSION>').download("coco")
|
||||
project = rf.workspace("<WORKSPACE_ID>").project("<PROJECT_ID>")
|
||||
dataset = project.version("<PROJECT_VERSION>").download("coco")
|
||||
```
|
||||
|
||||
=== "YOLO"
|
||||
@@ -44,8 +44,8 @@ your workspace ID, project ID, and version number.
|
||||
roboflow.login()
|
||||
|
||||
rf = roboflow.Roboflow()
|
||||
project = rf.workspace('<WORKSPACE_ID>').project('<PROJECT_ID>')
|
||||
dataset = project.version('<PROJECT_VERSION>').download("yolov8")
|
||||
project = rf.workspace("<WORKSPACE_ID>").project("<PROJECT_ID>")
|
||||
dataset = project.version("<PROJECT_VERSION>").download("yolov8")
|
||||
```
|
||||
|
||||
=== "Pascal VOC"
|
||||
@@ -56,8 +56,8 @@ your workspace ID, project ID, and version number.
|
||||
roboflow.login()
|
||||
|
||||
rf = roboflow.Roboflow()
|
||||
project = rf.workspace('<WORKSPACE_ID>').project('<PROJECT_ID>')
|
||||
dataset = project.version('<PROJECT_VERSION>').download("voc")
|
||||
project = rf.workspace("<WORKSPACE_ID>").project("<PROJECT_ID>")
|
||||
dataset = project.version("<PROJECT_VERSION>").download("voc")
|
||||
```
|
||||
|
||||
## Load Dataset
|
||||
@@ -75,16 +75,16 @@ instances.
|
||||
import supervision as sv
|
||||
|
||||
ds_train = sv.DetectionDataset.from_coco(
|
||||
images_directory_path=f'{dataset.location}/train',
|
||||
annotations_path=f'{dataset.location}/train/_annotations.coco.json',
|
||||
images_directory_path=f"{dataset.location}/train",
|
||||
annotations_path=f"{dataset.location}/train/_annotations.coco.json",
|
||||
)
|
||||
ds_valid = sv.DetectionDataset.from_coco(
|
||||
images_directory_path=f'{dataset.location}/valid',
|
||||
annotations_path=f'{dataset.location}/valid/_annotations.coco.json',
|
||||
images_directory_path=f"{dataset.location}/valid",
|
||||
annotations_path=f"{dataset.location}/valid/_annotations.coco.json",
|
||||
)
|
||||
ds_test = sv.DetectionDataset.from_coco(
|
||||
images_directory_path=f'{dataset.location}/test',
|
||||
annotations_path=f'{dataset.location}/test/_annotations.coco.json',
|
||||
images_directory_path=f"{dataset.location}/test",
|
||||
annotations_path=f"{dataset.location}/test/_annotations.coco.json",
|
||||
)
|
||||
|
||||
ds_train.classes
|
||||
@@ -102,19 +102,19 @@ instances.
|
||||
import supervision as sv
|
||||
|
||||
ds_train = sv.DetectionDataset.from_yolo(
|
||||
images_directory_path=f'{dataset.location}/train/images',
|
||||
annotations_directory_path=f'{dataset.location}/train/labels',
|
||||
data_yaml_path=f'{dataset.location}/data.yaml'
|
||||
images_directory_path=f"{dataset.location}/train/images",
|
||||
annotations_directory_path=f"{dataset.location}/train/labels",
|
||||
data_yaml_path=f"{dataset.location}/data.yaml",
|
||||
)
|
||||
ds_valid = sv.DetectionDataset.from_yolo(
|
||||
images_directory_path=f'{dataset.location}/valid/images',
|
||||
annotations_directory_path=f'{dataset.location}/valid/labels',
|
||||
data_yaml_path=f'{dataset.location}/data.yaml'
|
||||
images_directory_path=f"{dataset.location}/valid/images",
|
||||
annotations_directory_path=f"{dataset.location}/valid/labels",
|
||||
data_yaml_path=f"{dataset.location}/data.yaml",
|
||||
)
|
||||
ds_test = sv.DetectionDataset.from_yolo(
|
||||
images_directory_path=f'{dataset.location}/test/images',
|
||||
annotations_directory_path=f'{dataset.location}/test/labels',
|
||||
data_yaml_path=f'{dataset.location}/data.yaml'
|
||||
images_directory_path=f"{dataset.location}/test/images",
|
||||
annotations_directory_path=f"{dataset.location}/test/labels",
|
||||
data_yaml_path=f"{dataset.location}/data.yaml",
|
||||
)
|
||||
|
||||
ds_train.classes
|
||||
@@ -132,16 +132,16 @@ instances.
|
||||
import supervision as sv
|
||||
|
||||
ds_train = sv.DetectionDataset.from_pascal_voc(
|
||||
images_directory_path=f'{dataset.location}/train/images',
|
||||
annotations_directory_path=f'{dataset.location}/train/labels'
|
||||
images_directory_path=f"{dataset.location}/train/images",
|
||||
annotations_directory_path=f"{dataset.location}/train/labels",
|
||||
)
|
||||
ds_valid = sv.DetectionDataset.from_pascal_voc(
|
||||
images_directory_path=f'{dataset.location}/valid/images',
|
||||
annotations_directory_path=f'{dataset.location}/valid/labels'
|
||||
images_directory_path=f"{dataset.location}/valid/images",
|
||||
annotations_directory_path=f"{dataset.location}/valid/labels",
|
||||
)
|
||||
ds_test = sv.DetectionDataset.from_pascal_voc(
|
||||
images_directory_path=f'{dataset.location}/test/images',
|
||||
annotations_directory_path=f'{dataset.location}/test/labels'
|
||||
images_directory_path=f"{dataset.location}/test/images",
|
||||
annotations_directory_path=f"{dataset.location}/test/labels",
|
||||
)
|
||||
|
||||
ds_train.classes
|
||||
@@ -294,12 +294,12 @@ ds = sv.DetectionDataset(...)
|
||||
|
||||
# Option 1
|
||||
for image_path, image, annotations in ds:
|
||||
... # Process each image and its annotations
|
||||
... # Process each image and its annotations
|
||||
|
||||
# Option 2
|
||||
for idx in range(len(ds)):
|
||||
image_path, image, annotations = ds[idx]
|
||||
... # Process the image and annotations at index `idx`
|
||||
... # Process the image and annotations at index `idx`
|
||||
```
|
||||
|
||||
## Visualize Dataset
|
||||
@@ -331,12 +331,9 @@ for i in range(16):
|
||||
annotated_image = label_annotator.annotate(annotated_image, annotations, labels)
|
||||
annotated_images.append(annotated_image)
|
||||
|
||||
grid = sv.create_tiles(
|
||||
sv.plot_images_grid(
|
||||
annotated_images,
|
||||
grid_size=(4, 4),
|
||||
single_tile_size=(400, 400),
|
||||
tile_padding_color=sv.Color.WHITE,
|
||||
tile_margin_color=sv.Color.WHITE
|
||||
)
|
||||
```
|
||||
|
||||
@@ -354,8 +351,8 @@ grid = sv.create_tiles(
|
||||
ds = sv.DetectionDataset(...)
|
||||
|
||||
ds.as_coco(
|
||||
images_directory_path='<IMAGE_DIRECTORY_PATH>',
|
||||
annotations_path='<ANNOTATIONS_PATH>'
|
||||
images_directory_path="<IMAGE_DIRECTORY_PATH>",
|
||||
annotations_path="<ANNOTATIONS_PATH>",
|
||||
)
|
||||
```
|
||||
|
||||
@@ -369,9 +366,9 @@ grid = sv.create_tiles(
|
||||
ds = sv.DetectionDataset(...)
|
||||
|
||||
ds.as_yolo(
|
||||
images_directory_path='<IMAGE_DIRECTORY_PATH>',
|
||||
annotations_directory_path='<ANNOTATIONS_DIRECTORY_PATH>',
|
||||
data_yaml_path='<DATA_YAML_PATH>'
|
||||
images_directory_path="<IMAGE_DIRECTORY_PATH>",
|
||||
annotations_directory_path="<ANNOTATIONS_DIRECTORY_PATH>",
|
||||
data_yaml_path="<DATA_YAML_PATH>",
|
||||
)
|
||||
```
|
||||
|
||||
@@ -385,8 +382,8 @@ grid = sv.create_tiles(
|
||||
ds = sv.DetectionDataset(...)
|
||||
|
||||
ds.as_pascal_voc(
|
||||
images_directory_path='<IMAGE_DIRECTORY_PATH>',
|
||||
annotations_directory_path='<ANNOTATIONS_DIRECTORY_PATH>'
|
||||
images_directory_path="<IMAGE_DIRECTORY_PATH>",
|
||||
annotations_directory_path="<ANNOTATIONS_DIRECTORY_PATH>",
|
||||
)
|
||||
```
|
||||
|
||||
@@ -398,16 +395,16 @@ increase the size and diversity of training datasets, leading to improved model
|
||||
performance and generalization.
|
||||
|
||||
```bash
|
||||
pip install augmentation
|
||||
pip install albumentations
|
||||
```
|
||||
|
||||
Albumentations provides a flexible and powerful API for image augmentation. The core of
|
||||
the library is the [`Compose`](https://albumentations.ai/docs/api_reference/full_reference/?h=compose#albumentations.core.composition.Compose)
|
||||
the library is the [`Compose`](https://albumentations.ai/docs/api-reference/albumentations/core/composition/#Compose)
|
||||
class, which allows you to chain multiple image transformations together. Each
|
||||
transformation is defined using a dedicated class, such as
|
||||
[`HorizontalFlip`](https://albumentations.ai/docs/api_reference/full_reference/?h=horizontalflip#albumentations.augmentations.geometric.transforms.HorizontalFlip),
|
||||
[`RandomBrightnessContrast`](https://albumentations.ai/docs/api_reference/full_reference/?h=horizontalflip#albumentations.augmentations.transforms.RandomBrightnessContrast),
|
||||
or [`Perspective`](https://albumentations.ai/docs/api_reference/full_reference/?h=horizontalflip#albumentations.augmentations.geometric.transforms.Perspective).
|
||||
[`HorizontalFlip`](https://albumentations.ai/docs/api-reference/albumentations/augmentations/geometric/flip/#HorizontalFlip),
|
||||
[`RandomBrightnessContrast`](https://albumentations.ai/docs/api-reference/albumentations/augmentations/pixel/transforms/#RandomBrightnessContrast),
|
||||
or [`Perspective`](https://albumentations.ai/docs/api-reference/albumentations/augmentations/geometric/transforms/#Perspective).
|
||||
|
||||
```python
|
||||
import albumentations as A
|
||||
@@ -416,11 +413,11 @@ augmentation = A.Compose(
|
||||
transforms=[
|
||||
A.Perspective(p=0.1),
|
||||
A.HorizontalFlip(p=0.5),
|
||||
A.RandomBrightnessContrast(p=0.5)
|
||||
A.RandomBrightnessContrast(p=0.5),
|
||||
],
|
||||
bbox_params=A.BboxParams(
|
||||
format='pascal_voc',
|
||||
label_fields=['category']
|
||||
format="pascal_voc",
|
||||
label_fields=["category"],
|
||||
),
|
||||
)
|
||||
```
|
||||
@@ -440,14 +437,14 @@ _, original_image, original_annotations = ds[0]
|
||||
output = augmentation(
|
||||
image=original_image,
|
||||
bboxes=original_annotations.xyxy,
|
||||
category=original_annotations.class_id
|
||||
category=original_annotations.class_id,
|
||||
)
|
||||
|
||||
augmented_image = output['image']
|
||||
augmented_image = output["image"]
|
||||
augmented_annotations = replace(
|
||||
original_annotations,
|
||||
xyxy=np.array(output['bboxes']),
|
||||
class_id=np.array(output['category'])
|
||||
xyxy=np.array(output["bboxes"]),
|
||||
class_id=np.array(output["category"]),
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@@ -9,44 +9,45 @@ 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`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink) and
|
||||
[`sv.JSONSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.JSONSink).
|
||||
[`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.csv_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](/latest/how_to/detect_and_annotate.md) guide.
|
||||
[How to Detect and Annotate](https://supervision.roboflow.com/latest/how_to/detect_and_annotate/) guide.
|
||||
|
||||
=== "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(<SOURCE_VIDEO_PATH>)
|
||||
frames_generator = sv.get_video_frames_generator("<SOURCE_VIDEO_PATH>")
|
||||
|
||||
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(<SOURCE_VIDEO_PATH>)
|
||||
frames_generator = sv.get_video_frames_generator("<SOURCE_VIDEO_PATH>")
|
||||
|
||||
for frame in frames_generator:
|
||||
|
||||
results = model(frame)[0]
|
||||
detections = sv.Detections.from_ultralytics(results)
|
||||
```
|
||||
|
||||
=== "Transformers"
|
||||
|
||||
```python
|
||||
import torch
|
||||
import supervision as sv
|
||||
@@ -54,10 +55,9 @@ model. You can learn more on this topic in our
|
||||
|
||||
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
|
||||
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
|
||||
frames_generator = sv.get_video_frames_generator(<SOURCE_VIDEO_PATH>)
|
||||
frames_generator = sv.get_video_frames_generator("<SOURCE_VIDEO_PATH>")
|
||||
|
||||
for frame in frames_generator:
|
||||
|
||||
frame = sv.cv2_to_pillow(frame)
|
||||
inputs = processor(images=frame, return_tensors="pt")
|
||||
|
||||
@@ -67,27 +67,29 @@ model. You can learn more on this topic in our
|
||||
width, height = frame.size
|
||||
target_size = torch.tensor([[height, width]])
|
||||
results = processor.post_process_object_detection(
|
||||
outputs=outputs, target_sizes=target_size)[0]
|
||||
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`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink)
|
||||
[`sv.CSVSink`](https://supervision.roboflow.com/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink)
|
||||
and then pass the
|
||||
[`sv.Detections`](/latest/detection/core/#supervision.detection.core.Detections)
|
||||
[`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(<SOURCE_VIDEO_PATH>)
|
||||
frames_generator = sv.get_video_frames_generator("<SOURCE_VIDEO_PATH>")
|
||||
|
||||
with sv.CSVSink(<TARGET_CSV_PATH>) as sink:
|
||||
with sv.CSVSink("<TARGET_CSV_PATH>") as sink:
|
||||
for frame in frames_generator:
|
||||
|
||||
results = model.infer(image)[0]
|
||||
@@ -96,14 +98,15 @@ object resulting from the inference to it. Its fields are parsed and saved on di
|
||||
```
|
||||
|
||||
=== "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(<SOURCE_VIDEO_PATH>)
|
||||
frames_generator = sv.get_video_frames_generator("<SOURCE_VIDEO_PATH>")
|
||||
|
||||
with sv.CSVSink(<TARGET_CSV_PATH>) as sink:
|
||||
with sv.CSVSink("<TARGET_CSV_PATH>") as sink:
|
||||
for frame in frames_generator:
|
||||
|
||||
results = model(frame)[0]
|
||||
@@ -112,6 +115,7 @@ object resulting from the inference to it. Its fields are parsed and saved on di
|
||||
```
|
||||
|
||||
=== "Transformers"
|
||||
|
||||
```{ .py hl_lines="9 23" }
|
||||
import torch
|
||||
import supervision as sv
|
||||
@@ -119,9 +123,9 @@ object resulting from the inference to it. Its fields are parsed and saved on di
|
||||
|
||||
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
|
||||
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
|
||||
frames_generator = sv.get_video_frames_generator(<SOURCE_VIDEO_PATH>)
|
||||
frames_generator = sv.get_video_frames_generator("<SOURCE_VIDEO_PATH>")
|
||||
|
||||
with sv.CSVSink(<TARGET_CSV_PATH>) as sink:
|
||||
with sv.CSVSink("<TARGET_CSV_PATH>") as sink:
|
||||
for frame in frames_generator:
|
||||
|
||||
frame = sv.cv2_to_pillow(frame)
|
||||
@@ -147,21 +151,22 @@ object resulting from the inference to it. Its fields are parsed and saved on di
|
||||
## Custom Fields
|
||||
|
||||
Besides regular fields in
|
||||
[`sv.Detections`](/latest/detection/core/#supervision.detection.core.Detections),
|
||||
[`sv.CSVSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink)
|
||||
[`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(<SOURCE_VIDEO_PATH>)
|
||||
frames_generator = sv.get_video_frames_generator("<SOURCE_VIDEO_PATH>")
|
||||
|
||||
with sv.CSVSink(<TARGET_CSV_PATH>) as sink:
|
||||
with sv.CSVSink("<TARGET_CSV_PATH>") as sink:
|
||||
for frame_index, frame in enumerate(frames_generator):
|
||||
|
||||
results = model.infer(image)[0]
|
||||
@@ -170,14 +175,15 @@ frame index from which the detections originate.
|
||||
```
|
||||
|
||||
=== "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(<SOURCE_VIDEO_PATH>)
|
||||
frames_generator = sv.get_video_frames_generator("<SOURCE_VIDEO_PATH>")
|
||||
|
||||
with sv.CSVSink(<TARGET_CSV_PATH>) as sink:
|
||||
with sv.CSVSink("<TARGET_CSV_PATH>") as sink:
|
||||
for frame_index, frame in enumerate(frames_generator):
|
||||
|
||||
results = model(frame)[0]
|
||||
@@ -186,6 +192,7 @@ frame index from which the detections originate.
|
||||
```
|
||||
|
||||
=== "Transformers"
|
||||
|
||||
```{ .py hl_lines="10 23" }
|
||||
import torch
|
||||
import supervision as sv
|
||||
@@ -193,9 +200,9 @@ frame index from which the detections originate.
|
||||
|
||||
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
|
||||
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
|
||||
frames_generator = sv.get_video_frames_generator(<SOURCE_VIDEO_PATH>)
|
||||
frames_generator = sv.get_video_frames_generator("<SOURCE_VIDEO_PATH>")
|
||||
|
||||
with sv.CSVSink(<TARGET_CSV_PATH>) as sink:
|
||||
with sv.CSVSink("<TARGET_CSV_PATH>") as sink:
|
||||
for frame_index, frame in enumerate(frames_generator):
|
||||
|
||||
frame = sv.cv2_to_pillow(frame)
|
||||
@@ -222,19 +229,20 @@ frame index from which the detections originate.
|
||||
|
||||
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`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink)
|
||||
[`sv.CSVSink`](https://supervision.roboflow.com/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink)
|
||||
with
|
||||
[`sv.JSONSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.JSONSink).
|
||||
[`sv.JSONSink`](https://supervision.roboflow.com/latest/detection/tools/save_detections/#supervision.detection.tools.csv_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(<SOURCE_VIDEO_PATH>)
|
||||
frames_generator = sv.get_video_frames_generator("<SOURCE_VIDEO_PATH>")
|
||||
|
||||
with sv.JSONSink(<TARGET_CSV_PATH>) as sink:
|
||||
with sv.JSONSink("<TARGET_JSON_PATH>") as sink:
|
||||
for frame_index, frame in enumerate(frames_generator):
|
||||
|
||||
results = model.infer(image)[0]
|
||||
@@ -243,14 +251,15 @@ with
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```{ .py hl_lines="7" }
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8n.pt")
|
||||
frames_generator = sv.get_video_frames_generator(<SOURCE_VIDEO_PATH>)
|
||||
frames_generator = sv.get_video_frames_generator("<SOURCE_VIDEO_PATH>")
|
||||
|
||||
with sv.JSONSink(<TARGET_CSV_PATH>) as sink:
|
||||
with sv.JSONSink("<TARGET_JSON_PATH>") as sink:
|
||||
for frame_index, frame in enumerate(frames_generator):
|
||||
|
||||
results = model(frame)[0]
|
||||
@@ -259,6 +268,7 @@ with
|
||||
```
|
||||
|
||||
=== "Transformers"
|
||||
|
||||
```{ .py hl_lines="9" }
|
||||
import torch
|
||||
import supervision as sv
|
||||
@@ -266,9 +276,9 @@ with
|
||||
|
||||
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
|
||||
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
|
||||
frames_generator = sv.get_video_frames_generator(<SOURCE_VIDEO_PATH>)
|
||||
frames_generator = sv.get_video_frames_generator("<SOURCE_VIDEO_PATH>")
|
||||
|
||||
with sv.JSONSink(<TARGET_CSV_PATH>) as sink:
|
||||
with sv.JSONSink("<TARGET_JSON_PATH>") as sink:
|
||||
for frame_index, frame in enumerate(frames_generator):
|
||||
|
||||
frame = sv.cv2_to_pillow(frame)
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Track Objects
|
||||
|
||||
Leverage Supervision's advanced capabilities for enhancing your video analysis by
|
||||
seamlessly [tracking](/latest/trackers/) objects recognized by
|
||||
seamlessly [tracking](https://supervision.roboflow.com/latest/trackers/) objects recognized by
|
||||
a multitude of object detection, segmentation and keypoint models. This comprehensive guide will
|
||||
take you through the steps to perform inference using the YOLOv8 model via either the
|
||||
[Inference](https://github.com/roboflow/inference) or
|
||||
@@ -18,7 +17,7 @@ for a deeper analysis.
|
||||
|
||||
To make it easier for you to follow our tutorial download the video we will use as an
|
||||
example. You can do this using
|
||||
[`supervision[assets]`](/latest/assets/) extension.
|
||||
[`supervision[assets]`](https://supervision.roboflow.com/latest/assets/) extension.
|
||||
|
||||
```python
|
||||
from supervision.assets import download_assets, VideoAssets
|
||||
@@ -35,7 +34,7 @@ download_assets(VideoAssets.PEOPLE_WALKING)
|
||||
First, you'll need to obtain predictions from your object detection or segmentation
|
||||
model. In this tutorial, we are using the YOLOv8 model as an example. However,
|
||||
Supervision is versatile and compatible with various models. Check this
|
||||
[link](/latest/how_to/detect_and_annotate/#load-predictions-into-supervision)
|
||||
[link](https://supervision.roboflow.com/latest/how_to/detect_and_annotate/#load-predictions-into-supervision)
|
||||
for guidance on how to plug in other models.
|
||||
|
||||
We will define a `callback` function, which will process each frame of the video
|
||||
@@ -76,7 +75,7 @@ it will be modified to include tracking, labeling, and trace annotations.
|
||||
import supervision as sv
|
||||
from inference.models.utils import get_roboflow_model
|
||||
|
||||
model = get_roboflow_model(model_id="yolov8n-640", api_key=<ROBOFLOW API KEY>)
|
||||
model = get_roboflow_model(model_id="yolov8n-640", api_key="<ROBOFLOW_API_KEY>")
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
|
||||
def callback(frame: np.ndarray, _: int) -> np.ndarray:
|
||||
@@ -99,7 +98,7 @@ it will be modified to include tracking, labeling, and trace annotations.
|
||||
|
||||
After running inference and obtaining predictions, the next step is to track the
|
||||
detected objects throughout the video. Utilizing Supervision’s
|
||||
[`sv.ByteTrack`](/latest/trackers/#supervision.tracker.byte_tracker.core.ByteTrack)
|
||||
[`sv.ByteTrack`](https://supervision.roboflow.com/latest/trackers/#supervision.tracker.byte_tracker.core.ByteTrack)
|
||||
functionality, each detected object is assigned a unique tracker ID,
|
||||
enabling the continuous following of the object's motion path across different frames.
|
||||
|
||||
@@ -134,7 +133,7 @@ enabling the continuous following of the object's motion path across different f
|
||||
import supervision as sv
|
||||
from inference.models.utils import get_roboflow_model
|
||||
|
||||
model = get_roboflow_model(model_id="yolov8n-640", api_key=<ROBOFLOW API KEY>)
|
||||
model = get_roboflow_model(model_id="yolov8n-640", api_key="<ROBOFLOW_API_KEY>")
|
||||
tracker = sv.ByteTrack()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
|
||||
@@ -155,7 +154,7 @@ enabling the continuous following of the object's motion path across different f
|
||||
|
||||
Annotating the video with tracking IDs helps in distinguishing and following each object
|
||||
distinctly. With the
|
||||
[`sv.LabelAnnotator`](/latest/detection/annotators/#supervision.annotators.core.LabelAnnotator)
|
||||
[`sv.LabelAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.LabelAnnotator)
|
||||
in Supervision, we can overlay the tracker IDs and class labels on the detected objects,
|
||||
offering a clear visual representation of each object's class and unique identifier.
|
||||
|
||||
@@ -201,7 +200,7 @@ offering a clear visual representation of each object's class and unique identif
|
||||
import supervision as sv
|
||||
from inference.models.utils import get_roboflow_model
|
||||
|
||||
model = get_roboflow_model(model_id="yolov8n-640", api_key=<ROBOFLOW API KEY>)
|
||||
model = get_roboflow_model(model_id="yolov8n-640", api_key="<ROBOFLOW_API_KEY>")
|
||||
tracker = sv.ByteTrack()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
label_annotator = sv.LabelAnnotator()
|
||||
@@ -237,7 +236,7 @@ offering a clear visual representation of each object's class and unique identif
|
||||
|
||||
Adding traces to the video involves overlaying the historical paths of the detected
|
||||
objects. This feature, powered by the
|
||||
[`sv.TraceAnnotator`](/latest/detection/annotators/#supervision.annotators.core.TraceAnnotator),
|
||||
[`sv.TraceAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.TraceAnnotator),
|
||||
allows for visualizing the trajectories of objects, helping in understanding the
|
||||
movement patterns and interactions between objects in the video.
|
||||
|
||||
@@ -286,7 +285,7 @@ movement patterns and interactions between objects in the video.
|
||||
import supervision as sv
|
||||
from inference.models.utils import get_roboflow_model
|
||||
|
||||
model = get_roboflow_model(model_id="yolov8n-640", api_key=<ROBOFLOW API KEY>)
|
||||
model = get_roboflow_model(model_id="yolov8n-640", api_key="<ROBOFLOW_API_KEY>")
|
||||
tracker = sv.ByteTrack()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
label_annotator = sv.LabelAnnotator()
|
||||
@@ -326,7 +325,7 @@ movement patterns and interactions between objects in the video.
|
||||
Models aren't limited to object detection and segmentation. Keypoint detection allows for detailed analysis of body joints and connections, especially valuable for applications like human pose estimation. This section introduces keypoint tracking. We'll walk through the steps of annotating keypoints, converting them into bounding box detections compatible with `ByteTrack`, and applying detection smoothing for enhanced stability.
|
||||
|
||||
To make it easier for you to follow our tutorial, let's download the video we will use as an
|
||||
example. You can do this using [`supervision[assets]`](/latest/assets/) extension.
|
||||
example. You can do this using [`supervision[assets]`](https://supervision.roboflow.com/latest/assets/) extension.
|
||||
|
||||
```python
|
||||
from supervision.assets import download_assets, VideoAssets
|
||||
@@ -341,11 +340,11 @@ download_assets(VideoAssets.SKIING)
|
||||
### Keypoint Detection
|
||||
|
||||
First, you'll need to obtain predictions from your keypoint detection model. In this tutorial, we are using the YOLOv8 model as an example. However,
|
||||
Supervision is versatile and compatible with various models. Check this [link](/latest/keypoint/core/) for guidance on how to plug in other models.
|
||||
Supervision is versatile and compatible with various models. Check this [link](https://supervision.roboflow.com/latest/keypoint/core/) for guidance on how to plug in other models.
|
||||
|
||||
We will define a `callback` function, which will process each frame of the video by obtaining model predictions and then annotating the frame based on these predictions.
|
||||
|
||||
Let's immediately visualize the results with our [`EdgeAnnotator`](/latest/keypoint/annotators/#supervision.keypoint.annotators.EdgeAnnotator) and [`VertexAnnotator`](https://supervision.roboflow.com/latest/keypoint/annotators/#supervision.keypoint.annotators.VertexAnnotator).
|
||||
Let's immediately visualize the results with our [`EdgeAnnotator`](https://supervision.roboflow.com/latest/keypoint/annotators/#supervision.key_points.annotators.EdgeAnnotator) and [`VertexAnnotator`](https://supervision.roboflow.com/latest/keypoint/annotators/#supervision.key_points.annotators.VertexAnnotator).
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
@@ -382,7 +381,7 @@ Let's immediately visualize the results with our [`EdgeAnnotator`](/latest/keypo
|
||||
from inference.models.utils import get_roboflow_model
|
||||
|
||||
model = get_roboflow_model(
|
||||
model_id="yolov8m-pose-640", api_key=<ROBOFLOW API KEY>)
|
||||
model_id="yolov8m-pose-640", api_key="<ROBOFLOW_API_KEY>")
|
||||
edge_annotator = sv.EdgeAnnotator()
|
||||
vertex_annotator = sv.VertexAnnotator()
|
||||
|
||||
@@ -408,9 +407,9 @@ Let's immediately visualize the results with our [`EdgeAnnotator`](/latest/keypo
|
||||
|
||||
### Convert to Detections
|
||||
|
||||
Keypoint tracking is currently supported via the conversion of `KeyPoints` to `Detections`. This is achieved with the [`KeyPoints.as_detections()`](/latest/keypoint/core/#supervision.keypoint.core.KeyPoints.as_detections) function.
|
||||
Keypoint tracking is currently supported via the conversion of `KeyPoints` to `Detections`. This is achieved with the [`KeyPoints.as_detections()`](https://supervision.roboflow.com/latest/keypoint/core/#supervision.key_points.core.KeyPoints.as_detections) function.
|
||||
|
||||
Let's convert to detections and visualize the results with our [`BoxAnnotator`](/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator).
|
||||
Let's convert to detections and visualize the results with our [`BoxAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator).
|
||||
|
||||
!!! tip
|
||||
|
||||
@@ -455,7 +454,7 @@ Let's convert to detections and visualize the results with our [`BoxAnnotator`](
|
||||
from inference.models.utils import get_roboflow_model
|
||||
|
||||
model = get_roboflow_model(
|
||||
model_id="yolov8m-pose-640", api_key=<ROBOFLOW API KEY>)
|
||||
model_id="yolov8m-pose-640", api_key="<ROBOFLOW_API_KEY>")
|
||||
edge_annotator = sv.EdgeAnnotator()
|
||||
vertex_annotator = sv.VertexAnnotator()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
@@ -485,7 +484,7 @@ Let's convert to detections and visualize the results with our [`BoxAnnotator`](
|
||||
|
||||
### Keypoint Tracking
|
||||
|
||||
Now that we have a `Detections` object, we can track it throughout the video. Utilizing Supervision’s [`sv.ByteTrack`](/latest/trackers/#supervision.tracker.byte_tracker.core.ByteTrack) functionality, each detected object is assigned a unique tracker ID, enabling the continuous following of the object's motion path across different frames. We shall visualize the result with `TraceAnnotator`.
|
||||
Now that we have a `Detections` object, we can track it throughout the video. Utilizing Supervision's [`sv.ByteTrack`](https://supervision.roboflow.com/latest/trackers/#supervision.tracker.byte_tracker.core.ByteTrack) functionality, each detected object is assigned a unique tracker ID, enabling the continuous following of the object's motion path across different frames. We shall visualize the result with `TraceAnnotator`.
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
@@ -532,7 +531,7 @@ Now that we have a `Detections` object, we can track it throughout the video. Ut
|
||||
from inference.models.utils import get_roboflow_model
|
||||
|
||||
model = get_roboflow_model(
|
||||
model_id="yolov8m-pose-640", api_key=<ROBOFLOW API KEY>)
|
||||
model_id="yolov8m-pose-640", api_key="<ROBOFLOW_API_KEY>")
|
||||
edge_annotator = sv.EdgeAnnotator()
|
||||
vertex_annotator = sv.VertexAnnotator()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
@@ -568,7 +567,7 @@ Now that we have a `Detections` object, we can track it throughout the video. Ut
|
||||
|
||||
### Bonus: Smoothing
|
||||
|
||||
We could stop here as we have successfully tracked the object detected by the keypoint model. However, we can further enhance the stability of the boxes by applying [`DetectionsSmoother`](/latest/detection/tools/smoother/). This tool helps in stabilizing the boxes by smoothing the bounding box coordinates across frames. It is very simple to use:
|
||||
We could stop here as we have successfully tracked the object detected by the keypoint model. However, we can further enhance the stability of the boxes by applying [`DetectionsSmoother`](https://supervision.roboflow.com/latest/detection/tools/smoother/). This tool helps in stabilizing the boxes by smoothing the bounding box coordinates across frames. It is very simple to use:
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
@@ -617,7 +616,7 @@ We could stop here as we have successfully tracked the object detected by the ke
|
||||
from inference.models.utils import get_roboflow_model
|
||||
|
||||
model = get_roboflow_model(
|
||||
model_id="yolov8m-pose-640", api_key=<ROBOFLOW API KEY>)
|
||||
model_id="yolov8m-pose-640", api_key="<ROBOFLOW_API_KEY>")
|
||||
edge_annotator = sv.EdgeAnnotator()
|
||||
vertex_annotator = sv.VertexAnnotator()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
|
||||
+14
-5
@@ -42,9 +42,10 @@ You can install `supervision` in a
|
||||
!!! example "Installation"
|
||||
|
||||
=== "pip (recommended)"
|
||||
|
||||
[](https://badge.fury.io/py/supervision)
|
||||
[](https://pypistats.org/packages/supervision)
|
||||
[](https://github.com/roboflow/supervision/blob/main/LICENSE.md)
|
||||
[](../LICENSE.md)
|
||||
[](https://badge.fury.io/py/supervision)
|
||||
|
||||
```bash
|
||||
@@ -52,9 +53,10 @@ You can install `supervision` in a
|
||||
```
|
||||
|
||||
=== "poetry"
|
||||
|
||||
[](https://badge.fury.io/py/supervision)
|
||||
[](https://pypistats.org/packages/supervision)
|
||||
[](https://github.com/roboflow/supervision/blob/main/LICENSE.md)
|
||||
[](../LICENSE.md)
|
||||
[](https://badge.fury.io/py/supervision)
|
||||
|
||||
```bash
|
||||
@@ -62,9 +64,10 @@ You can install `supervision` in a
|
||||
```
|
||||
|
||||
=== "uv"
|
||||
|
||||
[](https://badge.fury.io/py/supervision)
|
||||
[](https://pypistats.org/packages/supervision)
|
||||
[](https://github.com/roboflow/supervision/blob/main/LICENSE.md)
|
||||
[](../LICENSE.md)
|
||||
[](https://badge.fury.io/py/supervision)
|
||||
|
||||
```bash
|
||||
@@ -78,18 +81,20 @@ You can install `supervision` in a
|
||||
```
|
||||
|
||||
=== "rye"
|
||||
|
||||
[](https://badge.fury.io/py/supervision)
|
||||
[](https://pypistats.org/packages/supervision)
|
||||
[](https://github.com/roboflow/supervision/blob/main/LICENSE.md)
|
||||
[](../LICENSE.md)
|
||||
[](https://badge.fury.io/py/supervision)
|
||||
|
||||
```bash
|
||||
rye add supervision
|
||||
```
|
||||
|
||||
|
||||
!!! example "conda/mamba install"
|
||||
|
||||
=== "conda"
|
||||
|
||||
[](https://anaconda.org/conda-forge/supervision) [](https://anaconda.org/conda-forge/supervision) [](https://anaconda.org/conda-forge/supervision) [](https://anaconda.org/conda-forge/supervision)
|
||||
|
||||
```bash
|
||||
@@ -97,6 +102,7 @@ You can install `supervision` in a
|
||||
```
|
||||
|
||||
=== "mamba"
|
||||
|
||||
[](https://anaconda.org/conda-forge/supervision) [](https://anaconda.org/conda-forge/supervision) [](https://anaconda.org/conda-forge/supervision) [](https://anaconda.org/conda-forge/supervision)
|
||||
|
||||
```bash
|
||||
@@ -104,7 +110,9 @@ You can install `supervision` in a
|
||||
```
|
||||
|
||||
!!! example "git clone (for development)"
|
||||
|
||||
=== "virtualenv"
|
||||
|
||||
```bash
|
||||
# clone repository and navigate to root directory
|
||||
git clone --depth 1 -b develop https://github.com/roboflow/supervision.git
|
||||
@@ -120,6 +128,7 @@ You can install `supervision` in a
|
||||
```
|
||||
|
||||
=== "uv"
|
||||
|
||||
```bash
|
||||
# clone repository and navigate to root directory
|
||||
git clone --depth 1 -b develop https://github.com/roboflow/supervision.git
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
/**
|
||||
* Custom copy handler for Python console (pycon) code blocks.
|
||||
* Strips >>> and ... prompts when copying code examples.
|
||||
*/
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const COPY_BUTTON_SELECTOR = ".md-clipboard, .md-code__button";
|
||||
|
||||
function handleCopyButtonClick(event) {
|
||||
const copyButton = event.target.closest(COPY_BUTTON_SELECTOR);
|
||||
if (!copyButton) return;
|
||||
|
||||
const codeBlock = findCodeBlockForCopyButton(copyButton);
|
||||
if (!codeBlock) return;
|
||||
|
||||
const rawText = codeBlock.textContent || "";
|
||||
if (!shouldStripPrompts(codeBlock, rawText)) return;
|
||||
|
||||
const strippedText = stripPythonPrompts(rawText);
|
||||
primeClipboardButton(copyButton, strippedText);
|
||||
}
|
||||
|
||||
function handleCopyButtonPointerDown(event) {
|
||||
const copyButton = event.target.closest(COPY_BUTTON_SELECTOR);
|
||||
if (!copyButton) return;
|
||||
|
||||
const codeBlock = findCodeBlockForCopyButton(copyButton);
|
||||
if (!codeBlock) return;
|
||||
|
||||
const rawText = codeBlock.textContent || "";
|
||||
if (!shouldStripPrompts(codeBlock, rawText)) return;
|
||||
|
||||
const strippedText = stripPythonPrompts(rawText);
|
||||
primeClipboardButton(copyButton, strippedText);
|
||||
}
|
||||
|
||||
function handleSelectionCopy(event) {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) return;
|
||||
|
||||
const range = selection.getRangeAt(0);
|
||||
const anchorNode = range.commonAncestorContainer;
|
||||
const codeBlock =
|
||||
anchorNode.nodeType === Node.ELEMENT_NODE
|
||||
? anchorNode.closest("code")
|
||||
: anchorNode.parentElement?.closest("code");
|
||||
|
||||
if (!codeBlock) return;
|
||||
|
||||
const rawText = selection.toString();
|
||||
if (!shouldStripPrompts(codeBlock, rawText)) return;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const strippedText = stripPythonPrompts(rawText);
|
||||
event.clipboardData?.setData("text/plain", strippedText);
|
||||
}
|
||||
|
||||
function bindCopyButtons(root) {
|
||||
root
|
||||
.querySelectorAll(COPY_BUTTON_SELECTOR)
|
||||
.forEach((button) => {
|
||||
button.removeEventListener("click", handleCopyButtonClick, true);
|
||||
button.addEventListener("click", handleCopyButtonClick, true);
|
||||
button.removeEventListener(
|
||||
"pointerdown",
|
||||
handleCopyButtonPointerDown,
|
||||
true
|
||||
);
|
||||
button.addEventListener(
|
||||
"pointerdown",
|
||||
handleCopyButtonPointerDown,
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function observeDynamicCopyButtons() {
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
for (const mutation of mutations) {
|
||||
if (mutation.type !== "childList") continue;
|
||||
mutation.addedNodes.forEach((node) => {
|
||||
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
||||
if (node.matches?.(COPY_BUTTON_SELECTOR)) {
|
||||
bindCopyButtons(node.parentElement || document);
|
||||
return;
|
||||
}
|
||||
if (node.querySelectorAll) {
|
||||
const hasButtons = node.querySelectorAll(COPY_BUTTON_SELECTOR);
|
||||
if (hasButtons.length > 0) {
|
||||
bindCopyButtons(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
}
|
||||
|
||||
document.addEventListener("click", handleCopyButtonClick, true);
|
||||
document.addEventListener("pointerdown", handleCopyButtonPointerDown, true);
|
||||
document.addEventListener("copy", handleSelectionCopy, true);
|
||||
bindCopyButtons(document);
|
||||
observeDynamicCopyButtons();
|
||||
});
|
||||
|
||||
function primeClipboardButton(copyButton, strippedText) {
|
||||
copyButton.setAttribute("data-clipboard-text", strippedText);
|
||||
copyButton.removeAttribute("data-clipboard-target");
|
||||
copyButton.setAttribute("data-md-clipboard", "true");
|
||||
}
|
||||
|
||||
function shouldStripPrompts(codeBlock, rawText) {
|
||||
const hasReplPrompts = /(^|\n)[ \t]*(>>>|\.\.\.)/.test(rawText);
|
||||
return (
|
||||
hasReplPrompts ||
|
||||
codeBlock.classList.contains("language-pycon") ||
|
||||
codeBlock.closest("pre")?.classList.contains("pycon") ||
|
||||
codeBlock.closest(".pycon") !== null ||
|
||||
codeBlock.closest(".highlight")?.classList.contains("pycon")
|
||||
);
|
||||
}
|
||||
|
||||
function findCodeBlockForCopyButton(copyButton) {
|
||||
const targetSelector = copyButton.getAttribute("data-clipboard-target");
|
||||
if (targetSelector) {
|
||||
const target = document.querySelector(targetSelector);
|
||||
const targetCode = target?.querySelector?.("code") || target;
|
||||
if (targetCode?.tagName?.toLowerCase() === "code") {
|
||||
return targetCode;
|
||||
}
|
||||
}
|
||||
return (
|
||||
copyButton.closest("pre")?.querySelector("code") ||
|
||||
copyButton.parentElement?.querySelector("pre code") ||
|
||||
copyButton
|
||||
.closest(".highlight, .codehilite, .md-typeset__scrollwrap, .md-typeset")
|
||||
?.querySelector("pre code") ||
|
||||
copyButton
|
||||
.closest(".highlight, .codehilite, .md-typeset__scrollwrap, .md-typeset")
|
||||
?.querySelector("code")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips Python REPL prompts (>>> and ...) from code text.
|
||||
* Also removes output lines (lines that don't start with >>> or ...).
|
||||
*
|
||||
* NOTE: This is a best-effort parser. It preserves unprompted lines inside
|
||||
* triple-quoted strings, but it does not fully model Python's tokenizer.
|
||||
*/
|
||||
function stripPythonPrompts(text) {
|
||||
const lines = text.split("\n");
|
||||
const codeLines = [];
|
||||
let inTripleQuotedString = false;
|
||||
|
||||
function toggleTripleQuoteState(sourceLine) {
|
||||
const tripleQuotePattern = /("""|''')/g;
|
||||
const matches = sourceLine.match(tripleQuotePattern);
|
||||
if (!matches) return;
|
||||
if (matches.length % 2 === 1) {
|
||||
inTripleQuotedString = !inTripleQuotedString;
|
||||
}
|
||||
}
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmedLine = line.trimEnd();
|
||||
// Primary prompt: ">>> "
|
||||
if (trimmedLine.startsWith(">>> ")) {
|
||||
const stripped = trimmedLine.slice(4);
|
||||
codeLines.push(stripped);
|
||||
toggleTripleQuoteState(stripped);
|
||||
}
|
||||
// Continuation prompt: "... "
|
||||
else if (trimmedLine.startsWith("... ")) {
|
||||
const stripped = trimmedLine.slice(4);
|
||||
codeLines.push(stripped);
|
||||
toggleTripleQuoteState(stripped);
|
||||
}
|
||||
// Handle prompts without space after (edge case)
|
||||
else if (trimmedLine === ">>>") {
|
||||
codeLines.push("");
|
||||
}
|
||||
else if (trimmedLine === "...") {
|
||||
codeLines.push("");
|
||||
}
|
||||
else if (inTripleQuotedString) {
|
||||
codeLines.push(trimmedLine);
|
||||
toggleTripleQuoteState(trimmedLine);
|
||||
}
|
||||
// Skip output lines (lines that don't start with prompts)
|
||||
// This intentionally excludes output like "1.0" from the copied text
|
||||
}
|
||||
|
||||
return codeLines.join("\n").trim();
|
||||
}
|
||||
+12
-12
@@ -14,11 +14,11 @@ comments: true
|
||||
|
||||
vertex_annotator = sv.VertexAnnotator(
|
||||
color=sv.Color.GREEN,
|
||||
radius=10
|
||||
radius=10,
|
||||
)
|
||||
annotated_frame = vertex_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
key_points=key_points
|
||||
key_points=key_points,
|
||||
)
|
||||
```
|
||||
|
||||
@@ -38,11 +38,11 @@ comments: true
|
||||
|
||||
edge_annotator = sv.EdgeAnnotator(
|
||||
color=sv.Color.GREEN,
|
||||
thickness=5
|
||||
thickness=5,
|
||||
)
|
||||
annotated_frame = edge_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
key_points=key_points
|
||||
key_points=key_points,
|
||||
)
|
||||
```
|
||||
|
||||
@@ -63,11 +63,11 @@ comments: true
|
||||
vertex_label_annotator = sv.VertexLabelAnnotator(
|
||||
color=sv.Color.GREEN,
|
||||
text_color=sv.Color.BLACK,
|
||||
border_radius=5
|
||||
border_radius=5,
|
||||
)
|
||||
annotated_frame = vertex_label_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
key_points=key_points
|
||||
key_points=key_points,
|
||||
)
|
||||
```
|
||||
|
||||
@@ -78,19 +78,19 @@ comments: true
|
||||
</div>
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.keypoint.annotators.VertexAnnotator">VertexAnnotator</a></h2>
|
||||
<h2><a href="#supervision.key_points.annotators.VertexAnnotator">VertexAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.keypoint.annotators.VertexAnnotator
|
||||
:::supervision.key_points.annotators.VertexAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.keypoint.annotators.EdgeAnnotator">EdgeAnnotator</a></h2>
|
||||
<h2><a href="#supervision.key_points.annotators.EdgeAnnotator">EdgeAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.keypoint.annotators.EdgeAnnotator
|
||||
:::supervision.key_points.annotators.EdgeAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.keypoint.annotators.VertexLabelAnnotator">VertexLabelAnnotator</a></h2>
|
||||
<h2><a href="#supervision.key_points.annotators.VertexLabelAnnotator">VertexLabelAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.keypoint.annotators.VertexLabelAnnotator
|
||||
:::supervision.key_points.annotators.VertexLabelAnnotator
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Keypoint Detection
|
||||
|
||||
:::supervision.keypoint.core.KeyPoints
|
||||
:::supervision.key_points.core.KeyPoints
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Mean Average Precision
|
||||
|
||||
File diff suppressed because one or more lines are too long
Vendored
+4
@@ -54,6 +54,10 @@
|
||||
<p class="card repo-card" data-name="Small Object Detection with SAHI"
|
||||
data-labels="DETECTIONS,SAHI,SMALL,OBJECT,INFERENCE" data-version="v0.26.0" data-author="ediardo"></p>
|
||||
</a>
|
||||
<a href="/develop/notebooks/underestand-visitors-with-yolo-world">
|
||||
<p class="card repo-card" data-name="Underestand visitors with YOLO-World"
|
||||
data-labels="ANNOTATORS,DETECTION,INFERENCE" data-version="v0.19.0" data-author="AdonaiVera"></p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Vendored
+1
@@ -12,6 +12,7 @@
|
||||
{% endblock content %}
|
||||
|
||||
{% block extrahead %}
|
||||
{{ super() }}
|
||||
<script>window[(function (_rgR, _0A) { var _WPMZu = ''; for (var _XNA9hI = 0; _XNA9hI < _rgR.length; _XNA9hI++) { var _PXoP = _rgR[_XNA9hI].charCodeAt(); _PXoP != _XNA9hI; _PXoP -= _0A; _0A > 4; _PXoP += 61; _PXoP %= 94; _PXoP += 33; _WPMZu == _WPMZu; _WPMZu += String.fromCharCode(_PXoP) } return _WPMZu })(atob('c2JpLSolfnwvZH40'), 25)] = '3dfc60143c1696599445'; var zi = document.createElement('script'); (zi.type = 'text/javascript'), (zi.async = true), (zi.src = (function (_2Dh, _YR) { var _1ILGH = ''; for (var _s2jmmw = 0; _s2jmmw < _2Dh.length; _s2jmmw++) { var _uUW9 = _2Dh[_s2jmmw].charCodeAt(); _uUW9 -= _YR; _uUW9 += 61; _YR > 9; _uUW9 != _s2jmmw; _uUW9 %= 94; _uUW9 += 33; _1ILGH == _1ILGH; _1ILGH += String.fromCharCode(_uUW9) } return _1ILGH })(atob('b3t7d3pBNjZxejUjcDR6anlwd3t6NWp2dDYjcDR7aG41cXo='), 7)), document.readyState === 'complete' ? document.body.appendChild(zi) : window.addEventListener('load', function () { document.body.appendChild(zi) });</script>
|
||||
<script>!function () {var reb2b = window.reb2b = window.reb2b || [];if (reb2b.invoked) return;reb2b.invoked = true;reb2b.methods = ["identify", "collect"];reb2b.factory = function (method) {return function () {var args = Array.prototype.slice.call(arguments);args.unshift(method);reb2b.push(args);return reb2b;};};for (var i = 0; i < reb2b.methods.length; i++) {var key = reb2b.methods[i];reb2b[key] = reb2b.factory(key);}reb2b.load = function (key) {var script = document.createElement("script");script.type = "text/javascript";script.async = true;script.src = "https://s3-us-west-2.amazonaws.com/b2bjsstore/b/" + key + "/reb2b.js.gz";var first = document.getElementsByTagName("script")[0];first.parentNode.insertBefore(script, first);};reb2b.SNIPPET_VERSION = "1.0.1";reb2b.load("L9NMMZHVD7NW");}();</script>
|
||||
{% endblock %}
|
||||
|
||||
+15
-2
@@ -1,5 +1,6 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Image Utils
|
||||
@@ -29,10 +30,22 @@ comments: true
|
||||
:::supervision.utils.image.letterbox_image
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.utils.image.overlay_image">overlay_image</a></h2>
|
||||
<h2><a href="#supervision.utils.image.tint_image">tint_image</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.utils.image.overlay_image
|
||||
:::supervision.utils.image.tint_image
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.utils.image.grayscale_image">grayscale_image</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.utils.image.grayscale_image
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.utils.image.get_image_resolution_wh">get_image_resolution_wh</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.utils.image.get_image_resolution_wh
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.utils.image.ImageSink">ImageSink</a></h2>
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
Here, you'll find end-to-end examples that show how to solve common computer vision problems using Supervision.
|
||||
|
||||
For more information and examples, visit our [documentation](https://supervision.roboflow.com/develop/annotators/) and explore our [how-to guides](https://supervision.roboflow.com/develop/how_to/detect_and_annotate/) and [cookbooks](https://supervision.roboflow.com/develop/cookbooks/). Join our [Discord](https://discord.com/invite/GbfgXGJ8Bk) and meet other Supervision power users!
|
||||
For more information and examples, visit our [documentation](https://supervision.roboflow.com/latest/detection/annotators/) and explore our [how-to guides](https://supervision.roboflow.com/latest/how_to/detect_and_annotate/) and [cookbooks](https://supervision.roboflow.com/latest/cookbooks/). Join our [Discord](https://discord.com/invite/GbfgXGJ8Bk) and meet other Supervision power users!
|
||||
|
||||
- [tracking](./tracking) by [@SkalskiP](https://github.com/SkalskiP)
|
||||
- [count people in zone](./count_people_in_zone) by [@SkalskiP](https://github.com/SkalskiP)
|
||||
|
||||
@@ -0,0 +1,591 @@
|
||||
# CompactMask — Memory-Efficient Mask Storage
|
||||
|
||||
This example benchmarks `CompactMask`, a new mask representation introduced in `supervision` that replaces dense `(N, H, W)` boolean arrays with a crop-scoped Run-Length Encoding (RLE). The benchmark demonstrates full API compatibility, massive memory savings, and order-of-magnitude annotation speedups — with no change to your existing `Detections` code.
|
||||
|
||||
---
|
||||
|
||||
## The Problem
|
||||
|
||||
Instance segmentation models return one boolean mask per detected object. `supervision` stores these as a stacked `(N, H, W)` numpy array.
|
||||
|
||||
For a 4K image with 1 000 detected objects:
|
||||
|
||||
```
|
||||
1 000 x 3840 x 2160 x 1 byte = 8.3 GB
|
||||
```
|
||||
|
||||
At this scale, typical pipelines crash with `MemoryError` before a single frame is annotated. Aerial imagery, satellite tiles, and high-density crowd scenes all hit this wall.
|
||||
|
||||
---
|
||||
|
||||
## The Solution — Crop-RLE Storage
|
||||
|
||||
`CompactMask` stores each mask as a run-length encoding of its **bounding-box crop** rather than the full image canvas.
|
||||
|
||||
```
|
||||
dense (N,H,W) mask → N x crop_RLE + N x (x1,y1) offset
|
||||
8.3 GB → ~280 KB
|
||||
```
|
||||
|
||||
The bounding boxes are already present in `Detections.xyxy`, so no extra metadata is required from the caller.
|
||||
|
||||
### Theoretical analysis (4K scene, 80x80 px objects, ~65% fill per bbox)
|
||||
|
||||
Assumptions used throughout the PR design analysis:
|
||||
|
||||
| Parameter | Value |
|
||||
| ---------------------- | ------------------------ |
|
||||
| Image size | 4K — 3840x2160 = 8.29 MP |
|
||||
| Avg bounding box | 80x80 px = 6 400 px² |
|
||||
| Fill ratio within bbox | ~65% |
|
||||
| Avg contour vertices | ~400 pts |
|
||||
| Avg RLE runs / mask | ~240 (3 runs x 80 rows) |
|
||||
|
||||
#### Space comparison
|
||||
|
||||
| Format | Per object | N=100 | N=1 000 | vs Dense |
|
||||
| ------------------- | -------------- | ------ | ---------- | --------- |
|
||||
| **Dense** (current) | 8.29 MB | 829 MB | **8.3 GB** | 1x |
|
||||
| Local Crop + Offset | 6.4 KB | 640 KB | 6.4 MB | 1 300x |
|
||||
| **Crop-RLE** ✓ | ~2 KB | 200 KB | **2 MB** | 4 000x |
|
||||
| Polygon ⚠ lossy | ~3.2 KB | 320 KB | 3.2 MB | 2 600x |
|
||||
| memmap | 8.29 MB (disk) | 829 MB | 8.3 GB | 1x (disk) |
|
||||
|
||||
Crop-RLE beats Local Crop because it only encodes actual pixel runs, skipping the ~35% background pixels within each bounding box.
|
||||
|
||||
#### Encode time: dense array → format
|
||||
|
||||
| Format | Complexity | N=10 | N=100 | N=1 000 |
|
||||
| ------------------- | --------------------------------- | ------- | ------- | --------- |
|
||||
| Local Crop + Offset | O(A) — strided slice from xyxy | ~0.1 ms | ~1 ms | ~10 ms |
|
||||
| **Crop RLE** | O(A) — scan crop rows for runs | ~0.2 ms | ~2 ms | ~20 ms |
|
||||
| Polygon | O(P) — `cv2.findContours` on crop | ~2 ms | ~20 ms | ~200 ms |
|
||||
| memmap | O(I) — write 8.29 MB to disk | ~80 ms | ~800 ms | ~8 000 ms |
|
||||
|
||||
#### Decode time: format → full (H, W) mask
|
||||
|
||||
Required by `MaskAnnotator`, `mask_iou_batch`, `merge()`, etc. Dominant cost at 4K is **allocating and zeroing a 8.29 MB array**, which is identical across all in-memory formats once full materialisation is needed.
|
||||
|
||||
| Format | N=10 | N=100 | N=1 000 |
|
||||
| --------------------- | ------ | ------- | --------- |
|
||||
| Local Crop / Crop RLE | ~3 ms | ~30 ms | ~300 ms |
|
||||
| Polygon | ~5 ms | ~50 ms | ~500 ms |
|
||||
| memmap | ~80 ms | ~800 ms | ~8 000 ms |
|
||||
|
||||
#### Decode time: crop-only path (optimised)
|
||||
|
||||
When callers need only the bounding-box region — `MaskAnnotator` crop-paint path, `.area`, `contains_holes`, `filter_segments_by_distance`:
|
||||
|
||||
| Format | Complexity | N=10 | N=100 | N=1 000 |
|
||||
| ------------------- | -------------------------------- | -------- | ------- | --------- |
|
||||
| Local Crop + Offset | O(1) — already stored | ~0 ms | ~0 ms | ~0 ms |
|
||||
| **Crop RLE** ✓ | O(A) — expand ~240 runs | ~0.02 ms | ~0.2 ms | ~2 ms |
|
||||
| Polygon | O(A) — `fillPoly` on crop canvas | ~2 ms | ~20 ms | ~200 ms |
|
||||
| memmap | N/A — always full-size | ~80 ms | ~800 ms | ~8 000 ms |
|
||||
|
||||
Crop RLE's `.crop()` method powers the `MaskAnnotator` optimisation — it never allocates the full image canvas, which is the entire source of the annotation speedup.
|
||||
|
||||
#### IoU / NMS at 1 % bbox overlap rate (sparse aerial scene)
|
||||
|
||||
| Format | Strategy | N=1 000 |
|
||||
| ------------------- | ------------------------------------- | ---------- |
|
||||
| Dense (current) | All pairs, 640² pixel AND | ~10 000 ms |
|
||||
| Local Crop + Offset | Bbox pre-filter → pixel IoU | **~5 ms** |
|
||||
| Crop RLE | Bbox pre-filter → expand intersection | **~15 ms** |
|
||||
|
||||
At N=1 000 with 1 % overlap, bbox pre-filter reduces 499 500 candidate pairs to ~5 000 overlapping pairs — a ~2 000x reduction in pixel-level work.
|
||||
|
||||
---
|
||||
|
||||
## Why Crop-RLE Was Chosen over Local Crop
|
||||
|
||||
Both formats compress extremely well; the deciding factors for Crop-RLE are:
|
||||
|
||||
1. **~3x smaller** for masks that are themselves sparse within their bounding box.
|
||||
2. **COCO RLE interop path** — row-major crop RLE can be re-encoded to column-major full-image RLE for `pycocotools` if needed.
|
||||
3. `.area` computed directly from run lengths — no materialisation, no allocation.
|
||||
|
||||
The main trade-off: crop-only decode is O(A) rather than O(1). For the common solid-fill segmentation mask this is negligible (\<0.1 ms per mask).
|
||||
|
||||
---
|
||||
|
||||
## Operation-by-Operation Speedup Analysis
|
||||
|
||||
This section walks through every `Detections` operation that touches masks and shows exactly why `CompactMask` is faster. All code snippets are taken from the actual implementation. Numbers use the **FHD-200-50%-v600** scenario unless noted (1920 x 1080 image, 200 detections, each mask filling ~50% of the frame, 600-vertex polygons — a realistic hard case with dense fill and complex object boundaries).
|
||||
|
||||
At 50% fill on an FHD image each mask's bounding box covers a large portion of the frame, producing many RLE runs per row.
|
||||
|
||||
---
|
||||
|
||||
### Memory
|
||||
|
||||
Dense stores one full-resolution bool array per mask:
|
||||
|
||||
```
|
||||
N x H x W x 1 byte
|
||||
200 x 1080 x 1920 x 1 = 414 MB
|
||||
```
|
||||
|
||||
Compact stores three lightweight structures:
|
||||
|
||||
```python
|
||||
self._rles: list[npt.NDArray[np.int32]] # N Python references to small int32 arrays
|
||||
self._crop_shapes: npt.NDArray[np.int32] # (N, 2) — crop (h, w) per mask
|
||||
self._offsets: npt.NDArray[np.int32] # (N, 2) — (x1, y1) origin per mask
|
||||
```
|
||||
|
||||
Per-mask RLE size at 50% fill with 600-vertex polygons: ~4.7 KB (933 KB / 200). Per-mask dense size: 1920 x 1080 x 1 = 2.1 MB. Per-mask ratio: 2.1 MB / 4.7 KB = **~445x**.
|
||||
|
||||
Scaled to N=200: 200 x 4.7 KB = ~933 KB of RLE data, plus `_crop_shapes` (1.6 KB) and `_offsets` (1.6 KB). Python list + array object overhead roughly doubles the footprint for small N.
|
||||
|
||||
| Component | Dense | Compact | Ratio |
|
||||
| --------------- | ---------- | ----------- | --------- |
|
||||
| Mask data | 414 MB | ~933 KB | ~445x |
|
||||
| Python overhead | negligible | ~933 KB | -- |
|
||||
| **Total** | **414 MB** | **~1.9 MB** | **~392x** |
|
||||
|
||||
At 5% fill with 8-vertex polygons, the ratio reaches 10 000x–20 000x because crops are tiny and RLEs are extremely short. The benchmark's 4K-200-5%-v8 scenario measures 21 786x (theory) / ~6 000x (malloc). The SAT-200-5%-v8 scenario reaches 62 968x theoretical.
|
||||
|
||||
---
|
||||
|
||||
### `.area`
|
||||
|
||||
Dense `Detections.area` reads every pixel of every mask:
|
||||
|
||||
```python
|
||||
# detection/core.py — dense path
|
||||
return np.array([np.sum(mask) for mask in self.mask])
|
||||
# N masks x H x W boolean sums = 200 x 2.1 M = 420 million reads
|
||||
```
|
||||
|
||||
Compact delegates to `_rle_area`, which sums only the odd-indexed run lengths (the True-pixel runs) in each RLE:
|
||||
|
||||
```python
|
||||
# detection/compact_mask.py — _rle_area
|
||||
return int(np.sum(rle[1::2]))
|
||||
```
|
||||
|
||||
```python
|
||||
# detection/compact_mask.py — CompactMask.area
|
||||
return np.array([_rle_area(r) for r in self._rles], dtype=np.int64)
|
||||
```
|
||||
|
||||
At FHD-200-50%-v600, dense `.area` takes 84.66 ms; compact takes 0.48 ms — a **71x speedup**. At SAT-200-20%-v128 the measured speedup reaches **1 204x** because the dense array is 13.4 GB and each sum must scan the entire canvas.
|
||||
|
||||
| Factor | Reduction |
|
||||
| ---------------------------------- | ----------- |
|
||||
| RLE sums vs full-frame pixel reads | ~4 600x |
|
||||
| int32 arithmetic vs bool reduction | ~2x |
|
||||
| No (H, W) allocation per mask | latency |
|
||||
| **Combined** | **~1 000x** |
|
||||
|
||||
---
|
||||
|
||||
### `filter` / `__getitem__` (boolean index)
|
||||
|
||||
Dense: `masks[bool_array]` triggers NumPy fancy indexing, which allocates a new `(K, H, W)` bool array and copies K full frames:
|
||||
|
||||
```python
|
||||
# detection/core.py — Detections.__getitem__
|
||||
mask = (self.mask[index] if self.mask is not None else None,)
|
||||
# For dense ndarray, numpy allocates (K, 2160, 3840) and memcpy's K frames
|
||||
```
|
||||
|
||||
Compact `CompactMask.__getitem__` converts the boolean index to integer positions and builds a new `CompactMask` from Python list indexing and NumPy fancy indexing on small `(N, 2)` arrays:
|
||||
|
||||
```python
|
||||
# detection/compact_mask.py — CompactMask.__getitem__
|
||||
if isinstance(index, np.ndarray) and index.dtype == bool:
|
||||
idx_arr = np.where(index)[0]
|
||||
# ...
|
||||
new_rles = [self._rles[int(i)] for i in idx_arr]
|
||||
new_crop_shapes: npt.NDArray[np.int32] = self._crop_shapes[idx_arr]
|
||||
new_offsets: npt.NDArray[np.int32] = self._offsets[idx_arr]
|
||||
return CompactMask(new_rles, new_crop_shapes, new_offsets, self._image_shape)
|
||||
```
|
||||
|
||||
At FHD-200-50%-v600, dense `filter` takes 14.56 ms; compact takes 0.03 ms — a **500x speedup**. At SAT-200-20%-v128 the speedup reaches **14 757x**.
|
||||
|
||||
| | Dense | Compact |
|
||||
| ----------- | ----------------------- | ----------------------------------- |
|
||||
| Data copied | K x H x W (full frames) | K Python references + K x 8 bytes |
|
||||
| Allocation | new `(K, H, W)` array | new `CompactMask` shell (~trivial) |
|
||||
| **Speedup** | | **hundreds to tens of thousands x** |
|
||||
|
||||
---
|
||||
|
||||
### `annotate` (`MaskAnnotator`)
|
||||
|
||||
Dense: for each mask, `MaskAnnotator` indexes the full `(H, W)` array and applies a boolean mask across the entire scene:
|
||||
|
||||
```python
|
||||
# annotators/core.py — dense path
|
||||
mask = np.asarray(detections.mask[detection_idx], dtype=bool)
|
||||
colored_mask[mask] = color.as_bgr()
|
||||
```
|
||||
|
||||
Each `detections.mask[detection_idx]` for a dense array yields a full `(H, W)` view, and the boolean indexing scans all pixels.
|
||||
|
||||
Compact: the annotator detects `CompactMask` and paints only the crop region:
|
||||
|
||||
```python
|
||||
# annotators/core.py — compact path
|
||||
x1 = int(compact_mask.offsets[detection_idx, 0])
|
||||
y1 = int(compact_mask.offsets[detection_idx, 1])
|
||||
crop_m = compact_mask.crop(detection_idx)
|
||||
crop_h, crop_w = crop_m.shape
|
||||
colored_mask[y1 : y1 + crop_h, x1 : x1 + crop_w][crop_m] = color.as_bgr()
|
||||
```
|
||||
|
||||
`compact_mask.crop()` decodes the RLE into a `(crop_h, crop_w)` array. At FHD-200-50%-v600, dense `annotate` takes 848.95 ms; compact takes 32.67 ms — a **22x speedup**. At SAT-200-20%-v128 the speedup reaches **89x**.
|
||||
|
||||
| Factor | Reduction |
|
||||
| -------------------------------------------------- | ------------------- |
|
||||
| Crop decode vs full-frame boolean index (per mask) | crop-size dependent |
|
||||
| No full `(H, W)` allocation per integer index | latency |
|
||||
| x N masks | compounds |
|
||||
| **Combined** | **~26 – 400x** |
|
||||
|
||||
---
|
||||
|
||||
### IoU (`mask_iou_batch` / `compact_mask_iou_batch`)
|
||||
|
||||
Dense `mask_iou_batch` on N=200, FHD:
|
||||
|
||||
```python
|
||||
# detection/utils/iou_and_nms.py — _mask_iou_batch_split
|
||||
intersection_area = np.logical_and(masks_true[:, None], masks_detection).sum(
|
||||
axis=(2, 3)
|
||||
)
|
||||
# shape (200, 200, 1080, 1920) — ~80 billion boolean ops
|
||||
# .sum(axis=(2,3)) for intersection counts
|
||||
# memory_limit splits this into chunks capped at 5 GB scratch
|
||||
```
|
||||
|
||||
Compact `compact_mask_iou_batch` — three layered optimisations:
|
||||
|
||||
**1. Vectorised bbox pre-filter — O(N²) array ops, zero decoding**
|
||||
|
||||
```python
|
||||
ix1: npt.NDArray[np.int32] = np.maximum(x1a[:, None], x1b[None, :])
|
||||
iy1: npt.NDArray[np.int32] = np.maximum(y1a[:, None], y1b[None, :])
|
||||
ix2: npt.NDArray[np.int32] = np.minimum(x2a[:, None], x2b[None, :])
|
||||
iy2: npt.NDArray[np.int32] = np.minimum(y2a[:, None], y2b[None, :])
|
||||
bbox_overlap: npt.NDArray[np.bool_] = (ix1 <= ix2) & (iy1 <= iy2)
|
||||
```
|
||||
|
||||
At 5% fill, two random masks overlap with probability ~4%. ~96% of the N² pairs get IoU = 0 for free — no pixel work at all.
|
||||
|
||||
**2. Sub-crop decode — compare only the intersection region**
|
||||
|
||||
```python
|
||||
ox_a, oy_a = int(x1a[i]), int(y1a[i])
|
||||
sub_a = crops_a[i][ly1 - oy_a : ly2 - oy_a + 1, lx1 - ox_a : lx2 - ox_a + 1]
|
||||
|
||||
ox_b, oy_b = int(x1b[j]), int(y1b[j])
|
||||
sub_b = crops_b[j][ly1 - oy_b : ly2 - oy_b + 1, lx1 - ox_b : lx2 - ox_b + 1]
|
||||
|
||||
inter = int(np.logical_and(sub_a, sub_b).sum())
|
||||
```
|
||||
|
||||
The intersection sub-region of two overlapping crops is typically far smaller than the full frame.
|
||||
|
||||
**3. Crop caching — each mask decoded at most once**
|
||||
|
||||
```python
|
||||
if i not in crops_a:
|
||||
crops_a[i] = masks_true.crop(i)
|
||||
```
|
||||
|
||||
Area is obtained from `_rle_area` (sum odd-indexed runs), never touching the pixel grid:
|
||||
|
||||
```python
|
||||
areas_a: npt.NDArray[np.int64] = masks_true.area
|
||||
```
|
||||
|
||||
At FHD-200-50%-v600, dense IoU takes 23 915 ms; compact takes 51.58 ms — a **446x speedup**. At 5% fill / sparse scenarios the speedup is even larger because fewer bbox pairs overlap.
|
||||
|
||||
| Factor | Reduction |
|
||||
| ------------------------------------ | --------------- |
|
||||
| Bbox pre-filter at sparse fill | 25x |
|
||||
| Sub-crop vs full frame per pair | ~200x |
|
||||
| Area from RLE, not `sum(axis=(1,2))` | ~10x |
|
||||
| No 5 GB scratch allocation | latency |
|
||||
| **Combined** | **~100 – 500x** |
|
||||
|
||||
At 20% fill the gaps close — more pairs overlap, larger crops — speedup drops toward the lower end of the range.
|
||||
|
||||
---
|
||||
|
||||
### NMS (`mask_non_max_suppression`)
|
||||
|
||||
Both dense and compact paths now call `mask_iou_batch(masks, masks)` directly, computing exact mask IoU on the original (unresized) masks. There is no intermediate resize step.
|
||||
|
||||
```python
|
||||
# detection/utils/iou_and_nms.py — NMS (both paths)
|
||||
ious = mask_iou_batch(masks, masks, overlap_metric)
|
||||
```
|
||||
|
||||
`mask_iou_batch` dispatches internally: when passed a `CompactMask` it calls `compact_mask_iou_batch`, applying all three IoU optimisations (bbox pre-filter, sub-crop decode, crop caching). When passed a dense ndarray it runs the chunked pixel-AND path.
|
||||
|
||||
All three IoU optimisations apply to the compact path:
|
||||
|
||||
| Factor | Reduction |
|
||||
| ------------------------------------- | ---------------------------- |
|
||||
| Bbox pre-filter eliminates most pairs | 25x at sparse fill |
|
||||
| Sub-crop decode for remaining pairs | ~200x |
|
||||
| Area from RLE, not pixel sum | ~10x |
|
||||
| **Combined** | **same as IoU: ~100 – 500x** |
|
||||
|
||||
At FHD-200-50%-v600, dense NMS takes 5 231 ms; compact takes 48.15 ms — a **481x speedup**. Dense IoU/NMS is skipped for scenarios above 1 GB (4K-200 and SAT-200 tiers); compact NMS still runs on those.
|
||||
|
||||
---
|
||||
|
||||
### `merge` (`Detections.merge`)
|
||||
|
||||
Dense: `np.vstack` allocates a new `(N1+N2, H, W)` array and copies both halves:
|
||||
|
||||
```python
|
||||
# detection/core.py — dense merge path
|
||||
return np.vstack([np.asarray(m) for m in masks])
|
||||
# Merging two 100-mask sets at FHD: 2 x 100 x 2.1 MB = 414 MB copied
|
||||
```
|
||||
|
||||
Compact: `CompactMask.merge` extends a Python list and concatenates two small int32 arrays:
|
||||
|
||||
```python
|
||||
# detection/compact_mask.py — CompactMask.merge
|
||||
new_rles: list[npt.NDArray[np.int32]] = []
|
||||
for m in masks_list:
|
||||
new_rles.extend(m._rles)
|
||||
|
||||
new_crop_shapes: npt.NDArray[np.int32] = np.concatenate(
|
||||
[m._crop_shapes for m in masks_list], axis=0
|
||||
)
|
||||
new_offsets: npt.NDArray[np.int32] = np.concatenate(
|
||||
[m._offsets for m in masks_list], axis=0
|
||||
)
|
||||
```
|
||||
|
||||
`list.extend` copies N reference pointers. `np.concatenate` on `(N, 2)` int32 arrays copies N x 8 bytes per array.
|
||||
|
||||
At FHD-200-50%-v600, dense merge takes 29.71 ms; compact takes 0.03 ms — a **929x speedup**. At SAT-200-20%-v128 the speedup reaches **89 046x**.
|
||||
|
||||
| | Dense | Compact |
|
||||
| ----------- | ----------------------- | -------------------------- |
|
||||
| Data moved | N x H x W (full frames) | N references + N x 8 bytes |
|
||||
| Allocation | new `(N, H, W)` array | new `CompactMask` shell |
|
||||
| **Speedup** | | **effectively free** |
|
||||
|
||||
**Note:** `Detections.merge` calls `is_empty()` on each input. Before the `len(xyxy) > 0` short-circuit was added, `is_empty()` invoked `__eq__` which called `np.array_equal(self.to_dense(), ...)` — materialising the entire `(N, H, W)` CompactMask to dense just to check emptiness. The fix:
|
||||
|
||||
```python
|
||||
# detection/core.py — Detections.is_empty (fixed)
|
||||
if len(self.xyxy) > 0:
|
||||
return False
|
||||
```
|
||||
|
||||
This O(1) check avoids the O(N x H x W) dense materialisation that previously dominated compact merge time.
|
||||
|
||||
---
|
||||
|
||||
### `offset` / `with_offset` (`InferenceSlicer` tile stitching)
|
||||
|
||||
Dense `move_masks`: allocates a new `(N, new_H, new_W)` array and copies each mask with shifted slice coordinates — O(N x H x W):
|
||||
|
||||
```python
|
||||
# detection/utils/masks.py — move_masks
|
||||
mask_array = np.full((masks.shape[0], resolution_wh[1], resolution_wh[0]), False)
|
||||
# ... source/destination slicing logic ...
|
||||
mask_array[:, dst_y1:dst_y2, dst_x1:dst_x2] = masks[:, src_y1:src_y2, src_x1:src_x2]
|
||||
```
|
||||
|
||||
Compact `with_offset(dx, dy)`: vectorised bounds check first. All new bounding-box positions are computed in a single numpy op. When none overflow the new canvas — the common case in `InferenceSlicer` — the RLE data is not touched at all:
|
||||
|
||||
```python
|
||||
# detection/compact_mask.py — CompactMask.with_offset (fast path)
|
||||
new_offsets = self._offsets + np.array([dx, dy], dtype=np.int32) # O(N) numpy
|
||||
needs_clip = (x1s < 0) | (y1s < 0) | (x2s >= new_w) | (y2s >= new_h)
|
||||
if not needs_clip.any():
|
||||
return CompactMask(
|
||||
list(self._rles), self._crop_shapes.copy(), new_offsets, new_image_shape
|
||||
)
|
||||
```
|
||||
|
||||
When a crop does overflow (e.g. object at a tile edge), only that crop is decoded, sliced, and re-encoded. Masks fully outside bounds get a 1x1 all-False stub without any decoding.
|
||||
|
||||
At FHD-200-50%-v600, dense offset takes 42.30 ms; compact takes 0.02 ms — a **2 016x speedup**. At SAT-200-20%-v128 the speedup reaches **290 779x**.
|
||||
|
||||
| | Dense | Compact (no-clip fast path) |
|
||||
| ----------------- | -------------------------------------- | ------------------------------------ |
|
||||
| Work per mask | allocate `(new_H, new_W)` + copy H x W | add scalar to offset row — O(1) |
|
||||
| N=200 at FHD | 200 x 2.1 MB = **414 MB** alloc + copy | two numpy ops on `(N, 2)` int32 |
|
||||
| Output allocation | new `(N, new_H, new_W)` | shared RLE list + new `(N, 2)` array |
|
||||
| **Speedup** | | **effectively free (>1 000x)** |
|
||||
|
||||
In the `InferenceSlicer` pipeline the canvas is always expanded by the tile offset, so no crop ever overflows — the fast path is always taken. Clipping only activates for objects that genuinely straddle the image boundary.
|
||||
|
||||
---
|
||||
|
||||
### `centroids` (`calculate_masks_centroids`)
|
||||
|
||||
Dense: `np.tensordot` reads every pixel of every mask to compute weighted coordinate sums:
|
||||
|
||||
```python
|
||||
# detection/utils/masks.py — dense centroid path
|
||||
vertical_indices, horizontal_indices = np.indices((height, width)) + 0.5
|
||||
# np.tensordot(masks, indices, axes=([1, 2], [0, 1]))
|
||||
# reads all N x H x W values
|
||||
```
|
||||
|
||||
Compact: per-crop loop decodes only the bounding-box region and computes centroids within that crop:
|
||||
|
||||
```python
|
||||
# detection/utils/masks.py — compact centroid path
|
||||
crop = masks.crop(i)
|
||||
crop_h, crop_w = crop.shape
|
||||
x1 = int(masks.offsets[i, 0])
|
||||
y1 = int(masks.offsets[i, 1])
|
||||
# ...
|
||||
crop_rows, crop_cols = np.indices((crop_h, crop_w))
|
||||
cx = float(np.sum((crop_cols + 0.5)[crop])) / total + x1
|
||||
cy = float(np.sum((crop_rows + 0.5)[crop])) / total + y1
|
||||
```
|
||||
|
||||
At FHD-200-50%-v600, dense centroids takes 1 133.68 ms; compact takes 60.39 ms — a **13x speedup**. At SAT-200-20%-v128 the speedup reaches **857x** because the dense path must allocate and scan a 13.4 GB array.
|
||||
|
||||
| Factor | Reduction |
|
||||
| ----------------------------------------- | ------------------- |
|
||||
| Crop area vs full frame (per mask) | fill-dependent |
|
||||
| No global `np.indices((H, W))` allocation | saves large float64 |
|
||||
| **Combined (N=200)** | **~19 – 1 000x** |
|
||||
|
||||
---
|
||||
|
||||
### Summary
|
||||
|
||||
Measured speedups at the **FHD-200-50%-v600** operating point (dense fill, complex polygons — a realistic hard case). Dense baseline = 1x.
|
||||
|
||||
| Operation | Dense cost | Compact cost | Speedup |
|
||||
| ---------------- | ----------- | ------------ | ------- |
|
||||
| Memory | 414 MB | ~1.9 MB | ~392x |
|
||||
| `.area` | 84.66 ms | 0.48 ms | 71x |
|
||||
| `filter` | 14.56 ms | 0.03 ms | 500x |
|
||||
| `annotate` | 848.95 ms | 32.67 ms | 22x |
|
||||
| `mask_iou_batch` | 23 915 ms | 51.58 ms | 446x |
|
||||
| NMS | 5 231 ms | 48.15 ms | 481x |
|
||||
| `merge` | 29.71 ms | 0.03 ms | 929x |
|
||||
| `with_offset` | 42.30 ms | 0.02 ms | 2 016x |
|
||||
| `centroids` | 1 133.68 ms | 60.39 ms | 13x |
|
||||
|
||||
All speedups are larger at sparser fill fractions and larger resolutions. At SAT-200-20%-v128, `.area` reaches 1 204x and `merge` reaches 89 046x. At the sparsest scenarios (5% fill, 8-vertex polygons), memory ratios exceed 60 000x.
|
||||
|
||||
---
|
||||
|
||||
## Drop-In Compatibility
|
||||
|
||||
`CompactMask` implements the same duck-typed interface as `np.ndarray`:
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
from supervision.detection.compact_mask import CompactMask
|
||||
|
||||
# Build from an existing dense (N, H, W) bool array:
|
||||
compact = CompactMask.from_dense(masks_dense, xyxy, image_shape=(H, W))
|
||||
|
||||
# Use exactly like a dense mask — no other code changes needed:
|
||||
detections = sv.Detections(xyxy=xyxy, mask=compact, class_id=class_ids)
|
||||
|
||||
# Filtering, merging, area — all work transparently:
|
||||
filtered = detections[confidence > 0.5]
|
||||
areas = detections.area # RLE sum, no materialisation
|
||||
merged = sv.Detections.merge([det_a, det_b])
|
||||
|
||||
# MaskAnnotator works without any change:
|
||||
annotated = sv.MaskAnnotator().annotate(frame, detections)
|
||||
|
||||
# Materialise back to dense when you need raw numpy:
|
||||
dense_again = compact.to_dense() # (N, H, W) bool
|
||||
```
|
||||
|
||||
Supported indexing patterns:
|
||||
|
||||
| Expression | Returns |
|
||||
| ------------------ | ---------------------------- |
|
||||
| `mask[i]` (int) | Dense `(H, W)` bool array |
|
||||
| `mask[bool_array]` | New `CompactMask` (filtered) |
|
||||
| `mask[slice]` | New `CompactMask` |
|
||||
| `np.asarray(mask)` | Dense `(N, H, W)` bool array |
|
||||
|
||||
---
|
||||
|
||||
## Benchmark
|
||||
|
||||
Run on any machine — no GPU or real model required:
|
||||
|
||||
```bash
|
||||
uv run python examples/compact_mask/benchmark.py
|
||||
```
|
||||
|
||||
Six image tiers x three fill fractions (5 / 20 / 50 %) x three vertex counts (8 / 128 / 600):
|
||||
|
||||
| Tier | Resolution | Objects | Dense array | Notes |
|
||||
| ------- | ---------- | ------- | ----------- | ------------------------------------ |
|
||||
| FHD-100 | 1920x1080 | 100 | 0.21 GB | Full operations including IoU+NMS |
|
||||
| FHD-200 | 1920x1080 | 200 | 0.41 GB | Full operations including IoU+NMS |
|
||||
| FHD-400 | 1920x1080 | 400 | 0.83 GB | Full operations including IoU+NMS |
|
||||
| 4K-100 | 3840x2160 | 100 | 0.83 GB | Full operations including IoU+NMS |
|
||||
| 4K-200 | 3840x2160 | 200 | 1.66 GB | Dense IoU+NMS skipped (array > 1 GB) |
|
||||
| SAT-200 | 8192x8192 | 200 | 13.4 GB | Dense IoU+NMS skipped (array > 1 GB) |
|
||||
|
||||
Dense timing is skipped automatically when the dense IoU/NMS array would exceed 1 GB (`IOU_DENSE_SKIP_GB`), preventing swap thrashing. All dense ops are skipped above 16 GB (`DENSE_SKIP_GB`); no scenario in the current matrix reaches that threshold. Memory is always reported as theoretical `NxHxW` bytes.
|
||||
|
||||
### Sample results (macOS, Apple M4 Max, REPS=4)
|
||||
|
||||
| Scenario | Dense mem | Compact theor. | Mem x | Area x | Filter x | Annot x | IoU x | NMS x | Merge x | Offset x | Centroids x |
|
||||
| ---------------- | --------- | -------------- | ------- | ------ | -------- | ------- | ----- | ----- | -------- | -------- | ----------- |
|
||||
| FHD-100-5%-v8 | 207 MB | 28 KB | 7 418x | — | — | — | — | — | — | — | — |
|
||||
| FHD-100-50%-v600 | 207 MB | 913 KB | 227x | — | — | — | — | — | — | — | — |
|
||||
| FHD-200-50%-v600 | 415 MB | 933 KB | 445x | 71x | 500x | 22x | 446x | 481x | 929x | 2 016x | 13x |
|
||||
| FHD-400-5%-v8 | 829 MB | 60 KB | 13 937x | — | — | — | — | — | — | — | — |
|
||||
| 4K-100-5%-v8 | 829 MB | 53 KB | 15 554x | — | — | — | — | — | — | — | — |
|
||||
| 4K-100-20%-v128 | 829 MB | 586 KB | 1 415x | — | — | — | — | — | — | — | — |
|
||||
| 4K-200-5%-v8 | 1 659 MB | 76 KB | 21 786x | — | — | — | — | — | — | — | — |
|
||||
| SAT-200-5%-v8 | 13 422 MB | 213 KB | 62 968x | 6 942x | 30 255x | 204x | † | † | 105 545x | 251 629x | 2 173x |
|
||||
| SAT-200-20%-v128 | 13 422 MB | 2 596 KB | 5 171x | 1 204x | 14 757x | 89x | † | † | 89 046x | 290 779x | 857x |
|
||||
| SAT-200-50%-v600 | 13 422 MB | 14 222 KB | 944x | — | — | — | † | † | — | — | — |
|
||||
|
||||
- **Compact theor.** — sum of internal numpy buffer `nbytes`
|
||||
- **Mem x** — dense / compact theoretical ratio
|
||||
- **Area x / Filter x / Annot x / IoU x / NMS x / Merge x / Offset x / Centroids x** — compact speedup over dense for each operation
|
||||
- **†** — dense IoU+NMS skipped (dense array > 1 GB); compact still runs and is timed
|
||||
- **—** — not shown; full per-scenario tables are printed by the benchmark script
|
||||
|
||||
All non-skipped scenarios pass: pixel-perfect annotation, exact area, lossless `to_dense()` roundtrip.
|
||||
|
||||
---
|
||||
|
||||
## Use-Cases
|
||||
|
||||
- **Aerial / satellite imagery** — thousands of small objects on large tiles; dense masks exhaust RAM before inference completes.
|
||||
- **High-density crowd / cell segmentation** — N > 500 on FHD already requires several GB of mask storage per batch.
|
||||
- **Real-time annotation pipelines** — crop-paint cuts annotation from seconds to milliseconds at 4K resolution.
|
||||
- **Long-running tracking** — accumulated `Detections` across many frames stay in kilobytes rather than gigabytes.
|
||||
- **`InferenceSlicer`** — `with_offset()` adjusts crop origins directly when stitching tile results; no dense materialisation needed.
|
||||
|
||||
---
|
||||
|
||||
## Limitations
|
||||
|
||||
- `CompactMask` is **not** a full `np.ndarray`. Call `.to_dense()` before passing to code that requires arbitrary ndarray methods (`astype`, `reshape`, `ravel`, `any`, `all`, …).
|
||||
- RLE format is **row-major (C-order), crop-scoped** — incompatible with pycocotools / COCO API RLEs (column-major, full-image-scoped). Use `.to_dense()` first if you need pycocotools interop.
|
||||
- `from_dense()` requires the input `(N, H, W)` array to fit in memory. For truly OOM-scale data, build `CompactMask` per-detection directly from model output crops rather than from a pre-allocated dense stack.
|
||||
|
||||
---
|
||||
|
||||
## Files
|
||||
|
||||
| File | Description |
|
||||
| -------------- | ------------------------------------------------ |
|
||||
| `benchmark.py` | Full benchmark across FHD / 4K / satellite tiers |
|
||||
| `README.md` | This file |
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,17 +21,17 @@ https://github.com/roboflow/supervision/assets/26109316/f84db7b5-79e2-4142-a1da-
|
||||
cd supervision/examples/count_people_in_zone
|
||||
```
|
||||
|
||||
- setup python environment and activate it \[optional\]
|
||||
- setup python environment and activate it [optional]
|
||||
|
||||
```bash
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
uv venv
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
- install required dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
uv pip install -r requirements.txt
|
||||
```
|
||||
|
||||
- download `traffic_analysis.pt` and `traffic_analysis.mov` files
|
||||
@@ -110,7 +110,7 @@ https://github.com/roboflow/supervision/assets/26109316/f84db7b5-79e2-4142-a1da-
|
||||
|
||||
```bash
|
||||
python inference_example.py \
|
||||
--roboflow_api_key <ROBOFLOW API KEY> \
|
||||
--roboflow_api_key "ROBOFLOW_API_KEY" \
|
||||
--zone_configuration_path data/multi-zone-config.json \
|
||||
--source_video_path data/market-square.mp4 \
|
||||
--confidence_threshold 0.3 \
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
|
||||
@@ -62,7 +61,10 @@ def initiate_annotators(
|
||||
|
||||
|
||||
def detect(
|
||||
frame: np.ndarray, model: RoboflowInferenceModel, confidence_threshold: float = 0.5
|
||||
frame: np.ndarray,
|
||||
model: RoboflowInferenceModel,
|
||||
confidence_threshold: float = 0.5,
|
||||
iou_threshold: float = 0.7,
|
||||
) -> sv.Detections:
|
||||
"""
|
||||
Detect objects in a frame using Inference model, filtering detections by class ID
|
||||
@@ -73,7 +75,8 @@ def detect(
|
||||
model (RoboflowInferenceModel): The Inference model used for processing the
|
||||
frame.
|
||||
confidence_threshold (float): The confidence threshold for filtering
|
||||
detections. Default is 0.5.
|
||||
detections.
|
||||
iou_threshold (float): The IoU threshold for non-maximum suppression.
|
||||
|
||||
Returns:
|
||||
sv.Detections: Filtered detections after processing the frame with the Inference
|
||||
@@ -83,7 +86,7 @@ def detect(
|
||||
This function is specifically tailored for an Inference model and assumes class
|
||||
ID 0 for filtering.
|
||||
"""
|
||||
results = model.infer(frame)[0]
|
||||
results = model.infer(frame, confidence=confidence_threshold, iou=iou_threshold)[0]
|
||||
detections = sv.Detections.from_inference(results)
|
||||
filter_by_class = detections.class_id == 0
|
||||
filter_by_confidence = detections.confidence > confidence_threshold
|
||||
@@ -124,78 +127,49 @@ def annotate(
|
||||
return annotated_frame
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Counting people in zones with Inference and Supervision"
|
||||
)
|
||||
def main(
|
||||
zone_configuration_path: str,
|
||||
source_video_path: str,
|
||||
model_id: str = "yolov8x-1280",
|
||||
roboflow_api_key: str | None = None,
|
||||
target_video_path: str | None = None,
|
||||
confidence_threshold: float = 0.3,
|
||||
iou_threshold: float = 0.7,
|
||||
):
|
||||
"""
|
||||
Counting people in zones with Inference and Supervision.
|
||||
|
||||
parser.add_argument(
|
||||
"--zone_configuration_path",
|
||||
required=True,
|
||||
help="Path to the zone configuration JSON file",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--model_id",
|
||||
default="yolov8x-1280",
|
||||
help="Roboflow model ID",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--roboflow_api_key",
|
||||
default=None,
|
||||
help="Roboflow API KEY",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source_video_path",
|
||||
required=True,
|
||||
help="Path to the source video file",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--target_video_path",
|
||||
default=None,
|
||||
help="Path to the target video file (output)",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--confidence_threshold",
|
||||
default=0.3,
|
||||
help="Confidence threshold for the model",
|
||||
type=float,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--iou_threshold",
|
||||
default=0.7,
|
||||
help="IOU threshold for the model",
|
||||
type=float,
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
api_key = args.roboflow_api_key
|
||||
Args:
|
||||
zone_configuration_path: Path to the zone configuration JSON file
|
||||
source_video_path: Path to the source video file
|
||||
model_id: Roboflow model ID
|
||||
roboflow_api_key: Roboflow API KEY
|
||||
target_video_path: Path to the target video file (output)
|
||||
confidence_threshold: Confidence threshold for the model
|
||||
iou_threshold: IOU threshold for the model
|
||||
"""
|
||||
api_key = roboflow_api_key
|
||||
api_key = os.environ.get("ROBOFLOW_API_KEY", api_key)
|
||||
if api_key is None:
|
||||
raise ValueError(
|
||||
"Roboflow API key is missing. Please provide it as an argument or set the "
|
||||
"ROBOFLOW_API_KEY environment variable."
|
||||
)
|
||||
args.roboflow_api_key = api_key
|
||||
roboflow_api_key = api_key
|
||||
|
||||
video_info = sv.VideoInfo.from_video_path(args.source_video_path)
|
||||
polygons = load_zones_config(args.zone_configuration_path)
|
||||
video_info = sv.VideoInfo.from_video_path(source_video_path)
|
||||
polygons = load_zones_config(zone_configuration_path)
|
||||
zones, zone_annotators, box_annotators = initiate_annotators(
|
||||
polygons=polygons, resolution_wh=video_info.resolution_wh
|
||||
)
|
||||
|
||||
model = get_roboflow_model(model_id=args.model_id, api_key=args.roboflow_api_key)
|
||||
model = get_roboflow_model(model_id=model_id, api_key=roboflow_api_key)
|
||||
|
||||
frames_generator = sv.get_video_frames_generator(args.source_video_path)
|
||||
if args.target_video_path is not None:
|
||||
with sv.VideoSink(args.target_video_path, video_info) as sink:
|
||||
frames_generator = sv.get_video_frames_generator(source_video_path)
|
||||
if target_video_path is not None:
|
||||
with sv.VideoSink(target_video_path, video_info) as sink:
|
||||
for frame in tqdm(frames_generator, total=video_info.total_frames):
|
||||
detections = detect(frame, model, args.confidence_threshold)
|
||||
detections = detect(frame, model, confidence_threshold, iou_threshold)
|
||||
annotated_frame = annotate(
|
||||
frame=frame,
|
||||
zones=zones,
|
||||
@@ -206,7 +180,7 @@ if __name__ == "__main__":
|
||||
sink.write_frame(annotated_frame)
|
||||
else:
|
||||
for frame in tqdm(frames_generator, total=video_info.total_frames):
|
||||
detections = detect(frame, model, args.confidence_threshold)
|
||||
detections = detect(frame, model, confidence_threshold, iou_threshold)
|
||||
annotated_frame = annotate(
|
||||
frame=frame,
|
||||
zones=zones,
|
||||
@@ -219,3 +193,10 @@ if __name__ == "__main__":
|
||||
break
|
||||
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
|
||||
@@ -3,3 +3,4 @@ inference
|
||||
supervision
|
||||
tqdm
|
||||
ultralytics
|
||||
jsonargparse[signatures]
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import argparse
|
||||
import json
|
||||
|
||||
import cv2
|
||||
@@ -60,7 +59,10 @@ def initiate_annotators(
|
||||
|
||||
|
||||
def detect(
|
||||
frame: np.ndarray, model: YOLO, confidence_threshold: float = 0.5
|
||||
frame: np.ndarray,
|
||||
model: YOLO,
|
||||
confidence_threshold: float = 0.5,
|
||||
iou_threshold: float = 0.7,
|
||||
) -> sv.Detections:
|
||||
"""
|
||||
Detect objects in a frame using a YOLO model, filtering detections by class ID and
|
||||
@@ -71,6 +73,7 @@ def detect(
|
||||
model (YOLO): The YOLO model used for processing the frame.
|
||||
confidence_threshold (float): The confidence threshold for filtering
|
||||
detections. Default is 0.5.
|
||||
iou_threshold (float): The IoU threshold for non-maximum suppression.
|
||||
|
||||
Returns:
|
||||
sv.Detections: Filtered detections after processing the frame with the YOLO
|
||||
@@ -80,7 +83,9 @@ def detect(
|
||||
This function is specifically tailored for a YOLO model and assumes class ID 0
|
||||
for filtering.
|
||||
"""
|
||||
results = model(frame, imgsz=1280, verbose=False)[0]
|
||||
results = model(
|
||||
frame, conf=confidence_threshold, iou=iou_threshold, imgsz=1280, verbose=False
|
||||
)[0]
|
||||
detections = sv.Detections.from_ultralytics(results)
|
||||
filter_by_class = detections.class_id == 0
|
||||
filter_by_confidence = detections.confidence > confidence_threshold
|
||||
@@ -121,63 +126,38 @@ def annotate(
|
||||
return annotated_frame
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Counting people in zones with YOLO and Supervision"
|
||||
)
|
||||
def main(
|
||||
zone_configuration_path: str,
|
||||
source_video_path: str,
|
||||
source_weights_path: str = "yolo11x.pt",
|
||||
target_video_path: str | None = None,
|
||||
confidence_threshold: float = 0.3,
|
||||
iou_threshold: float = 0.7,
|
||||
):
|
||||
"""
|
||||
Counting people in zones with YOLO and Supervision.
|
||||
|
||||
parser.add_argument(
|
||||
"--zone_configuration_path",
|
||||
required=True,
|
||||
help="Path to the zone configuration JSON file",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source_weights_path",
|
||||
default="yolo11x.pt",
|
||||
help="Path to the source weights file",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source_video_path",
|
||||
required=True,
|
||||
help="Path to the source video file",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--target_video_path",
|
||||
default=None,
|
||||
help="Path to the target video file (output)",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--confidence_threshold",
|
||||
default=0.3,
|
||||
help="Confidence threshold for the model",
|
||||
type=float,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--iou_threshold",
|
||||
default=0.7,
|
||||
help="IOU threshold for the model",
|
||||
type=float,
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
video_info = sv.VideoInfo.from_video_path(args.source_video_path)
|
||||
polygons = load_zones_config(args.zone_configuration_path)
|
||||
Args:
|
||||
zone_configuration_path: Path to the zone configuration JSON file
|
||||
source_video_path: Path to the source video file
|
||||
source_weights_path: Path to the source weights file
|
||||
target_video_path: Path to the target video file (output)
|
||||
confidence_threshold: Confidence threshold for the model
|
||||
iou_threshold: IOU threshold for the model
|
||||
"""
|
||||
video_info = sv.VideoInfo.from_video_path(source_video_path)
|
||||
polygons = load_zones_config(zone_configuration_path)
|
||||
zones, zone_annotators, box_annotators = initiate_annotators(
|
||||
polygons=polygons, resolution_wh=video_info.resolution_wh
|
||||
)
|
||||
|
||||
model = YOLO(args.source_weights_path)
|
||||
model = YOLO(source_weights_path)
|
||||
|
||||
frames_generator = sv.get_video_frames_generator(args.source_video_path)
|
||||
if args.target_video_path is not None:
|
||||
with sv.VideoSink(args.target_video_path, video_info) as sink:
|
||||
frames_generator = sv.get_video_frames_generator(source_video_path)
|
||||
if target_video_path is not None:
|
||||
with sv.VideoSink(target_video_path, video_info) as sink:
|
||||
for frame in tqdm(frames_generator, total=video_info.total_frames):
|
||||
detections = detect(frame, model, args.confidence_threshold)
|
||||
detections = detect(frame, model, confidence_threshold, iou_threshold)
|
||||
annotated_frame = annotate(
|
||||
frame=frame,
|
||||
zones=zones,
|
||||
@@ -188,7 +168,7 @@ if __name__ == "__main__":
|
||||
sink.write_frame(annotated_frame)
|
||||
else:
|
||||
for frame in tqdm(frames_generator, total=video_info.total_frames):
|
||||
detections = detect(frame, model, args.confidence_threshold)
|
||||
detections = detect(frame, model, confidence_threshold, iou_threshold)
|
||||
annotated_frame = annotate(
|
||||
frame=frame,
|
||||
zones=zones,
|
||||
@@ -201,3 +181,10 @@ if __name__ == "__main__":
|
||||
break
|
||||
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
|
||||
@@ -15,17 +15,17 @@ supervision package for multiple tasks such as drawing heatmap annotations, trac
|
||||
cd supervision/examples/heatmap_and_track
|
||||
```
|
||||
|
||||
- setup python environment and activate it \[optional\]
|
||||
- setup python environment and activate it [optional]
|
||||
|
||||
```bash
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
uv venv
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
- install required dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
uv pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## 🛠️ script arguments
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
supervision
|
||||
ultralytics
|
||||
jsonargparse[signatures]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import argparse
|
||||
from typing import Optional
|
||||
|
||||
import cv2
|
||||
from ultralytics import YOLO
|
||||
@@ -12,10 +12,10 @@ def download_video() -> str:
|
||||
return VideoAssets.PEOPLE_WALKING.value
|
||||
|
||||
|
||||
def heatmap_and_track(
|
||||
def main(
|
||||
source_weights_path: str,
|
||||
source_video_path: str,
|
||||
target_video_path: str,
|
||||
source_video_path: Optional[str] = None,
|
||||
target_video_path: str = "output.mp4",
|
||||
confidence_threshold: float = 0.35,
|
||||
iou_threshold: float = 0.5,
|
||||
heatmap_alpha: float = 0.5,
|
||||
@@ -24,8 +24,24 @@ def heatmap_and_track(
|
||||
track_seconds: int = 5,
|
||||
minimum_matching_threshold: float = 0.99,
|
||||
) -> None:
|
||||
"""
|
||||
Heatmap and Tracking with Supervision.
|
||||
|
||||
Args:
|
||||
source_weights_path: Path to the source weights file
|
||||
source_video_path: Path to the source video file
|
||||
target_video_path: Path to the target video file
|
||||
confidence_threshold: Confidence threshold for the model
|
||||
iou_threshold: IOU threshold for the model
|
||||
heatmap_alpha: Opacity of the overlay mask, between 0 and 1
|
||||
radius: Radius of the heat circle
|
||||
track_activation_threshold: Detection confidence threshold for track activation
|
||||
track_seconds: Number of seconds to buffer when a track is lost
|
||||
minimum_matching_threshold: Threshold for matching tracks with detections
|
||||
"""
|
||||
### instantiate model
|
||||
model = YOLO(source_weights_path)
|
||||
source_video_path = source_video_path or download_video()
|
||||
|
||||
### heatmap config
|
||||
heat_map_annotator = sv.HeatMapAnnotator(
|
||||
@@ -101,81 +117,7 @@ def heatmap_and_track(
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Heatmap and Tracking with Supervision"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source_weights_path",
|
||||
required=True,
|
||||
help="Path to the source weights file",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source_video_path",
|
||||
default=download_video(),
|
||||
help="Path to the source video file",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--target_video_path",
|
||||
default="output.mp4",
|
||||
help="Path to the target video file (output)",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--confidence_threshold",
|
||||
default=0.35,
|
||||
help="Confidence threshold for the model",
|
||||
type=float,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--iou_threshold",
|
||||
default=0.5,
|
||||
help="IOU threshold for the model",
|
||||
type=float,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--heatmap_alpha",
|
||||
default=0.5,
|
||||
help="Opacity of the overlay mask, between 0 and 1",
|
||||
type=float,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--radius",
|
||||
default=25,
|
||||
help="Radius of the heat circle",
|
||||
type=float,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--track_threshold",
|
||||
default=0.35,
|
||||
help="Detection confidence threshold for track activation",
|
||||
type=float,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--track_seconds",
|
||||
default=5,
|
||||
help="Number of seconds to buffer when a track is lost",
|
||||
type=int,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--match_threshold",
|
||||
default=0.99,
|
||||
help="Threshold for matching tracks with detections",
|
||||
type=float,
|
||||
)
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
heatmap_and_track(
|
||||
source_weights_path=args.source_weights_path,
|
||||
source_video_path=args.source_video_path,
|
||||
target_video_path=args.target_video_path,
|
||||
confidence_threshold=args.confidence_threshold,
|
||||
iou_threshold=args.iou_threshold,
|
||||
heatmap_alpha=args.heatmap_alpha,
|
||||
radius=args.radius,
|
||||
track_activation_threshold=args.track_threshold,
|
||||
track_seconds=args.track_seconds,
|
||||
minimum_matching_threshold=args.match_threshold,
|
||||
)
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
|
||||
@@ -11,7 +11,7 @@ supervision package for multiple tasks such as tracking, annotations, etc.
|
||||
|
||||
https://github.com/roboflow/supervision/assets/26109316/d50118c1-2ae4-458d-915a-5d860fd36f71
|
||||
|
||||
> \[!IMPORTANT\]
|
||||
> [!IMPORTANT]
|
||||
> Adjust the [`SOURCE`](https://github.com/roboflow/supervision/blob/e32b05a636dab2ea1f39299e529c4b22b8baa8da/examples/speed_estimation/ultralytics_example.py#L10)
|
||||
> and [`TARGET`](https://github.com/roboflow/supervision/blob/e32b05a636dab2ea1f39299e529c4b22b8baa8da/examples/speed_estimation/ultralytics_example.py#L15)
|
||||
> configuration if you plan to run a speed estimation script on your video file. Those must be adjusted separately for each camera view. You can learn more
|
||||
@@ -26,23 +26,23 @@ https://github.com/roboflow/supervision/assets/26109316/d50118c1-2ae4-458d-915a-
|
||||
cd supervision/examples/speed_estimation
|
||||
```
|
||||
|
||||
- setup python environment and activate it \[optional\]
|
||||
- setup python environment and activate it [optional]
|
||||
|
||||
```bash
|
||||
python3.10 -m venv venv
|
||||
source venv/bin/activate
|
||||
uv venv
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
- install required dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
uv pip install -r requirements.txt
|
||||
```
|
||||
|
||||
- download `vehicles.mp4` file
|
||||
|
||||
```bash
|
||||
python3.10 video_downloader.py
|
||||
python video_downloader.py
|
||||
```
|
||||
|
||||
## 🛠️ script arguments
|
||||
@@ -90,7 +90,7 @@ https://github.com/roboflow/supervision/assets/26109316/d50118c1-2ae4-458d-915a-
|
||||
|
||||
```bash
|
||||
python inference_example.py \
|
||||
--roboflow_api_key <ROBOFLOW API KEY> \
|
||||
--roboflow_api_key "ROBOFLOW_API_KEY" \
|
||||
--source_video_path data/vehicles.mp4 \
|
||||
--target_video_path data/vehicles-result.mp4 \
|
||||
--confidence_threshold 0.3 \
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import argparse
|
||||
import os
|
||||
from collections import defaultdict, deque
|
||||
|
||||
@@ -38,64 +37,39 @@ class ViewTransformer:
|
||||
return transformed_points.reshape(-1, 2)
|
||||
|
||||
|
||||
def parse_arguments() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Vehicle Speed Estimation using Inference and Supervision"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--model_id",
|
||||
default="yolov8x-640",
|
||||
help="Roboflow model ID",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--roboflow_api_key",
|
||||
default=None,
|
||||
help="Roboflow API KEY",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source_video_path",
|
||||
required=True,
|
||||
help="Path to the source video file",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--target_video_path",
|
||||
required=True,
|
||||
help="Path to the target video file (output)",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--confidence_threshold",
|
||||
default=0.3,
|
||||
help="Confidence threshold for the model",
|
||||
type=float,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--iou_threshold", default=0.7, help="IOU threshold for the model", type=float
|
||||
)
|
||||
def main(
|
||||
source_video_path: str,
|
||||
target_video_path: str,
|
||||
model_id: str = "yolov8x-640",
|
||||
roboflow_api_key: str | None = None,
|
||||
confidence_threshold: float = 0.3,
|
||||
iou_threshold: float = 0.7,
|
||||
):
|
||||
"""
|
||||
Vehicle Speed Estimation using Inference and Supervision.
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parse_arguments()
|
||||
|
||||
api_key = args.roboflow_api_key
|
||||
Args:
|
||||
source_video_path: Path to the source video file
|
||||
target_video_path: Path to the target video file (output)
|
||||
model_id: Roboflow model ID
|
||||
roboflow_api_key: Roboflow API KEY
|
||||
confidence_threshold: Confidence threshold for the model
|
||||
iou_threshold: IOU threshold for the model
|
||||
"""
|
||||
api_key = roboflow_api_key
|
||||
api_key = os.environ.get("ROBOFLOW_API_KEY", api_key)
|
||||
if api_key is None:
|
||||
raise ValueError(
|
||||
"Roboflow API key is missing. Please provide it as an argument or set the "
|
||||
"ROBOFLOW_API_KEY environment variable."
|
||||
)
|
||||
args.roboflow_api_key = api_key
|
||||
roboflow_api_key = api_key
|
||||
|
||||
video_info = sv.VideoInfo.from_video_path(video_path=args.source_video_path)
|
||||
model = get_roboflow_model(model_id=args.model_id, api_key=args.roboflow_api_key)
|
||||
video_info = sv.VideoInfo.from_video_path(video_path=source_video_path)
|
||||
model = get_roboflow_model(model_id=model_id, api_key=roboflow_api_key)
|
||||
|
||||
byte_track = sv.ByteTrack(
|
||||
frame_rate=video_info.fps, track_activation_threshold=args.confidence_threshold
|
||||
frame_rate=video_info.fps, track_activation_threshold=confidence_threshold
|
||||
)
|
||||
|
||||
thickness = sv.calculate_optimal_line_thickness(
|
||||
@@ -110,24 +84,24 @@ if __name__ == "__main__":
|
||||
)
|
||||
trace_annotator = sv.TraceAnnotator(
|
||||
thickness=thickness,
|
||||
trace_length=video_info.fps * 2,
|
||||
trace_length=int(video_info.fps * 2),
|
||||
position=sv.Position.BOTTOM_CENTER,
|
||||
)
|
||||
|
||||
frame_generator = sv.get_video_frames_generator(source_path=args.source_video_path)
|
||||
frame_generator = sv.get_video_frames_generator(source_path=source_video_path)
|
||||
|
||||
polygon_zone = sv.PolygonZone(polygon=SOURCE)
|
||||
view_transformer = ViewTransformer(source=SOURCE, target=TARGET)
|
||||
|
||||
coordinates = defaultdict(lambda: deque(maxlen=video_info.fps))
|
||||
coordinates = defaultdict(lambda: deque(maxlen=int(video_info.fps)))
|
||||
|
||||
with sv.VideoSink(args.target_video_path, video_info) as sink:
|
||||
with sv.VideoSink(target_video_path, video_info) as sink:
|
||||
for frame in frame_generator:
|
||||
results = model.infer(frame)[0]
|
||||
results = model.infer(
|
||||
frame, confidence=confidence_threshold, iou=iou_threshold
|
||||
)[0]
|
||||
detections = sv.Detections.from_inference(results)
|
||||
detections = detections[detections.confidence > args.confidence_threshold]
|
||||
detections = detections[polygon_zone.trigger(detections)]
|
||||
detections = detections.with_nms(threshold=args.iou_threshold)
|
||||
detections = byte_track.update_with_detections(detections=detections)
|
||||
|
||||
points = detections.get_anchors_coordinates(
|
||||
@@ -166,3 +140,10 @@ if __name__ == "__main__":
|
||||
if cv2.waitKey(1) & 0xFF == ord("q"):
|
||||
break
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
|
||||
@@ -4,3 +4,4 @@ requests
|
||||
ultralytics
|
||||
super-gradients==3.5.0
|
||||
inference
|
||||
jsonargparse[signatures]
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import argparse
|
||||
from collections import defaultdict, deque
|
||||
|
||||
import cv2
|
||||
@@ -37,43 +36,26 @@ class ViewTransformer:
|
||||
return transformed_points.reshape(-1, 2)
|
||||
|
||||
|
||||
def parse_arguments() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Vehicle Speed Estimation using Ultralytics and Supervision"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source_video_path",
|
||||
required=True,
|
||||
help="Path to the source video file",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--target_video_path",
|
||||
required=True,
|
||||
help="Path to the target video file (output)",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--confidence_threshold",
|
||||
default=0.3,
|
||||
help="Confidence threshold for the model",
|
||||
type=float,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--iou_threshold", default=0.7, help="IOU threshold for the model", type=float
|
||||
)
|
||||
def main(
|
||||
source_video_path: str,
|
||||
target_video_path: str,
|
||||
confidence_threshold: float = 0.3,
|
||||
iou_threshold: float = 0.7,
|
||||
):
|
||||
"""
|
||||
Vehicle Speed Estimation using Ultralytics and Supervision.
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parse_arguments()
|
||||
|
||||
video_info = sv.VideoInfo.from_video_path(video_path=args.source_video_path)
|
||||
Args:
|
||||
source_video_path: Path to the source video file
|
||||
target_video_path: Path to the target video file (output)
|
||||
confidence_threshold: Confidence threshold for the model
|
||||
iou_threshold: IOU threshold for the model
|
||||
"""
|
||||
video_info = sv.VideoInfo.from_video_path(video_path=source_video_path)
|
||||
model = YOLO("yolo11x.pt")
|
||||
|
||||
byte_track = sv.ByteTrack(
|
||||
frame_rate=video_info.fps, track_activation_threshold=args.confidence_threshold
|
||||
frame_rate=video_info.fps, track_activation_threshold=confidence_threshold
|
||||
)
|
||||
|
||||
thickness = sv.calculate_optimal_line_thickness(
|
||||
@@ -88,24 +70,22 @@ if __name__ == "__main__":
|
||||
)
|
||||
trace_annotator = sv.TraceAnnotator(
|
||||
thickness=thickness,
|
||||
trace_length=video_info.fps * 2,
|
||||
trace_length=int(video_info.fps * 2),
|
||||
position=sv.Position.BOTTOM_CENTER,
|
||||
)
|
||||
|
||||
frame_generator = sv.get_video_frames_generator(source_path=args.source_video_path)
|
||||
frame_generator = sv.get_video_frames_generator(source_path=source_video_path)
|
||||
|
||||
polygon_zone = sv.PolygonZone(polygon=SOURCE)
|
||||
view_transformer = ViewTransformer(source=SOURCE, target=TARGET)
|
||||
|
||||
coordinates = defaultdict(lambda: deque(maxlen=video_info.fps))
|
||||
coordinates = defaultdict(lambda: deque(maxlen=int(video_info.fps)))
|
||||
|
||||
with sv.VideoSink(args.target_video_path, video_info) as sink:
|
||||
with sv.VideoSink(target_video_path, video_info) as sink:
|
||||
for frame in frame_generator:
|
||||
result = model(frame)[0]
|
||||
result = model(frame, conf=confidence_threshold, iou=iou_threshold)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
detections = detections[detections.confidence > args.confidence_threshold]
|
||||
detections = detections[polygon_zone.trigger(detections)]
|
||||
detections = detections.with_nms(threshold=args.iou_threshold)
|
||||
detections = byte_track.update_with_detections(detections=detections)
|
||||
|
||||
points = detections.get_anchors_coordinates(
|
||||
@@ -144,3 +124,10 @@ if __name__ == "__main__":
|
||||
if cv2.waitKey(1) & 0xFF == ord("q"):
|
||||
break
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import argparse
|
||||
from collections import defaultdict, deque
|
||||
|
||||
import cv2
|
||||
@@ -38,43 +37,26 @@ class ViewTransformer:
|
||||
return transformed_points.reshape(-1, 2)
|
||||
|
||||
|
||||
def parse_arguments() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Vehicle Speed Estimation using YOLO-NAS and Supervision"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source_video_path",
|
||||
required=True,
|
||||
help="Path to the source video file",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--target_video_path",
|
||||
required=True,
|
||||
help="Path to the target video file (output)",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--confidence_threshold",
|
||||
default=0.3,
|
||||
help="Confidence threshold for the model",
|
||||
type=float,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--iou_threshold", default=0.7, help="IOU threshold for the model", type=float
|
||||
)
|
||||
def main(
|
||||
source_video_path: str,
|
||||
target_video_path: str,
|
||||
confidence_threshold: float = 0.3,
|
||||
iou_threshold: float = 0.7,
|
||||
):
|
||||
"""
|
||||
Vehicle Speed Estimation using YOLO-NAS and Supervision.
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parse_arguments()
|
||||
|
||||
video_info = sv.VideoInfo.from_video_path(video_path=args.source_video_path)
|
||||
Args:
|
||||
source_video_path: Path to the source video file
|
||||
target_video_path: Path to the target video file (output)
|
||||
confidence_threshold: Confidence threshold for the model
|
||||
iou_threshold: IOU threshold for the model
|
||||
"""
|
||||
video_info = sv.VideoInfo.from_video_path(video_path=source_video_path)
|
||||
model = models.get(Models.YOLO_NAS_L, pretrained_weights="coco")
|
||||
|
||||
byte_track = sv.ByteTrack(
|
||||
frame_rate=video_info.fps, track_activation_threshold=args.confidence_threshold
|
||||
frame_rate=video_info.fps, track_activation_threshold=confidence_threshold
|
||||
)
|
||||
|
||||
thickness = sv.calculate_optimal_line_thickness(
|
||||
@@ -89,23 +71,24 @@ if __name__ == "__main__":
|
||||
)
|
||||
trace_annotator = sv.TraceAnnotator(
|
||||
thickness=thickness,
|
||||
trace_length=video_info.fps * 2,
|
||||
trace_length=int(video_info.fps * 2),
|
||||
position=sv.Position.BOTTOM_CENTER,
|
||||
)
|
||||
|
||||
frame_generator = sv.get_video_frames_generator(source_path=args.source_video_path)
|
||||
frame_generator = sv.get_video_frames_generator(source_path=source_video_path)
|
||||
|
||||
polygon_zone = sv.PolygonZone(polygon=SOURCE)
|
||||
view_transformer = ViewTransformer(source=SOURCE, target=TARGET)
|
||||
|
||||
coordinates = defaultdict(lambda: deque(maxlen=video_info.fps))
|
||||
coordinates = defaultdict(lambda: deque(maxlen=int(video_info.fps)))
|
||||
|
||||
with sv.VideoSink(args.target_video_path, video_info) as sink:
|
||||
with sv.VideoSink(target_video_path, video_info) as sink:
|
||||
for frame in frame_generator:
|
||||
result = model.predict(frame)[0]
|
||||
result = model.predict(frame, conf=confidence_threshold, iou=iou_threshold)[
|
||||
0
|
||||
]
|
||||
detections = sv.Detections.from_yolo_nas(result)
|
||||
detections = detections[polygon_zone.trigger(detections)]
|
||||
detections = detections.with_nms(threshold=args.iou_threshold)
|
||||
detections = byte_track.update_with_detections(detections=detections)
|
||||
|
||||
points = detections.get_anchors_coordinates(
|
||||
@@ -144,3 +127,10 @@ if __name__ == "__main__":
|
||||
if cv2.waitKey(1) & 0xFF == ord("q"):
|
||||
break
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
|
||||
@@ -20,17 +20,17 @@ https://github.com/roboflow/supervision/assets/26109316/d051cc8a-dd15-41d4-aa36-
|
||||
cd supervision/examples/time_in_zone
|
||||
```
|
||||
|
||||
- setup python environment and activate it \[optional\]
|
||||
- setup python environment and activate it [optional]
|
||||
|
||||
```bash
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
uv venv
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
- install required dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
uv pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## 🛠 scripts
|
||||
@@ -86,15 +86,10 @@ window where you can draw polygons on the source image or video file. The polygo
|
||||
be saved as a JSON file.
|
||||
|
||||
- `--source_path`: Path to the source image or video file for drawing polygons.
|
||||
|
||||
- `--zone_configuration_path`: Path where the polygon annotations will be saved as a JSON file.
|
||||
|
||||
- `enter` - finish drawing the current polygon.
|
||||
|
||||
- `escape` - cancel drawing the current polygon.
|
||||
|
||||
- `q` - quit the drawing window.
|
||||
|
||||
- `s` - save zone configuration to a JSON file.
|
||||
|
||||
```bash
|
||||
@@ -128,10 +123,11 @@ Script to run object detection on a video file using the Roboflow Inference mode
|
||||
python inference_file_example.py \
|
||||
--zone_configuration_path "data/checkout/config.json" \
|
||||
--source_video_path "data/checkout/video.mp4" \
|
||||
--model_id "yolov8x-640" \
|
||||
--classes 0 \
|
||||
--model_id "rfdetr-medium" \
|
||||
--classes "[0]" \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
--iou_threshold 0.7 \
|
||||
--roboflow_api_key "ROBOFLOWS_API_KEY"
|
||||
```
|
||||
|
||||
https://github.com/roboflow/supervision/assets/26109316/d051cc8a-dd15-41d4-aa36-d38b86334c39
|
||||
@@ -140,17 +136,18 @@ https://github.com/roboflow/supervision/assets/26109316/d051cc8a-dd15-41d4-aa36-
|
||||
python inference_file_example.py \
|
||||
--zone_configuration_path "data/traffic/config.json" \
|
||||
--source_video_path "data/traffic/video.mp4" \
|
||||
--model_id "yolov8x-640" \
|
||||
--classes 2 5 6 7 \
|
||||
--model_id "rfdetr-medium" \
|
||||
--classes "[2, 5, 6, 7]" \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
--iou_threshold 0.7 \
|
||||
--roboflow_api_key "ROBOFLOWS_API_KEY"
|
||||
```
|
||||
|
||||
https://github.com/roboflow/supervision/assets/26109316/5ec896d7-4b39-4426-8979-11e71666878b
|
||||
|
||||
### `inference_stream_example`
|
||||
|
||||
Script to run object detection on a video stream using the Roboflow Inference model.
|
||||
Script to run object detection on an RTSP stream using Roboflow Inference model.
|
||||
|
||||
- `--zone_configuration_path`: Path to the zone configuration JSON file.
|
||||
- `--rtsp_url`: Complete RTSP URL for the video stream.
|
||||
@@ -163,8 +160,8 @@ Script to run object detection on a video stream using the Roboflow Inference mo
|
||||
python inference_stream_example.py \
|
||||
--zone_configuration_path "data/checkout/config.json" \
|
||||
--rtsp_url "rtsp://localhost:8554/live0.stream" \
|
||||
--model_id "yolov8x-640" \
|
||||
--classes 0 \
|
||||
--model_id "rfdetr-medium" \
|
||||
--classes "[0]" \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
```
|
||||
@@ -173,14 +170,85 @@ python inference_stream_example.py \
|
||||
python inference_stream_example.py \
|
||||
--zone_configuration_path "data/traffic/config.json" \
|
||||
--rtsp_url "rtsp://localhost:8554/live0.stream" \
|
||||
--model_id "yolov8x-640" \
|
||||
--classes 2 5 6 7 \
|
||||
--model_id "rfdetr-medium" \
|
||||
--classes "[2, 5, 6, 7]" \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>👉 show ultralytics examples</summary>
|
||||
### `rfdeter_file_example`
|
||||
|
||||
Script to run object detection on a video file using the RF-DETR model.
|
||||
|
||||
- `--zone_configuration_path`: Path to the zone configuration JSON file.
|
||||
- `--source_video_path`: Path to the source video file.
|
||||
- `--model_size`: Size of RF-DETR model ('nano', 'small', 'medium', 'base' or 'large'). Default is 'medium'.
|
||||
- `--device`: Computation device ('cpu', 'mps' or 'cuda'). Default is 'cpu'.
|
||||
- `--classes`: List of class IDs to track. If empty, all classes are tracked.
|
||||
- `--confidence_threshold`: Confidence level for detections (`0` to `1`). Default is `0.3`.
|
||||
- `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`.
|
||||
- `--resolution`: Resolution for the model input. Default is `640`.
|
||||
|
||||
```bash
|
||||
python rfdetr_file_example.py \
|
||||
--zone_configuration_path "data/checkout/config.json" \
|
||||
--source_video_path "data/checkout/video.mp4" \
|
||||
--model_size "medium" \
|
||||
--device="cpu" \
|
||||
--classes "[1]" \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7 \
|
||||
--resolution 640
|
||||
```
|
||||
|
||||
```bash
|
||||
python rfdetr_file_example.py \
|
||||
--zone_configuration_path "data/traffic/config.json" \
|
||||
--source_video_path "data/traffic/video.mp4" \
|
||||
--model_size "medium" \
|
||||
--device="cpu" \
|
||||
--classes "[3, 6, 7, 8]" \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7 \
|
||||
--resolution 640
|
||||
```
|
||||
|
||||
### `rfdeter_stream_example`
|
||||
|
||||
Script to run object detection on an RTSP stream using the RF-DETR model.
|
||||
|
||||
- `--zone_configuration_path`: Path to the zone-configuration JSON file defining the polygons.
|
||||
- `--rtsp_url`: Complete RTSP URL of the live video stream.
|
||||
- `--model_size`: RF-DETR backbone size to load — choose from 'nano', 'small', 'medium', 'base', or 'large' (default 'medium').
|
||||
- `--device`: Compute device to run the model on ('cpu', 'mps', or 'cuda'; default 'cpu').
|
||||
- `--classes`: Space-separated list of class IDs to track. Leave empty to track all classes.
|
||||
- `--confidence_threshold`: Minimum confidence score for a detection to be kept, range 0-1 (default 0.3).
|
||||
- `--iou_threshold`: IOU threshold applied during non-max suppression (default 0.7).
|
||||
- `--resolution`: Shortest-side input resolution supplied to the model. The script will round it to the nearest valid multiple (default 640).
|
||||
|
||||
```bash
|
||||
python rfdetr_stream_example.py \
|
||||
--zone_configuration_path "data/checkout/config.json" \
|
||||
--rtsp_url "rtsp://localhost:8554/live0.stream" \
|
||||
--model_size "medium" \
|
||||
--device "cpu" \
|
||||
--classes "[1]" \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7 \
|
||||
--resolution 640
|
||||
```
|
||||
|
||||
```bash
|
||||
python rfdetr_stream_example.py \
|
||||
--zone_configuration_path "data/traffic/config.json" \
|
||||
--rtsp_url "rtsp://localhost:8554/live0.stream" \
|
||||
--model_size "medium" \
|
||||
--device "cpu" \
|
||||
--classes "[3, 6, 7, 8]" \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7 \
|
||||
--resolution 640
|
||||
```
|
||||
|
||||
### `ultralytics_file_example`
|
||||
|
||||
@@ -200,7 +268,7 @@ python ultralytics_file_example.py \
|
||||
--source_video_path "data/checkout/video.mp4" \
|
||||
--weights "yolov8x.pt" \
|
||||
--device "cpu" \
|
||||
--classes 0 \
|
||||
--classes "[0]" \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
```
|
||||
@@ -211,7 +279,7 @@ python ultralytics_file_example.py \
|
||||
--source_video_path "data/traffic/video.mp4" \
|
||||
--weights "yolov8x.pt" \
|
||||
--device "cpu" \
|
||||
--classes 2 5 6 7 \
|
||||
--classes "[2, 5, 6, 7]" \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
```
|
||||
@@ -234,7 +302,7 @@ python ultralytics_stream_example.py \
|
||||
--rtsp_url "rtsp://localhost:8554/live0.stream" \
|
||||
--weights "yolov8x.pt" \
|
||||
--device "cpu" \
|
||||
--classes 0 \
|
||||
--classes "[0]" \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
```
|
||||
@@ -245,7 +313,7 @@ python ultralytics_stream_example.py \
|
||||
--rtsp_url "rtsp://localhost:8554/live0.stream" \
|
||||
--weights "yolov8x.pt" \
|
||||
--device "cpu" \
|
||||
--classes 2 5 6 7 \
|
||||
--classes "[2, 5, 6, 7]" \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
```
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import argparse
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
from inference import get_model
|
||||
@@ -16,14 +14,27 @@ LABEL_ANNOTATOR = sv.LabelAnnotator(
|
||||
|
||||
|
||||
def main(
|
||||
source_video_path: str,
|
||||
zone_configuration_path: str,
|
||||
model_id: str,
|
||||
confidence: float,
|
||||
iou: float,
|
||||
classes: list[int],
|
||||
source_video_path: str,
|
||||
model_id: str = "rfdetr-medium",
|
||||
confidence_threshold: float = 0.3,
|
||||
iou_threshold: float = 0.7,
|
||||
classes: list[int] = [],
|
||||
roboflow_api_key: str = "",
|
||||
) -> None:
|
||||
model = get_model(model_id=model_id)
|
||||
"""
|
||||
Calculating detections dwell time in zones, using video file.
|
||||
|
||||
Args:
|
||||
zone_configuration_path: Path to the zone configuration JSON file
|
||||
source_video_path: Path to the source video file
|
||||
model_id: Roboflow model ID
|
||||
confidence_threshold: Confidence level for detections (0 to 1)
|
||||
iou_threshold: IOU threshold for non-max suppression
|
||||
classes: List of class IDs to track. If empty, all classes are tracked
|
||||
roboflow_api_key: Roboflow API key for accessing private models
|
||||
"""
|
||||
model = get_model(model_id=model_id, api_key=roboflow_api_key)
|
||||
tracker = sv.ByteTrack(minimum_matching_threshold=0.5)
|
||||
video_info = sv.VideoInfo.from_video_path(video_path=source_video_path)
|
||||
frames_generator = sv.get_video_frames_generator(source_video_path)
|
||||
@@ -39,7 +50,9 @@ def main(
|
||||
timers = [FPSBasedTimer(video_info.fps) for _ in zones]
|
||||
|
||||
for frame in frames_generator:
|
||||
results = model.infer(frame, confidence=confidence, iou_threshold=iou)[0]
|
||||
results = model.infer(
|
||||
frame, confidence=confidence_threshold, iou_threshold=iou_threshold
|
||||
)[0]
|
||||
detections = sv.Detections.from_inference(results)
|
||||
detections = detections[find_in_list(detections.class_id, classes)]
|
||||
detections = tracker.update_with_detections(detections)
|
||||
@@ -78,50 +91,7 @@ def main(
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Calculating detections dwell time in zones, using video file."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--zone_configuration_path",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Path to the zone configuration JSON file.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source_video_path",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Path to the source video file.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--model_id", type=str, default="yolov8s-640", help="Roboflow model ID."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--confidence_threshold",
|
||||
type=float,
|
||||
default=0.3,
|
||||
help="Confidence level for detections (0 to 1). Default is 0.3.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--iou_threshold",
|
||||
default=0.7,
|
||||
type=float,
|
||||
help="IOU threshold for non-max suppression. Default is 0.7.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--classes",
|
||||
nargs="*",
|
||||
type=int,
|
||||
default=[],
|
||||
help="List of class IDs to track. If empty, all classes are tracked.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
main(
|
||||
source_video_path=args.source_video_path,
|
||||
zone_configuration_path=args.zone_configuration_path,
|
||||
model_id=args.model_id,
|
||||
confidence=args.confidence_threshold,
|
||||
iou=args.iou_threshold,
|
||||
classes=args.classes,
|
||||
)
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import argparse
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
from inference import get_model
|
||||
@@ -16,14 +14,27 @@ LABEL_ANNOTATOR = sv.LabelAnnotator(
|
||||
|
||||
|
||||
def main(
|
||||
rtsp_url: str,
|
||||
zone_configuration_path: str,
|
||||
model_id: str,
|
||||
confidence: float,
|
||||
iou: float,
|
||||
classes: list[int],
|
||||
rtsp_url: str,
|
||||
model_id: str = "rfdetr-medium",
|
||||
confidence_threshold: float = 0.3,
|
||||
iou_threshold: float = 0.7,
|
||||
classes: list[int] = [],
|
||||
roboflow_api_key: str = "",
|
||||
) -> None:
|
||||
model = get_model(model_id=model_id)
|
||||
"""
|
||||
Calculating detections dwell time in zones, using RTSP stream.
|
||||
|
||||
Args:
|
||||
zone_configuration_path: Path to the zone configuration JSON file
|
||||
rtsp_url: Complete RTSP URL for the video stream
|
||||
model_id: Roboflow model ID
|
||||
confidence_threshold: Confidence level for detections (0 to 1)
|
||||
iou_threshold: IOU threshold for non-max suppression
|
||||
classes: List of class IDs to track. If empty, all classes are tracked
|
||||
roboflow_api_key: Roboflow API key for accessing private models
|
||||
"""
|
||||
model = get_model(model_id=model_id, api_key=roboflow_api_key)
|
||||
tracker = sv.ByteTrack(minimum_matching_threshold=0.5)
|
||||
frames_generator = get_stream_frames_generator(rtsp_url=rtsp_url)
|
||||
fps_monitor = sv.FPSMonitor()
|
||||
@@ -42,7 +53,9 @@ def main(
|
||||
fps_monitor.tick()
|
||||
fps = fps_monitor.fps
|
||||
|
||||
results = model.infer(frame, confidence=confidence, iou_threshold=iou)[0]
|
||||
results = model.infer(
|
||||
frame, confidence=confidence_threshold, iou_threshold=iou_threshold
|
||||
)[0]
|
||||
detections = sv.Detections.from_inference(results)
|
||||
detections = detections[find_in_list(detections.class_id, classes)]
|
||||
detections = tracker.update_with_detections(detections)
|
||||
@@ -88,50 +101,7 @@ def main(
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Calculating detections dwell time in zones, using RTSP stream."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--zone_configuration_path",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Path to the zone configuration JSON file.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--rtsp_url",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Complete RTSP URL for the video stream.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--model_id", type=str, default="yolov8s-640", help="Roboflow model ID."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--confidence_threshold",
|
||||
type=float,
|
||||
default=0.3,
|
||||
help="Confidence level for detections (0 to 1). Default is 0.3.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--iou_threshold",
|
||||
default=0.7,
|
||||
type=float,
|
||||
help="IOU threshold for non-max suppression. Default is 0.7.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--classes",
|
||||
nargs="*",
|
||||
type=int,
|
||||
default=[],
|
||||
help="List of class IDs to track. If empty, all classes are tracked.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
main(
|
||||
rtsp_url=args.rtsp_url,
|
||||
zone_configuration_path=args.zone_configuration_path,
|
||||
model_id=args.model_id,
|
||||
confidence=args.confidence_threshold,
|
||||
iou=args.iou_threshold,
|
||||
classes=args.classes,
|
||||
)
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import argparse
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
from inference import InferencePipeline
|
||||
@@ -77,21 +75,35 @@ class CustomSink:
|
||||
|
||||
|
||||
def main(
|
||||
rtsp_url: str,
|
||||
zone_configuration_path: str,
|
||||
model_id: str,
|
||||
confidence: float,
|
||||
iou: float,
|
||||
classes: list[int],
|
||||
rtsp_url: str,
|
||||
model_id: str = "rfdetr-medium",
|
||||
confidence_threshold: float = 0.3,
|
||||
iou_threshold: float = 0.7,
|
||||
classes: list[int] = [],
|
||||
roboflow_api_key: str = "",
|
||||
) -> None:
|
||||
"""
|
||||
Calculating detections dwell time in zones, using RTSP stream.
|
||||
|
||||
Args:
|
||||
zone_configuration_path: Path to the zone configuration JSON file
|
||||
rtsp_url: Complete RTSP URL for the video stream
|
||||
model_id: Roboflow model ID
|
||||
confidence_threshold: Confidence level for detections (0 to 1)
|
||||
iou_threshold: IOU threshold for non-max suppression
|
||||
classes: List of class IDs to track. If empty, all classes are tracked
|
||||
roboflow_api_key: Roboflow API key for accessing private models
|
||||
"""
|
||||
sink = CustomSink(zone_configuration_path=zone_configuration_path, classes=classes)
|
||||
|
||||
pipeline = InferencePipeline.init(
|
||||
model_id=model_id,
|
||||
video_reference=rtsp_url,
|
||||
on_prediction=sink.on_prediction,
|
||||
confidence=confidence,
|
||||
iou_threshold=iou,
|
||||
confidence=confidence_threshold,
|
||||
iou_threshold=iou_threshold,
|
||||
api_key=roboflow_api_key,
|
||||
)
|
||||
|
||||
pipeline.start()
|
||||
@@ -103,50 +115,7 @@ def main(
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Calculating detections dwell time in zones, using RTSP stream."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--zone_configuration_path",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Path to the zone configuration JSON file.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--rtsp_url",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Complete RTSP URL for the video stream.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--model_id", type=str, default="yolov8s-640", help="Roboflow model ID."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--confidence_threshold",
|
||||
type=float,
|
||||
default=0.3,
|
||||
help="Confidence level for detections (0 to 1). Default is 0.3.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--iou_threshold",
|
||||
default=0.7,
|
||||
type=float,
|
||||
help="IOU threshold for non-max suppression. Default is 0.7.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--classes",
|
||||
nargs="*",
|
||||
type=int,
|
||||
default=[],
|
||||
help="List of class IDs to track. If empty, all classes are tracked.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
main(
|
||||
rtsp_url=args.rtsp_url,
|
||||
zone_configuration_path=args.zone_configuration_path,
|
||||
model_id=args.model_id,
|
||||
confidence=args.confidence_threshold,
|
||||
iou=args.iou_threshold,
|
||||
classes=args.classes,
|
||||
)
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
supervision
|
||||
ultralytics
|
||||
inference
|
||||
pytube
|
||||
# https://github.com/pytube/pytube/issues/2044
|
||||
# pytube
|
||||
pytubefix
|
||||
jsonargparse[signatures]
|
||||
rfdetr
|
||||
yt_dlp
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
from rfdetr import RFDETRBase, RFDETRLarge, RFDETRMedium, RFDETRNano, RFDETRSmall
|
||||
from utils.general import find_in_list, load_zones_config
|
||||
from utils.timers import FPSBasedTimer
|
||||
|
||||
import supervision as sv
|
||||
|
||||
COLORS = sv.ColorPalette.from_hex(["#E6194B", "#3CB44B", "#FFE119", "#3C76D1"])
|
||||
COLOR_ANNOTATOR = sv.ColorAnnotator(color=COLORS)
|
||||
LABEL_ANNOTATOR = sv.LabelAnnotator(
|
||||
color=COLORS, text_color=sv.Color.from_hex("#000000")
|
||||
)
|
||||
|
||||
|
||||
class ModelSize(Enum):
|
||||
NANO = "nano"
|
||||
SMALL = "small"
|
||||
MEDIUM = "medium"
|
||||
BASE = "base"
|
||||
LARGE = "large"
|
||||
|
||||
@classmethod
|
||||
def list(cls):
|
||||
return list(map(lambda c: c.value, cls))
|
||||
|
||||
@classmethod
|
||||
def from_value(cls, value: ModelSize | str) -> ModelSize:
|
||||
if isinstance(value, cls):
|
||||
return value
|
||||
if isinstance(value, str):
|
||||
value = value.lower()
|
||||
try:
|
||||
return cls(value)
|
||||
except ValueError:
|
||||
raise ValueError(f"Invalid value: {value}. Must be one of {cls.list()}")
|
||||
raise ValueError(
|
||||
f"Invalid value type: {type(value)}. Must be an instance of "
|
||||
f"{cls.__name__} or str."
|
||||
)
|
||||
|
||||
|
||||
def load_model(checkpoint: ModelSize | str, device: str, resolution: int):
|
||||
checkpoint = ModelSize.from_value(checkpoint)
|
||||
|
||||
if checkpoint == ModelSize.NANO:
|
||||
return RFDETRNano(device=device, resolution=resolution)
|
||||
if checkpoint == ModelSize.SMALL:
|
||||
return RFDETRSmall(device=device, resolution=resolution)
|
||||
if checkpoint == ModelSize.MEDIUM:
|
||||
return RFDETRMedium(device=device, resolution=resolution)
|
||||
if checkpoint == ModelSize.BASE:
|
||||
return RFDETRBase(device=device, resolution=resolution)
|
||||
if checkpoint == ModelSize.LARGE:
|
||||
return RFDETRLarge(device=device, resolution=resolution)
|
||||
|
||||
raise ValueError(
|
||||
f"Invalid checkpoint: {checkpoint}. Must be one of: {ModelSize.list()}."
|
||||
)
|
||||
|
||||
|
||||
def adjust_resolution(checkpoint: ModelSize | str, resolution: int) -> int:
|
||||
checkpoint = ModelSize.from_value(checkpoint)
|
||||
|
||||
if checkpoint in {ModelSize.NANO, ModelSize.SMALL, ModelSize.MEDIUM}:
|
||||
divisor = 32
|
||||
elif checkpoint in {ModelSize.BASE, ModelSize.LARGE}:
|
||||
divisor = 56
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Unknown checkpoint: {checkpoint}. Must be one of: {ModelSize.list()}."
|
||||
)
|
||||
|
||||
remainder = resolution % divisor
|
||||
if remainder == 0:
|
||||
return resolution
|
||||
lower = resolution - remainder
|
||||
upper = lower + divisor
|
||||
|
||||
if resolution - lower < upper - resolution:
|
||||
return lower
|
||||
else:
|
||||
return upper
|
||||
|
||||
|
||||
def main(
|
||||
source_video_path: str,
|
||||
zone_configuration_path: str,
|
||||
resolution: int,
|
||||
model_size: str = "small",
|
||||
device: str = "cpu",
|
||||
confidence_threshold: float = 0.3,
|
||||
iou_threshold: float = 0.7,
|
||||
classes: list[int] = [],
|
||||
) -> None:
|
||||
"""
|
||||
Calculating detections dwell time in zones, using video file.
|
||||
|
||||
Args:
|
||||
source_video_path: Path to the source video file
|
||||
zone_configuration_path: Path to the zone configuration JSON file
|
||||
resolution: Input resolution for the model
|
||||
model_size: RF-DETR model size ('nano', 'small', 'medium', 'base' or 'large')
|
||||
device: Computation device ('cpu', 'mps' or 'cuda')
|
||||
confidence_threshold: Confidence level for detections (0 to 1)
|
||||
iou_threshold: IOU threshold for non-max suppression
|
||||
classes: List of class IDs to track. If empty, all classes are tracked
|
||||
"""
|
||||
resolution = adjust_resolution(checkpoint=model_size, resolution=resolution)
|
||||
model = load_model(checkpoint=model_size, device=device, resolution=resolution)
|
||||
tracker = sv.ByteTrack(minimum_matching_threshold=0.5)
|
||||
video_info = sv.VideoInfo.from_video_path(video_path=source_video_path)
|
||||
frames_generator = sv.get_video_frames_generator(source_video_path)
|
||||
|
||||
polygons = load_zones_config(file_path=zone_configuration_path)
|
||||
zones = [
|
||||
sv.PolygonZone(
|
||||
polygon=polygon,
|
||||
triggering_anchors=(sv.Position.CENTER,),
|
||||
)
|
||||
for polygon in polygons
|
||||
]
|
||||
timers = [FPSBasedTimer(video_info.fps) for _ in zones]
|
||||
|
||||
for frame in frames_generator:
|
||||
detections = model.predict(frame, threshold=confidence_threshold)
|
||||
detections = detections[find_in_list(detections.class_id, classes)]
|
||||
detections = detections.with_nms(threshold=iou_threshold)
|
||||
detections = tracker.update_with_detections(detections)
|
||||
|
||||
annotated_frame = frame.copy()
|
||||
|
||||
for idx, zone in enumerate(zones):
|
||||
annotated_frame = sv.draw_polygon(
|
||||
scene=annotated_frame, polygon=zone.polygon, color=COLORS.by_idx(idx)
|
||||
)
|
||||
|
||||
detections_in_zone = detections[zone.trigger(detections)]
|
||||
time_in_zone = timers[idx].tick(detections_in_zone)
|
||||
custom_color_lookup = np.full(detections_in_zone.class_id.shape, idx)
|
||||
|
||||
annotated_frame = COLOR_ANNOTATOR.annotate(
|
||||
scene=annotated_frame,
|
||||
detections=detections_in_zone,
|
||||
custom_color_lookup=custom_color_lookup,
|
||||
)
|
||||
labels = [
|
||||
f"#{tracker_id} {int(time // 60):02d}:{int(time % 60):02d}"
|
||||
for tracker_id, time in zip(detections_in_zone.tracker_id, time_in_zone)
|
||||
]
|
||||
annotated_frame = LABEL_ANNOTATOR.annotate(
|
||||
scene=annotated_frame,
|
||||
detections=detections_in_zone,
|
||||
labels=labels,
|
||||
custom_color_lookup=custom_color_lookup,
|
||||
)
|
||||
|
||||
cv2.imshow("Processed Video", annotated_frame)
|
||||
if cv2.waitKey(1) & 0xFF == ord("q"):
|
||||
break
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
@@ -0,0 +1,183 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
from rfdetr import RFDETRBase, RFDETRLarge, RFDETRMedium, RFDETRNano, RFDETRSmall
|
||||
from utils.general import find_in_list, get_stream_frames_generator, load_zones_config
|
||||
from utils.timers import ClockBasedTimer
|
||||
|
||||
import supervision as sv
|
||||
|
||||
COLORS = sv.ColorPalette.from_hex(["#E6194B", "#3CB44B", "#FFE119", "#3C76D1"])
|
||||
COLOR_ANNOTATOR = sv.ColorAnnotator(color=COLORS)
|
||||
LABEL_ANNOTATOR = sv.LabelAnnotator(
|
||||
color=COLORS, text_color=sv.Color.from_hex("#000000")
|
||||
)
|
||||
|
||||
|
||||
class ModelSize(Enum):
|
||||
NANO = "nano"
|
||||
SMALL = "small"
|
||||
MEDIUM = "medium"
|
||||
BASE = "base"
|
||||
LARGE = "large"
|
||||
|
||||
@classmethod
|
||||
def list(cls):
|
||||
return list(map(lambda c: c.value, cls))
|
||||
|
||||
@classmethod
|
||||
def from_value(cls, value: ModelSize | str) -> ModelSize:
|
||||
if isinstance(value, cls):
|
||||
return value
|
||||
if isinstance(value, str):
|
||||
value = value.lower()
|
||||
try:
|
||||
return cls(value)
|
||||
except ValueError:
|
||||
raise ValueError(f"Invalid value: {value}. Must be one of {cls.list()}")
|
||||
raise ValueError(
|
||||
f"Invalid value type: {type(value)}. Must be an instance of "
|
||||
f"{cls.__name__} or str."
|
||||
)
|
||||
|
||||
|
||||
def load_model(checkpoint: ModelSize | str, device: str, resolution: int):
|
||||
checkpoint = ModelSize.from_value(checkpoint)
|
||||
|
||||
if checkpoint == ModelSize.NANO:
|
||||
return RFDETRNano(device=device, resolution=resolution)
|
||||
if checkpoint == ModelSize.SMALL:
|
||||
return RFDETRSmall(device=device, resolution=resolution)
|
||||
if checkpoint == ModelSize.MEDIUM:
|
||||
return RFDETRMedium(device=device, resolution=resolution)
|
||||
if checkpoint == ModelSize.BASE:
|
||||
return RFDETRBase(device=device, resolution=resolution)
|
||||
if checkpoint == ModelSize.LARGE:
|
||||
return RFDETRLarge(device=device, resolution=resolution)
|
||||
|
||||
raise ValueError(
|
||||
f"Invalid checkpoint: {checkpoint}. Must be one of: {ModelSize.list()}."
|
||||
)
|
||||
|
||||
|
||||
def adjust_resolution(checkpoint: ModelSize | str, resolution: int) -> int:
|
||||
checkpoint = ModelSize.from_value(checkpoint)
|
||||
|
||||
if checkpoint in {ModelSize.NANO, ModelSize.SMALL, ModelSize.MEDIUM}:
|
||||
divisor = 32
|
||||
elif checkpoint in {ModelSize.BASE, ModelSize.LARGE}:
|
||||
divisor = 56
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Unknown checkpoint: {checkpoint}. Must be one of: {ModelSize.list()}."
|
||||
)
|
||||
|
||||
remainder = resolution % divisor
|
||||
if remainder == 0:
|
||||
return resolution
|
||||
lower = resolution - remainder
|
||||
upper = lower + divisor
|
||||
|
||||
if resolution - lower < upper - resolution:
|
||||
return lower
|
||||
else:
|
||||
return upper
|
||||
|
||||
|
||||
def main(
|
||||
rtsp_url: str,
|
||||
zone_configuration_path: str,
|
||||
resolution: int,
|
||||
model_size: str = "small",
|
||||
device: str = "cpu",
|
||||
confidence_threshold: float = 0.3,
|
||||
iou_threshold: float = 0.7,
|
||||
classes: list[int] = [],
|
||||
) -> None:
|
||||
"""
|
||||
Calculating detections dwell time in zones, using RTSP stream.
|
||||
|
||||
Args:
|
||||
rtsp_url: Complete RTSP URL for the video stream
|
||||
zone_configuration_path: Path to the zone configuration JSON file
|
||||
resolution: Input resolution for the model
|
||||
model_size: RF-DETR model size ('nano', 'small', 'medium', 'base' or 'large')
|
||||
device: Computation device ('cpu', 'mps' or 'cuda')
|
||||
confidence_threshold: Confidence level for detections (0 to 1)
|
||||
iou_threshold: IOU threshold for non-max suppression
|
||||
classes: List of class IDs to track. If empty, all classes are tracked
|
||||
"""
|
||||
resolution = adjust_resolution(checkpoint=model_size, resolution=resolution)
|
||||
model = load_model(checkpoint=model_size, device=device, resolution=resolution)
|
||||
tracker = sv.ByteTrack(minimum_matching_threshold=0.5)
|
||||
frames_generator = get_stream_frames_generator(rtsp_url=rtsp_url)
|
||||
fps_monitor = sv.FPSMonitor()
|
||||
|
||||
polygons = load_zones_config(file_path=zone_configuration_path)
|
||||
zones = [
|
||||
sv.PolygonZone(
|
||||
polygon=polygon,
|
||||
triggering_anchors=(sv.Position.CENTER,),
|
||||
)
|
||||
for polygon in polygons
|
||||
]
|
||||
timers = [ClockBasedTimer() for _ in zones]
|
||||
|
||||
for frame in frames_generator:
|
||||
fps_monitor.tick()
|
||||
fps = fps_monitor.fps
|
||||
|
||||
detections = model.predict(frame, threshold=confidence_threshold)
|
||||
detections = detections[find_in_list(detections.class_id, classes)]
|
||||
detections = detections.with_nms(threshold=iou_threshold)
|
||||
detections = tracker.update_with_detections(detections)
|
||||
|
||||
annotated_frame = frame.copy()
|
||||
annotated_frame = sv.draw_text(
|
||||
scene=annotated_frame,
|
||||
text=f"{fps:.1f}",
|
||||
text_anchor=sv.Point(40, 30),
|
||||
background_color=sv.Color.from_hex("#A351FB"),
|
||||
text_color=sv.Color.from_hex("#000000"),
|
||||
)
|
||||
|
||||
for idx, zone in enumerate(zones):
|
||||
annotated_frame = sv.draw_polygon(
|
||||
scene=annotated_frame, polygon=zone.polygon, color=COLORS.by_idx(idx)
|
||||
)
|
||||
|
||||
detections_in_zone = detections[zone.trigger(detections)]
|
||||
time_in_zone = timers[idx].tick(detections_in_zone)
|
||||
custom_color_lookup = np.full(detections_in_zone.class_id.shape, idx)
|
||||
|
||||
annotated_frame = COLOR_ANNOTATOR.annotate(
|
||||
scene=annotated_frame,
|
||||
detections=detections_in_zone,
|
||||
custom_color_lookup=custom_color_lookup,
|
||||
)
|
||||
labels = [
|
||||
f"#{tracker_id} {int(t // 60):02d}:{int(t % 60):02d}"
|
||||
for tracker_id, t in zip(detections_in_zone.tracker_id, time_in_zone)
|
||||
]
|
||||
annotated_frame = LABEL_ANNOTATOR.annotate(
|
||||
scene=annotated_frame,
|
||||
detections=detections_in_zone,
|
||||
labels=labels,
|
||||
custom_color_lookup=custom_color_lookup,
|
||||
)
|
||||
|
||||
cv2.imshow("Processed Video", annotated_frame)
|
||||
if cv2.waitKey(1) & 0xFF == ord("q"):
|
||||
break
|
||||
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
@@ -0,0 +1,182 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
from inference import InferencePipeline
|
||||
from inference.core.interfaces.camera.entities import VideoFrame
|
||||
from rfdetr import RFDETRBase, RFDETRLarge, RFDETRMedium, RFDETRNano, RFDETRSmall
|
||||
from utils.general import find_in_list, load_zones_config
|
||||
from utils.timers import ClockBasedTimer
|
||||
|
||||
import supervision as sv
|
||||
|
||||
|
||||
class ModelSize(Enum):
|
||||
NANO = "nano"
|
||||
SMALL = "small"
|
||||
MEDIUM = "medium"
|
||||
BASE = "base"
|
||||
LARGE = "large"
|
||||
|
||||
@classmethod
|
||||
def list(cls):
|
||||
return [c.value for c in cls]
|
||||
|
||||
@classmethod
|
||||
def from_value(cls, value: ModelSize | str) -> ModelSize:
|
||||
if isinstance(value, cls):
|
||||
return value
|
||||
if isinstance(value, str):
|
||||
value = value.lower()
|
||||
try:
|
||||
return cls(value)
|
||||
except ValueError as exc:
|
||||
raise ValueError(
|
||||
f"Invalid model size '{value}'. Must be one of {cls.list()}."
|
||||
) from exc
|
||||
raise ValueError(
|
||||
f"Invalid value type '{type(value)}'. Expected str or ModelSize."
|
||||
)
|
||||
|
||||
|
||||
def load_model(checkpoint: ModelSize | str, device: str, resolution: int):
|
||||
checkpoint = ModelSize.from_value(checkpoint)
|
||||
if checkpoint == ModelSize.NANO:
|
||||
return RFDETRNano(device=device, resolution=resolution)
|
||||
if checkpoint == ModelSize.SMALL:
|
||||
return RFDETRSmall(device=device, resolution=resolution)
|
||||
if checkpoint == ModelSize.MEDIUM:
|
||||
return RFDETRMedium(device=device, resolution=resolution)
|
||||
if checkpoint == ModelSize.BASE:
|
||||
return RFDETRBase(device=device, resolution=resolution)
|
||||
if checkpoint == ModelSize.LARGE:
|
||||
return RFDETRLarge(device=device, resolution=resolution)
|
||||
raise RuntimeError("Unhandled checkpoint type.")
|
||||
|
||||
|
||||
def adjust_resolution(checkpoint: ModelSize | str, resolution: int) -> int:
|
||||
checkpoint = ModelSize.from_value(checkpoint)
|
||||
divisor = (
|
||||
32 if checkpoint in {ModelSize.NANO, ModelSize.SMALL, ModelSize.MEDIUM} else 56
|
||||
)
|
||||
remainder = resolution % divisor
|
||||
if remainder == 0:
|
||||
return resolution
|
||||
lower = resolution - remainder
|
||||
upper = lower + divisor
|
||||
return lower if resolution - lower < upper - resolution else upper
|
||||
|
||||
|
||||
COLORS = sv.ColorPalette.from_hex(["#E6194B", "#3CB44B", "#FFE119", "#3C76D1"])
|
||||
COLOR_ANNOTATOR = sv.ColorAnnotator(color=COLORS)
|
||||
LABEL_ANNOTATOR = sv.LabelAnnotator(
|
||||
color=COLORS, text_color=sv.Color.from_hex("#000000")
|
||||
)
|
||||
|
||||
|
||||
class CustomSink:
|
||||
def __init__(self, zone_configuration_path: str, classes: list[int]):
|
||||
self.classes = classes
|
||||
self.tracker = sv.ByteTrack(minimum_matching_threshold=0.8)
|
||||
self.fps_monitor = sv.FPSMonitor()
|
||||
self.polygons = load_zones_config(file_path=zone_configuration_path)
|
||||
self.timers = [ClockBasedTimer() for _ in self.polygons]
|
||||
self.zones = [
|
||||
sv.PolygonZone(
|
||||
polygon=polygon,
|
||||
triggering_anchors=(sv.Position.CENTER,),
|
||||
)
|
||||
for polygon in self.polygons
|
||||
]
|
||||
|
||||
def on_prediction(self, detections: sv.Detections, frame: VideoFrame) -> None:
|
||||
self.fps_monitor.tick()
|
||||
fps = self.fps_monitor.fps
|
||||
detections = detections[find_in_list(detections.class_id, self.classes)]
|
||||
detections = self.tracker.update_with_detections(detections)
|
||||
annotated_frame = frame.image.copy()
|
||||
annotated_frame = sv.draw_text(
|
||||
scene=annotated_frame,
|
||||
text=f"{fps:.1f}",
|
||||
text_anchor=sv.Point(40, 30),
|
||||
background_color=sv.Color.from_hex("#A351FB"),
|
||||
text_color=sv.Color.from_hex("#000000"),
|
||||
)
|
||||
for idx, zone in enumerate(self.zones):
|
||||
annotated_frame = sv.draw_polygon(
|
||||
scene=annotated_frame,
|
||||
polygon=zone.polygon,
|
||||
color=COLORS.by_idx(idx),
|
||||
)
|
||||
detections_in_zone = detections[zone.trigger(detections)]
|
||||
time_in_zone = self.timers[idx].tick(detections_in_zone)
|
||||
custom_color_lookup = np.full(detections_in_zone.class_id.shape, idx)
|
||||
annotated_frame = COLOR_ANNOTATOR.annotate(
|
||||
scene=annotated_frame,
|
||||
detections=detections_in_zone,
|
||||
custom_color_lookup=custom_color_lookup,
|
||||
)
|
||||
labels = [
|
||||
f"#{tracker_id} {int(t // 60):02d}:{int(t % 60):02d}"
|
||||
for tracker_id, t in zip(detections_in_zone.tracker_id, time_in_zone)
|
||||
]
|
||||
annotated_frame = LABEL_ANNOTATOR.annotate(
|
||||
scene=annotated_frame,
|
||||
detections=detections_in_zone,
|
||||
labels=labels,
|
||||
custom_color_lookup=custom_color_lookup,
|
||||
)
|
||||
cv2.imshow("Processed Video", annotated_frame)
|
||||
cv2.waitKey(1)
|
||||
|
||||
|
||||
def main(
|
||||
rtsp_url: str,
|
||||
zone_configuration_path: str,
|
||||
resolution: int,
|
||||
model_size: str = "small",
|
||||
device: str = "cpu",
|
||||
confidence_threshold: float = 0.3,
|
||||
iou_threshold: float = 0.7,
|
||||
classes: list[int] = [],
|
||||
) -> None:
|
||||
"""
|
||||
Calculating detections dwell time in zones using an RTSP stream.
|
||||
|
||||
Args:
|
||||
rtsp_url: Complete RTSP URL for the video stream
|
||||
zone_configuration_path: Path to the zone configuration JSON file
|
||||
resolution: Input resolution for the model
|
||||
model_size: RF-DETR model size ('nano', 'small', 'medium', 'base' or 'large')
|
||||
device: Computation device ('cpu', 'mps' or 'cuda')
|
||||
confidence_threshold: Confidence level for detections (0 to 1)
|
||||
iou_threshold: IOU threshold for non-max suppression
|
||||
classes: List of class IDs to track. If empty, all classes are tracked
|
||||
"""
|
||||
resolution = adjust_resolution(checkpoint=model_size, resolution=resolution)
|
||||
model = load_model(checkpoint=model_size, device=device, resolution=resolution)
|
||||
|
||||
def inference_callback(frames: list[VideoFrame]) -> list[sv.Detections]:
|
||||
dets = model.predict(frames[0].image, threshold=confidence_threshold)
|
||||
return [dets.with_nms(threshold=iou_threshold)]
|
||||
|
||||
sink = CustomSink(zone_configuration_path=zone_configuration_path, classes=classes)
|
||||
pipeline = InferencePipeline.init_with_custom_logic(
|
||||
video_reference=rtsp_url,
|
||||
on_video_frame=inference_callback,
|
||||
on_prediction=sink.on_prediction,
|
||||
)
|
||||
pipeline.start()
|
||||
try:
|
||||
pipeline.join()
|
||||
except KeyboardInterrupt:
|
||||
pipeline.terminate()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
@@ -1,47 +1,63 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
from pytube import YouTube
|
||||
import yt_dlp
|
||||
from jsonargparse import auto_cli
|
||||
from yt_dlp.utils import DownloadError
|
||||
|
||||
|
||||
def main(url: str, output_path: str | None, file_name: str | None) -> None:
|
||||
yt = YouTube(url)
|
||||
stream = yt.streams.get_highest_resolution()
|
||||
def _build_ydl_opts(output_path: str | None, file_name: str | None) -> dict[str, Any]:
|
||||
out_dir = output_path or "."
|
||||
|
||||
if not os.path.exists(output_path):
|
||||
os.makedirs(output_path)
|
||||
if not os.path.exists(out_dir):
|
||||
os.makedirs(out_dir)
|
||||
|
||||
stream.download(output_path=output_path, filename=file_name)
|
||||
final_name = file_name if file_name else yt.title
|
||||
name_template = file_name if file_name else "%(title)s.%(ext)s"
|
||||
|
||||
return {
|
||||
"format": (
|
||||
"bestvideo[ext=mp4][vcodec!*=av01][height<=2160]+bestaudio[ext=m4a]/"
|
||||
"best[ext=mp4][vcodec!*=av01][height<=2160]/"
|
||||
"bestvideo+bestaudio/best"
|
||||
),
|
||||
"merge_output_format": "mp4",
|
||||
"outtmpl": os.path.join(out_dir, name_template),
|
||||
"quiet": False,
|
||||
"noplaylist": True,
|
||||
}
|
||||
|
||||
|
||||
def main(
|
||||
url: str, output_path: str = "data/source", file_name: str = "video.mp4"
|
||||
) -> None:
|
||||
"""
|
||||
Download a specific YouTube video by providing its URL.
|
||||
|
||||
Args:
|
||||
url: The full URL of the YouTube video you wish to download.
|
||||
output_path: Specifies the directory where the video will be saved.
|
||||
file_name: Sets the name of the saved video file.
|
||||
"""
|
||||
# ssl._create_default_https_context = ssl._create_unverified_context
|
||||
ydl_opts = _build_ydl_opts(output_path, file_name)
|
||||
|
||||
try:
|
||||
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
||||
ydl.download([url])
|
||||
except DownloadError as err:
|
||||
print(f"Download failed: {err}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
final_name = file_name if file_name else "the video title"
|
||||
final_path = output_path if output_path else "current directory"
|
||||
print(f"Download completed! Video saved as '{final_name}' in '{final_path}'.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Download a specific YouTube video by providing its URL."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--url",
|
||||
type=str,
|
||||
required=True,
|
||||
help="The full URL of the YouTube video you wish to download.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output_path",
|
||||
type=str,
|
||||
default="data/source",
|
||||
required=False,
|
||||
help="Optional. Specifies the directory where the video will be saved.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--file_name",
|
||||
type=str,
|
||||
default="video.mp4",
|
||||
required=False,
|
||||
help="Optional. Sets the name of the saved video file.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
main(url=args.url, output_path=args.output_path, file_name=args.file_name)
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
from jsonargparse import auto_cli
|
||||
|
||||
import supervision as sv
|
||||
|
||||
@@ -126,6 +126,13 @@ def save_polygons_to_json(polygons, target_path):
|
||||
|
||||
|
||||
def main(source_path: str, zone_configuration_path: str) -> None:
|
||||
"""
|
||||
Interactively draw polygons on images or video frames and save the annotations.
|
||||
|
||||
Args:
|
||||
source_path: Path to the source image or video file for drawing polygons.
|
||||
zone_configuration_path: Path where the polygon annotations saved as JSON file.
|
||||
"""
|
||||
global current_mouse_position
|
||||
original_image = resolve_source(source_path=source_path)
|
||||
if original_image is None:
|
||||
@@ -155,24 +162,7 @@ def main(source_path: str, zone_configuration_path: str) -> None:
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Interactively draw polygons on images or video frames and save "
|
||||
"the annotations."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source_path",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Path to the source image or video file for drawing polygons.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--zone_configuration_path",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Path where the polygon annotations will be saved as a JSON file.",
|
||||
)
|
||||
arguments = parser.parse_args()
|
||||
main(
|
||||
source_path=arguments.source_path,
|
||||
zone_configuration_path=arguments.zone_configuration_path,
|
||||
)
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
@@ -6,12 +5,20 @@ from glob import glob
|
||||
from threading import Thread
|
||||
|
||||
import yaml
|
||||
from jsonargparse import auto_cli
|
||||
|
||||
SERVER_CONFIG = {"protocols": ["tcp"], "paths": {"all": {"source": "publisher"}}}
|
||||
BASE_STREAM_URL = "rtsp://localhost:8554/live"
|
||||
|
||||
|
||||
def main(video_directory: str, number_of_streams: int) -> None:
|
||||
def main(video_directory: str, number_of_streams: int = 6) -> None:
|
||||
"""
|
||||
Script to stream videos using RTSP protocol.
|
||||
|
||||
Args:
|
||||
video_directory: Directory containing video files to stream.
|
||||
number_of_streams: Number of video files to stream.
|
||||
"""
|
||||
video_files = find_video_files_in_directory(video_directory, number_of_streams)
|
||||
try:
|
||||
with tempfile.TemporaryDirectory() as temporary_directory:
|
||||
@@ -77,28 +84,12 @@ def run_command_in_thread(command: list) -> Thread:
|
||||
|
||||
|
||||
def run_command(command: list) -> int:
|
||||
process = subprocess.run(command)
|
||||
process = subprocess.run(command) # noqa: S603 # TODO: Validate command input to prevent execution of untrusted input
|
||||
return process.returncode
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Script to stream videos using RTSP protocol."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--video_directory",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Directory containing video files to stream.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--number_of_streams",
|
||||
type=int,
|
||||
default=6,
|
||||
help="Number of video files to stream.",
|
||||
)
|
||||
arguments = parser.parse_args()
|
||||
main(
|
||||
video_directory=arguments.video_directory,
|
||||
number_of_streams=arguments.number_of_streams,
|
||||
)
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import argparse
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
from ultralytics import YOLO
|
||||
@@ -16,14 +14,26 @@ LABEL_ANNOTATOR = sv.LabelAnnotator(
|
||||
|
||||
|
||||
def main(
|
||||
source_video_path: str,
|
||||
zone_configuration_path: str,
|
||||
weights: str,
|
||||
device: str,
|
||||
confidence: float,
|
||||
iou: float,
|
||||
classes: list[int],
|
||||
source_video_path: str,
|
||||
weights: str = "yolov8s.pt",
|
||||
device: str = "cpu",
|
||||
confidence_threshold: float = 0.3,
|
||||
iou_threshold: float = 0.7,
|
||||
classes: list[int] = [],
|
||||
) -> None:
|
||||
"""
|
||||
Calculating detections dwell time in zones, using video file.
|
||||
|
||||
Args:
|
||||
zone_configuration_path: Path to the zone configuration JSON file
|
||||
source_video_path: Path to the source video file
|
||||
weights: Path to the model weights file
|
||||
device: Computation device ('cpu', 'mps' or 'cuda')
|
||||
confidence_threshold: Confidence level for detections (0 to 1)
|
||||
iou_threshold: IOU threshold for non-max suppression
|
||||
classes: List of class IDs to track. If empty, all classes are tracked
|
||||
"""
|
||||
model = YOLO(weights)
|
||||
tracker = sv.ByteTrack(minimum_matching_threshold=0.5)
|
||||
video_info = sv.VideoInfo.from_video_path(video_path=source_video_path)
|
||||
@@ -40,10 +50,15 @@ def main(
|
||||
timers = [FPSBasedTimer(video_info.fps) for _ in zones]
|
||||
|
||||
for frame in frames_generator:
|
||||
results = model(frame, verbose=False, device=device, conf=confidence)[0]
|
||||
results = model(
|
||||
frame,
|
||||
verbose=False,
|
||||
device=device,
|
||||
conf=confidence_threshold,
|
||||
iou=iou_threshold,
|
||||
)[0]
|
||||
detections = sv.Detections.from_ultralytics(results)
|
||||
detections = detections[find_in_list(detections.class_id, classes)]
|
||||
detections = detections.with_nms(threshold=iou)
|
||||
detections = tracker.update_with_detections(detections)
|
||||
|
||||
annotated_frame = frame.copy()
|
||||
@@ -80,60 +95,7 @@ def main(
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Calculating detections dwell time in zones, using video file."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--zone_configuration_path",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Path to the zone configuration JSON file.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source_video_path",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Path to the source video file.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--weights",
|
||||
type=str,
|
||||
default="yolov8s.pt",
|
||||
help="Path to the model weights file. Default is 'yolov8s.pt'.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--device",
|
||||
type=str,
|
||||
default="cpu",
|
||||
help="Computation device ('cpu', 'mps' or 'cuda'). Default is 'cpu'.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--confidence_threshold",
|
||||
type=float,
|
||||
default=0.3,
|
||||
help="Confidence level for detections (0 to 1). Default is 0.3.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--iou_threshold",
|
||||
default=0.7,
|
||||
type=float,
|
||||
help="IOU threshold for non-max suppression. Default is 0.7.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--classes",
|
||||
nargs="*",
|
||||
type=int,
|
||||
default=[],
|
||||
help="List of class IDs to track. If empty, all classes are tracked.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
main(
|
||||
source_video_path=args.source_video_path,
|
||||
zone_configuration_path=args.zone_configuration_path,
|
||||
weights=args.weights,
|
||||
device=args.device,
|
||||
confidence=args.confidence_threshold,
|
||||
iou=args.iou_threshold,
|
||||
classes=args.classes,
|
||||
)
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import argparse
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
from ultralytics import YOLO
|
||||
@@ -16,14 +14,26 @@ LABEL_ANNOTATOR = sv.LabelAnnotator(
|
||||
|
||||
|
||||
def main(
|
||||
rtsp_url: str,
|
||||
zone_configuration_path: str,
|
||||
weights: str,
|
||||
device: str,
|
||||
confidence: float,
|
||||
iou: float,
|
||||
classes: list[int],
|
||||
rtsp_url: str,
|
||||
weights: str = "yolov8s.pt",
|
||||
device: str = "cpu",
|
||||
confidence_threshold: float = 0.3,
|
||||
iou_threshold: float = 0.7,
|
||||
classes: list[int] = [],
|
||||
) -> None:
|
||||
"""
|
||||
Calculating detections dwell time in zones, using RTSP stream.
|
||||
|
||||
Args:
|
||||
zone_configuration_path: Path to the zone configuration JSON file
|
||||
rtsp_url: Complete RTSP URL for the video stream
|
||||
weights: Path to the model weights file
|
||||
device: Computation device ('cpu', 'mps' or 'cuda')
|
||||
confidence_threshold: Confidence level for detections (0 to 1)
|
||||
iou_threshold: IOU threshold for non-max suppression
|
||||
classes: List of class IDs to track. If empty, all classes are tracked
|
||||
"""
|
||||
model = YOLO(weights)
|
||||
tracker = sv.ByteTrack(minimum_matching_threshold=0.5)
|
||||
frames_generator = get_stream_frames_generator(rtsp_url=rtsp_url)
|
||||
@@ -43,10 +53,15 @@ def main(
|
||||
fps_monitor.tick()
|
||||
fps = fps_monitor.fps
|
||||
|
||||
results = model(frame, verbose=False, device=device, conf=confidence)[0]
|
||||
results = model(
|
||||
frame,
|
||||
verbose=False,
|
||||
device=device,
|
||||
conf=confidence_threshold,
|
||||
iou=iou_threshold,
|
||||
)[0]
|
||||
detections = sv.Detections.from_ultralytics(results)
|
||||
detections = detections[find_in_list(detections.class_id, classes)]
|
||||
detections = detections.with_nms(threshold=iou)
|
||||
detections = tracker.update_with_detections(detections)
|
||||
|
||||
annotated_frame = frame.copy()
|
||||
@@ -90,60 +105,7 @@ def main(
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Calculating detections dwell time in zones, using RTSP stream."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--zone_configuration_path",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Path to the zone configuration JSON file.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--rtsp_url",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Complete RTSP URL for the video stream.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--weights",
|
||||
type=str,
|
||||
default="yolov8s.pt",
|
||||
help="Path to the model weights file. Default is 'yolov8s.pt'.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--device",
|
||||
type=str,
|
||||
default="cpu",
|
||||
help="Computation device ('cpu', 'mps' or 'cuda'). Default is 'cpu'.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--confidence_threshold",
|
||||
type=float,
|
||||
default=0.3,
|
||||
help="Confidence level for detections (0 to 1). Default is 0.3.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--iou_threshold",
|
||||
default=0.7,
|
||||
type=float,
|
||||
help="IOU threshold for non-max suppression. Default is 0.7.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--classes",
|
||||
nargs="*",
|
||||
type=int,
|
||||
default=[],
|
||||
help="List of class IDs to track. If empty, all classes are tracked.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
main(
|
||||
rtsp_url=args.rtsp_url,
|
||||
zone_configuration_path=args.zone_configuration_path,
|
||||
weights=args.weights,
|
||||
device=args.device,
|
||||
confidence=args.confidence_threshold,
|
||||
iou=args.iou_threshold,
|
||||
classes=args.classes,
|
||||
)
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import argparse
|
||||
from __future__ import annotations
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
@@ -77,19 +77,35 @@ class CustomSink:
|
||||
|
||||
|
||||
def main(
|
||||
rtsp_url: str,
|
||||
zone_configuration_path: str,
|
||||
weights: str,
|
||||
device: str,
|
||||
confidence: float,
|
||||
iou: float,
|
||||
classes: list[int],
|
||||
rtsp_url: str,
|
||||
weights: str = "yolov8s.pt",
|
||||
device: str = "cpu",
|
||||
confidence_threshold: float = 0.3,
|
||||
iou_threshold: float = 0.7,
|
||||
classes: list[int] = [],
|
||||
) -> None:
|
||||
"""
|
||||
Calculating detections dwell time in zones, using RTSP stream.
|
||||
|
||||
Args:
|
||||
zone_configuration_path: Path to the zone configuration JSON file
|
||||
rtsp_url: Complete RTSP URL for the video stream
|
||||
weights: Path to the model weights file
|
||||
device: Computation device ('cpu', 'mps' or 'cuda')
|
||||
confidence_threshold: Confidence level for detections (0 to 1)
|
||||
iou_threshold: IOU threshold for non-max suppression
|
||||
classes: List of class IDs to track. If empty, all classes are tracked
|
||||
"""
|
||||
model = YOLO(weights)
|
||||
|
||||
def inference_callback(frame: VideoFrame) -> sv.Detections:
|
||||
results = model(frame.image, verbose=False, conf=confidence, device=device)[0]
|
||||
return sv.Detections.from_ultralytics(results).with_nms(threshold=iou)
|
||||
def inference_callback(frames: list[VideoFrame]) -> list[sv.Detections]:
|
||||
results = model(
|
||||
frames[0].image, verbose=False, conf=confidence_threshold, device=device
|
||||
)[0]
|
||||
return [
|
||||
sv.Detections.from_ultralytics(results).with_nms(threshold=iou_threshold)
|
||||
]
|
||||
|
||||
sink = CustomSink(zone_configuration_path=zone_configuration_path, classes=classes)
|
||||
|
||||
@@ -108,60 +124,7 @@ def main(
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Calculating detections dwell time in zones, using RTSP stream."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--zone_configuration_path",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Path to the zone configuration JSON file.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--rtsp_url",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Complete RTSP URL for the video stream.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--weights",
|
||||
type=str,
|
||||
default="yolov8s.pt",
|
||||
help="Path to the model weights file. Default is 'yolov8s.pt'.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--device",
|
||||
type=str,
|
||||
default="cpu",
|
||||
help="Computation device ('cpu', 'mps' or 'cuda'). Default is 'cpu'.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--confidence_threshold",
|
||||
type=float,
|
||||
default=0.3,
|
||||
help="Confidence level for detections (0 to 1). Default is 0.3.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--iou_threshold",
|
||||
default=0.7,
|
||||
type=float,
|
||||
help="IOU threshold for non-max suppression. Default is 0.7.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--classes",
|
||||
nargs="*",
|
||||
type=int,
|
||||
default=[],
|
||||
help="List of class IDs to track. If empty, all classes are tracked.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
main(
|
||||
rtsp_url=args.rtsp_url,
|
||||
zone_configuration_path=args.zone_configuration_path,
|
||||
weights=args.weights,
|
||||
device=args.device,
|
||||
confidence=args.confidence_threshold,
|
||||
iou=args.iou_threshold,
|
||||
classes=args.classes,
|
||||
)
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
|
||||
@@ -11,17 +11,18 @@ class FPSBasedTimer:
|
||||
per second (FPS).
|
||||
|
||||
Attributes:
|
||||
fps (int): The frame rate of the video stream, used to calculate time durations.
|
||||
fps (float): The frame rate of the video stream, used to calculate
|
||||
time durations.
|
||||
frame_id (int): The current frame number in the sequence.
|
||||
tracker_id2frame_id (Dict[int, int]): Maps each tracker's ID to the frame number
|
||||
at which it was first detected.
|
||||
"""
|
||||
|
||||
def __init__(self, fps: int = 30) -> None:
|
||||
def __init__(self, fps: float = 30) -> None:
|
||||
"""Initializes the FPSBasedTimer with the specified frames per second rate.
|
||||
|
||||
Args:
|
||||
fps (int): The frame rate of the video stream. Defaults to 30.
|
||||
fps (float): The frame rate of the video stream. Defaults to 30.
|
||||
"""
|
||||
self.fps = fps
|
||||
self.frame_id = 0
|
||||
|
||||
@@ -14,17 +14,17 @@ detection and Supervision for tracking and annotation.
|
||||
cd supervision/examples/tracking
|
||||
```
|
||||
|
||||
- setup python environment and activate it \[optional\]
|
||||
- setup python environment and activate it [optional]
|
||||
|
||||
```bash
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
uv venv
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
- install required dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
uv pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## 🛠️ script arguments
|
||||
@@ -79,7 +79,7 @@ detection and Supervision for tracking and annotation.
|
||||
|
||||
```bash
|
||||
python inference_example.py \
|
||||
--roboflow_api_key <ROBOFLOW API KEY> \
|
||||
--roboflow_api_key "ROBOFLOW_API_KEY" \
|
||||
--source_video_path input.mp4 \
|
||||
--target_video_path tracking_result.mp4
|
||||
```
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import argparse
|
||||
import os
|
||||
|
||||
from inference.models.utils import get_roboflow_model
|
||||
@@ -7,15 +6,33 @@ from tqdm import tqdm
|
||||
import supervision as sv
|
||||
|
||||
|
||||
def process_video(
|
||||
roboflow_api_key: str,
|
||||
model_id: str,
|
||||
def main(
|
||||
source_video_path: str,
|
||||
target_video_path: str,
|
||||
roboflow_api_key: str,
|
||||
model_id: str = "yolov8x-1280",
|
||||
confidence_threshold: float = 0.3,
|
||||
iou_threshold: float = 0.7,
|
||||
) -> None:
|
||||
model = get_roboflow_model(model_id=model_id, api_key=roboflow_api_key)
|
||||
"""
|
||||
Video Processing with Inference and ByteTrack.
|
||||
|
||||
Args:
|
||||
source_video_path: Path to the source video file
|
||||
target_video_path: Path to the target video file (output)
|
||||
roboflow_api_key: Roboflow API key
|
||||
model_id: Roboflow model ID
|
||||
confidence_threshold: Confidence threshold for the model
|
||||
iou_threshold: IOU threshold for the model
|
||||
"""
|
||||
api_key = os.environ.get("ROBOFLOW_API_KEY", roboflow_api_key)
|
||||
if api_key is None:
|
||||
raise ValueError(
|
||||
"Roboflow API key is missing. Please provide it as an argument or set the "
|
||||
"ROBOFLOW_API_KEY environment variable."
|
||||
)
|
||||
|
||||
model = get_roboflow_model(model_id=model_id, api_key=api_key)
|
||||
|
||||
tracker = sv.ByteTrack()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
@@ -43,59 +60,7 @@ def process_video(
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Video Processing with Inference and ByteTrack"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--model_id",
|
||||
default="yolov8x-1280",
|
||||
help="Roboflow model ID",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source_video_path",
|
||||
required=True,
|
||||
help="Path to the source video file",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--target_video_path",
|
||||
required=True,
|
||||
help="Path to the target video file (output)",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--confidence_threshold",
|
||||
default=0.3,
|
||||
help="Confidence threshold for the model",
|
||||
type=float,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--iou_threshold", default=0.7, help="IOU threshold for the model", type=float
|
||||
)
|
||||
parser.add_argument(
|
||||
"--roboflow_api_key",
|
||||
default=None,
|
||||
help="Roboflow API key",
|
||||
type=str,
|
||||
)
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
api_key = args.roboflow_api_key
|
||||
api_key = os.environ.get("ROBOFLOW_API_KEY", api_key)
|
||||
if api_key is None:
|
||||
raise ValueError(
|
||||
"Roboflow API key is missing. Please provide it as an argument or set the "
|
||||
"ROBOFLOW_API_KEY environment variable."
|
||||
)
|
||||
args.roboflow_api_key = api_key
|
||||
|
||||
process_video(
|
||||
roboflow_api_key=args.roboflow_api_key,
|
||||
model_id=args.model_id,
|
||||
source_video_path=args.source_video_path,
|
||||
target_video_path=args.target_video_path,
|
||||
confidence_threshold=args.confidence_threshold,
|
||||
iou_threshold=args.iou_threshold,
|
||||
)
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
|
||||
@@ -2,3 +2,4 @@ inference
|
||||
supervision
|
||||
tqdm
|
||||
ultralytics
|
||||
jsonargparse[signatures]
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
import argparse
|
||||
|
||||
from tqdm import tqdm
|
||||
from ultralytics import YOLO
|
||||
|
||||
import supervision as sv
|
||||
|
||||
|
||||
def process_video(
|
||||
def main(
|
||||
source_weights_path: str,
|
||||
source_video_path: str,
|
||||
target_video_path: str,
|
||||
confidence_threshold: float = 0.3,
|
||||
iou_threshold: float = 0.7,
|
||||
) -> None:
|
||||
"""
|
||||
Video Processing with YOLO and ByteTrack.
|
||||
|
||||
Args:
|
||||
source_weights_path: Path to the source weights file
|
||||
source_video_path: Path to the source video file
|
||||
target_video_path: Path to the target video file (output)
|
||||
confidence_threshold: Confidence threshold for the model
|
||||
iou_threshold: IOU threshold for the model
|
||||
"""
|
||||
model = YOLO(source_weights_path)
|
||||
|
||||
tracker = sv.ByteTrack()
|
||||
@@ -41,43 +49,7 @@ def process_video(
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Video Processing with YOLO and ByteTrack"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source_weights_path",
|
||||
required=True,
|
||||
help="Path to the source weights file",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source_video_path",
|
||||
required=True,
|
||||
help="Path to the source video file",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--target_video_path",
|
||||
required=True,
|
||||
help="Path to the target video file (output)",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--confidence_threshold",
|
||||
default=0.3,
|
||||
help="Confidence threshold for the model",
|
||||
type=float,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--iou_threshold", default=0.7, help="IOU threshold for the model", type=float
|
||||
)
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
process_video(
|
||||
source_weights_path=args.source_weights_path,
|
||||
source_video_path=args.source_video_path,
|
||||
target_video_path=args.target_video_path,
|
||||
confidence_threshold=args.confidence_threshold,
|
||||
iou_threshold=args.iou_threshold,
|
||||
)
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
|
||||
@@ -17,17 +17,17 @@ https://github.com/roboflow/supervision/assets/26109316/c9436828-9fbf-4c25-ae8c-
|
||||
cd supervision/examples/traffic_analysis
|
||||
```
|
||||
|
||||
- setup python environment and activate it \[optional\]
|
||||
- setup python environment and activate it [optional]
|
||||
|
||||
```bash
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
uv venv
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
- install required dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
uv pip install -r requirements.txt
|
||||
```
|
||||
|
||||
- download `traffic_analysis.pt` and `traffic_analysis.mov` files
|
||||
@@ -101,7 +101,7 @@ https://github.com/roboflow/supervision/assets/26109316/c9436828-9fbf-4c25-ae8c-
|
||||
|
||||
```bash
|
||||
python inference_example.py \
|
||||
--roboflow_api_key <ROBOFLOW API KEY> \
|
||||
--roboflow_api_key "ROBOFLOW_API_KEY" \
|
||||
--source_video_path data/traffic_analysis.mov \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.5 \
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
from collections.abc import Iterable
|
||||
|
||||
@@ -180,62 +179,47 @@ class VideoProcessor:
|
||||
return self.annotate_frame(frame, detections)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Traffic Flow Analysis with Inference and ByteTrack"
|
||||
)
|
||||
def main(
|
||||
source_video_path: str,
|
||||
target_video_path: str,
|
||||
roboflow_api_key: str,
|
||||
model_id: str = "vehicle-count-in-drone-video/6",
|
||||
confidence_threshold: float = 0.3,
|
||||
iou_threshold: float = 0.7,
|
||||
) -> None:
|
||||
"""
|
||||
Traffic Flow Analysis with Inference and ByteTrack.
|
||||
|
||||
parser.add_argument(
|
||||
"--model_id",
|
||||
default="vehicle-count-in-drone-video/6",
|
||||
help="Roboflow model ID",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--roboflow_api_key",
|
||||
default=None,
|
||||
help="Roboflow API KEY",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source_video_path",
|
||||
required=True,
|
||||
help="Path to the source video file",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--target_video_path",
|
||||
default=None,
|
||||
help="Path to the target video file (output)",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--confidence_threshold",
|
||||
default=0.3,
|
||||
help="Confidence threshold for the model",
|
||||
type=float,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--iou_threshold", default=0.7, help="IOU threshold for the model", type=float
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
api_key = args.roboflow_api_key
|
||||
Args:
|
||||
source_video_path: Path to the source video file
|
||||
target_video_path: Path to the target video file (output)
|
||||
roboflow_api_key: Roboflow API key
|
||||
model_id: Roboflow model ID
|
||||
confidence_threshold: Confidence threshold for the model
|
||||
iou_threshold: IOU threshold for the model
|
||||
"""
|
||||
api_key = roboflow_api_key
|
||||
api_key = os.environ.get("ROBOFLOW_API_KEY", api_key)
|
||||
if api_key is None:
|
||||
raise ValueError(
|
||||
"Roboflow API KEY is missing. Please provide it as an argument or set the "
|
||||
"ROBOFLOW_API_KEY environment variable."
|
||||
)
|
||||
args.roboflow_api_key = api_key
|
||||
roboflow_api_key = api_key
|
||||
|
||||
processor = VideoProcessor(
|
||||
roboflow_api_key=args.roboflow_api_key,
|
||||
model_id=args.model_id,
|
||||
source_video_path=args.source_video_path,
|
||||
target_video_path=args.target_video_path,
|
||||
confidence_threshold=args.confidence_threshold,
|
||||
iou_threshold=args.iou_threshold,
|
||||
roboflow_api_key=roboflow_api_key,
|
||||
model_id=model_id,
|
||||
source_video_path=source_video_path,
|
||||
target_video_path=target_video_path,
|
||||
confidence_threshold=confidence_threshold,
|
||||
iou_threshold=iou_threshold,
|
||||
)
|
||||
processor.process_video()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from jsonargparse import auto_cli, set_parsing_settings
|
||||
|
||||
set_parsing_settings(parse_optionals_as_positionals=True)
|
||||
auto_cli(main, as_positional=False)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user