Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
752818e577 | ||
|
|
2e38e8998f | ||
|
|
7878df14be | ||
|
|
dc449c4fff | ||
|
|
afcd0d6e5a | ||
|
|
6b48881de7 | ||
|
|
10beacabc0 | ||
|
|
f180f0f5be | ||
|
|
f906a2978f | ||
|
|
e4949a7de7 | ||
|
|
4e41e9b34f | ||
|
|
e804a01e85 | ||
|
|
d0559e104d | ||
|
|
fa8b6e125c | ||
|
|
47295cd5f7 | ||
|
|
2d48c5dba7 | ||
|
|
13b847d1dc | ||
|
|
7e19c633a6 | ||
|
|
8df41a5683 | ||
|
|
59b652d0f2 | ||
|
|
76429548b6 | ||
|
|
23301bbd17 | ||
|
|
77b7d3733a | ||
|
|
63388fdee9 | ||
|
|
c06d5f0131 | ||
|
|
a151685a57 | ||
|
|
cbaa605e08 | ||
|
|
fb1dbfd084 | ||
|
|
2c53eb10a8 | ||
|
|
d381f43e50 | ||
|
|
dc08d88c5c | ||
|
|
af562950a3 | ||
|
|
ee9568945a | ||
|
|
ea03d55d6a | ||
|
|
3f55c3022e | ||
|
|
a54e491d49 | ||
|
|
4c03f899b9 | ||
|
|
1b5c592abb | ||
|
|
a5cad09782 | ||
|
|
483cc0677f | ||
|
|
b2a1eb5b21 | ||
|
|
fa979cf0ab | ||
|
|
2a1542c465 | ||
|
|
e543aac6dd | ||
|
|
3ee6de3ed3 | ||
|
|
16848e9160 | ||
|
|
071feeadce | ||
|
|
9587439f8d | ||
|
|
a61a6c7144 | ||
|
|
6ae1dfdcb0 | ||
|
|
fc40438f35 | ||
|
|
730bbd246c | ||
|
|
656f639db5 | ||
|
|
663522bed8 | ||
|
|
324872303b | ||
|
|
651ed9d470 | ||
|
|
6aedf4874f | ||
|
|
d33c159a26 | ||
|
|
572ea32c0c | ||
|
|
3415c15365 | ||
|
|
2ed66bfffc | ||
|
|
c6591f609f | ||
|
|
ba7760f9ad | ||
|
|
3f05b107ed | ||
|
|
d4c6fd7426 | ||
|
|
6d8d4fdddb | ||
|
|
adec783d49 | ||
|
|
2cca69be00 | ||
|
|
894ee7f5f9 | ||
|
|
23085eaf8a | ||
|
|
4deb9597b2 | ||
|
|
392d769483 | ||
|
|
f088e16fe2 | ||
|
|
30cf394a7b | ||
|
|
beabf6810c | ||
|
|
dbd6104c8b | ||
|
|
2a5ce95472 | ||
|
|
77ae682120 | ||
|
|
4a0b3bd2cf | ||
|
|
a9aad6727c | ||
|
|
7c6668b58e | ||
|
|
385c26ad78 | ||
|
|
d57078a7ed | ||
|
|
7a5672bbcc | ||
|
|
e6f0a8f3f0 | ||
|
|
7b50eed7d9 | ||
|
|
7c2711745b | ||
|
|
dbade52364 | ||
|
|
63392ab640 | ||
|
|
b466076383 | ||
|
|
14b3c0cf8a | ||
|
|
092085efb1 | ||
|
|
b8d62f6e29 | ||
|
|
6f55d9de9e | ||
|
|
4b75b9bd82 | ||
|
|
55378df392 | ||
|
|
4fd1450f80 | ||
|
|
c894a7a763 | ||
|
|
a4a2d8c05d | ||
|
|
30010b9773 | ||
|
|
fd61460cc2 | ||
|
|
a80be9422f | ||
|
|
eb3b172139 | ||
|
|
9975b63216 | ||
|
|
3c4793a092 | ||
|
|
b6a6746f8b | ||
|
|
a9831323f3 | ||
|
|
e6f29a45a3 | ||
|
|
06f678e5d2 | ||
|
|
47c98173ea | ||
|
|
c9ea75e3ca | ||
|
|
517b25f27f | ||
|
|
40340ef8a6 | ||
|
|
3d62caf4f3 | ||
|
|
dab2194f47 | ||
|
|
c257221fe0 | ||
|
|
7c5320409c | ||
|
|
10f372fe28 | ||
|
|
64dc2e91c8 | ||
|
|
9a4781f2a5 | ||
|
|
1633522768 | ||
|
|
d061a4c89b | ||
|
|
059633435c | ||
|
|
001f85ad0f | ||
|
|
d519861b1e | ||
|
|
12509e756a | ||
|
|
fcf8b7f378 | ||
|
|
ea3b9a5584 | ||
|
|
f7d7786c56 | ||
|
|
a713d3650a | ||
|
|
5385ca744b | ||
|
|
34229c7ad7 | ||
|
|
4c88e03b6c | ||
|
|
37943b2d6f | ||
|
|
f3d27d8381 | ||
|
|
c416474b63 | ||
|
|
ae8e6b2976 | ||
|
|
7141f11c0b | ||
|
|
37eadfe305 | ||
|
|
a4dfcacbfd | ||
|
|
3d1416790b | ||
|
|
348069fb07 | ||
|
|
fac1581fcd | ||
|
|
d075eb1e47 | ||
|
|
eac2691c6e | ||
|
|
a0242b0594 | ||
|
|
1e2bdeee35 | ||
|
|
62a396fe90 | ||
|
|
9941417ced | ||
|
|
79390062ce | ||
|
|
66e02b77b0 | ||
|
|
816a990083 | ||
|
|
93d0313460 | ||
|
|
d229d2b069 | ||
|
|
28fa054fdc | ||
|
|
6889e33da5 | ||
|
|
228b4cff0c | ||
|
|
a5948c8992 | ||
|
|
853cbc428f | ||
|
|
d3f6570a89 | ||
|
|
d1c8adb33a | ||
|
|
5cd6844ff4 | ||
|
|
c0d5a7fd25 | ||
|
|
159c507d7f | ||
|
|
fdd317e53e | ||
|
|
370996358b | ||
|
|
def1cc4b33 | ||
|
|
5f0f90b699 | ||
|
|
ae9628e537 | ||
|
|
32eaf4eb50 | ||
|
|
02dac5adfe | ||
|
|
b3d12ed652 | ||
|
|
7bca2fe765 | ||
|
|
a90b8717ee | ||
|
|
b7e02add30 | ||
|
|
7626c32a56 | ||
|
|
fe93979e0b | ||
|
|
684522a7e8 | ||
|
|
bbbb37434c | ||
|
|
4bfee1096e | ||
|
|
dd0ab6a2a7 | ||
|
|
cdf66fd78d | ||
|
|
6d4b08c753 | ||
|
|
7c57a1b8de | ||
|
|
17e35e2bcf | ||
|
|
69017093e1 | ||
|
|
e54ce38458 | ||
|
|
46416e430c | ||
|
|
2ad5b60c66 | ||
|
|
512edd0c9b | ||
|
|
60b9b716e9 | ||
|
|
4b64d4ddca | ||
|
|
9ded219dfb | ||
|
|
987a8f269f | ||
|
|
a57f4f22ae | ||
|
|
88f113d14b | ||
|
|
8d18328dff | ||
|
|
7ea4b2ac13 | ||
|
|
284901c06b | ||
|
|
b2dc1b2a17 | ||
|
|
d9e3ee9465 | ||
|
|
37b5dd477b | ||
|
|
092809b2be | ||
|
|
742a824d2c | ||
|
|
c88de3641c | ||
|
|
aec0bc3529 | ||
|
|
89769e49f7 | ||
|
|
86f95f914a | ||
|
|
eaeb51a8db | ||
|
|
8d562c16a6 | ||
|
|
dd4ffc6d97 | ||
|
|
2d50beeaa9 | ||
|
|
7b3d88602b | ||
|
|
90168644de | ||
|
|
aae3c69ee3 | ||
|
|
3bb22abe0f | ||
|
|
d99565dcd1 | ||
|
|
3692fb65f9 | ||
|
|
1af9c7d629 | ||
|
|
1d9f0b1994 | ||
|
|
77c71dacac | ||
|
|
cfb462ca0b | ||
|
|
c12c562e29 | ||
|
|
10652efbc4 | ||
|
|
3532130b2d | ||
|
|
79b45a4ea8 | ||
|
|
cc8be562b0 | ||
|
|
a1d8c69654 | ||
|
|
4326b87191 | ||
|
|
38dbf5d05d | ||
|
|
5e76f86ff9 | ||
|
|
01c2912a64 | ||
|
|
5fedfe5220 | ||
|
|
c43ca06609 | ||
|
|
b72ad4656b | ||
|
|
fdb036ecd5 | ||
|
|
e4cf743c1c | ||
|
|
19677bede5 | ||
|
|
a6e1f037d7 | ||
|
|
0bfcdeab6f | ||
|
|
83d4386fa2 | ||
|
|
42bb4c2865 | ||
|
|
4ed05e76fd | ||
|
|
2084f57d76 | ||
|
|
1b101b49ac | ||
|
|
abcb055815 | ||
|
|
c3d7dc0ee3 | ||
|
|
c6c447c527 | ||
|
|
b66702c5ff | ||
|
|
920a2c0cda | ||
|
|
6728527f36 | ||
|
|
b9b802e9ea | ||
|
|
19b7ca0a3d | ||
|
|
01caa1f528 | ||
|
|
11978410f0 | ||
|
|
8e91865fd0 | ||
|
|
89b911d5f9 | ||
|
|
763be226a4 | ||
|
|
c52223b4b9 | ||
|
|
a0e909669d | ||
|
|
698c085518 | ||
|
|
6d7dba7572 | ||
|
|
a18fb116a1 | ||
|
|
c54b4be4f0 | ||
|
|
fe2781cf85 | ||
|
|
e1af717c59 | ||
|
|
58c5aa981b | ||
|
|
17f482b1ba | ||
|
|
b39ead2ef0 | ||
|
|
ee3aa9f08d | ||
|
|
01f0f9ebe7 | ||
|
|
659ddec360 | ||
|
|
8bb609eca0 | ||
|
|
62c2c6a668 | ||
|
|
d1e11684e7 | ||
|
|
f7c47b6edb | ||
|
|
5391671838 | ||
|
|
11355c5192 | ||
|
|
9ee1b5dc5a | ||
|
|
d645bd816e | ||
|
|
5cce8a1599 | ||
|
|
d388857057 | ||
|
|
940243fc18 | ||
|
|
48c63e1b17 | ||
|
|
6d03253370 | ||
|
|
5974054c6c | ||
|
|
62d869ca87 | ||
|
|
1f610d820b | ||
|
|
22d827fac4 | ||
|
|
9eee76a6d1 | ||
|
|
d3210ed0cc | ||
|
|
cfd02c8c5c | ||
|
|
fd1277f29a | ||
|
|
b380bd2df7 | ||
|
|
89be6f92d2 | ||
|
|
37cc16e7a8 | ||
|
|
e8983bf427 | ||
|
|
1f5b1c7e01 | ||
|
|
589d4f9152 | ||
|
|
4678dfc74a | ||
|
|
2704a24ff6 | ||
|
|
b8daa408bf | ||
|
|
46f7a221ae | ||
|
|
895bcfbddb | ||
|
|
ab99c3615d | ||
|
|
37cacec704 | ||
|
|
42b4d439c8 | ||
|
|
7229409fab | ||
|
|
353fa93305 | ||
|
|
ad4418a350 | ||
|
|
3b4cd4276f | ||
|
|
d1c07bc106 | ||
|
|
6f3491f03c | ||
|
|
37f0ad8b14 | ||
|
|
02735848b5 | ||
|
|
b760b96e4f | ||
|
|
f4850b870f | ||
|
|
ecf5b1334f | ||
|
|
066099f42d | ||
|
|
b26e8f5814 | ||
|
|
b70269947d | ||
|
|
b4d93ce6c9 | ||
|
|
a2e2e3725a | ||
|
|
ba559c2658 | ||
|
|
0e7f3cdfe0 | ||
|
|
40e16118f8 | ||
|
|
dbd4aa7798 | ||
|
|
98aedd7383 | ||
|
|
d56c566b62 | ||
|
|
f176f46d3a | ||
|
|
cf3c1e4e10 | ||
|
|
6c7ee37225 | ||
|
|
abd149fa1a | ||
|
|
76975478dd | ||
|
|
145ed41305 | ||
|
|
5c13d9c0dc | ||
|
|
40ef90c76e | ||
|
|
2efe3030cb | ||
|
|
74ae1eec87 | ||
|
|
ea776b58a2 | ||
|
|
3e8a88a8d4 | ||
|
|
fbd96d10c4 | ||
|
|
c17de5c11b | ||
|
|
bda4003453 | ||
|
|
eb3e1e6293 | ||
|
|
a9214b2fdb | ||
|
|
2c21e9ecc1 | ||
|
|
70dd0e1c1d | ||
|
|
b2762dafaf | ||
|
|
251b952438 | ||
|
|
4a7d9ceaa9 | ||
|
|
e5215b967c | ||
|
|
f60d89f508 | ||
|
|
1b95d1dea2 | ||
|
|
f8ff332872 | ||
|
|
5ac3999aeb | ||
|
|
75a4ff1c64 | ||
|
|
d50aa67ac0 | ||
|
|
847f132aa0 | ||
|
|
c6e701be1c | ||
|
|
4ce033c4d7 | ||
|
|
57870817f5 | ||
|
|
3eb5c0b024 | ||
|
|
ed6fe98e24 | ||
|
|
8d374a6282 | ||
|
|
5b09368a0e | ||
|
|
0012052cdd | ||
|
|
85f400c162 | ||
|
|
865c4e4231 | ||
|
|
e9d5f861a2 | ||
|
|
a879c1d39a | ||
|
|
213d59d952 | ||
|
|
62a4d45c6c | ||
|
|
086091a1e2 | ||
|
|
302041d8aa | ||
|
|
9822406cde | ||
|
|
1d3f61af85 | ||
|
|
9b35f8c3c4 | ||
|
|
c339315b31 | ||
|
|
8edf7bb941 | ||
|
|
7e7eb8bd5a | ||
|
|
b6ae599a21 | ||
|
|
a6897d4117 | ||
|
|
d333d67912 | ||
|
|
5242614b01 | ||
|
|
b86b660634 | ||
|
|
ef1f968289 | ||
|
|
337b0e4a5b | ||
|
|
892185e4c6 | ||
|
|
c7611e384b | ||
|
|
68a1052cfc | ||
|
|
bfe4f0f829 | ||
|
|
dcdeea85d1 | ||
|
|
6b3735001b | ||
|
|
f3f5585f31 | ||
|
|
a988b72822 | ||
|
|
94230c51dc | ||
|
|
b59f867bcb | ||
|
|
70bc4b9d88 | ||
|
|
c5dfb47d5c | ||
|
|
2530c41ffa | ||
|
|
4c928eda2e | ||
|
|
05b17c483a | ||
|
|
9659f7b2eb | ||
|
|
5fc7c3aa53 | ||
|
|
f1e32e7c07 | ||
|
|
75b6384a9a | ||
|
|
426e36dea1 | ||
|
|
53b26d0c05 | ||
|
|
201a186539 | ||
|
|
0f42a87ee8 | ||
|
|
62a3cfe480 | ||
|
|
ef85b325de | ||
|
|
16a4491b59 | ||
|
|
e433527942 | ||
|
|
12c6b873e3 | ||
|
|
baffd98314 | ||
|
|
a4f79b59f1 | ||
|
|
921e8f32d4 | ||
|
|
ebbc79aeae | ||
|
|
70869c7337 | ||
|
|
3231f7f498 | ||
|
|
22d73580db | ||
|
|
7862f9cc70 | ||
|
|
044d5039b2 | ||
|
|
c3e46b0f9c | ||
|
|
1523ee21af | ||
|
|
c70988123d | ||
|
|
e0db07a80f | ||
|
|
6d30eef4a9 | ||
|
|
124c7d9b90 | ||
|
|
58a6fd046d | ||
|
|
58e27f44f1 | ||
|
|
953ecba283 | ||
|
|
e1e00ffc52 | ||
|
|
b01b321eca | ||
|
|
c9dfdbe301 | ||
|
|
38a1d3ad90 | ||
|
|
68d668a1f2 | ||
|
|
8f61898964 | ||
|
|
02ed59b27b | ||
|
|
72d0b9b914 | ||
|
|
db0a078280 | ||
|
|
63f5bfd4f3 | ||
|
|
61ababd0b5 | ||
|
|
f8b6d3cf9a | ||
|
|
73ed766e97 | ||
|
|
d3d1bfc86c | ||
|
|
55d7c8daf3 | ||
|
|
66067cc0ac | ||
|
|
448a0ab7c0 | ||
|
|
f207eaca31 | ||
|
|
51e2f9e462 | ||
|
|
8755bc8391 | ||
|
|
eb0e50a2f9 | ||
|
|
d6aa72c0f2 | ||
|
|
2be5008b3c | ||
|
|
b09559a900 | ||
|
|
ef89d8ceff | ||
|
|
0b931e11cd | ||
|
|
0bc52bf6fe | ||
|
|
edf2277ea0 | ||
|
|
3863a1f2e9 | ||
|
|
78fd2147f1 | ||
|
|
5af3eab3d0 | ||
|
|
56c269a1ba | ||
|
|
52c9ec0def | ||
|
|
892bd87b6c | ||
|
|
23023e9764 | ||
|
|
3789f694c4 | ||
|
|
865cdd3bd1 | ||
|
|
121190be50 | ||
|
|
f01126fabb | ||
|
|
125a858654 | ||
|
|
1d4b61f20c | ||
|
|
edda8dea0b | ||
|
|
3b95c1d8bf | ||
|
|
1d77868080 | ||
|
|
eebd57a731 | ||
|
|
1860fdb0a4 | ||
|
|
31a3cca5d5 | ||
|
|
b45bee0761 | ||
|
|
103669bf0f | ||
|
|
5b39c81035 | ||
|
|
78f17cba2e | ||
|
|
36f0755767 | ||
|
|
9ddbad2300 | ||
|
|
c3b9db0258 | ||
|
|
dec29200a9 | ||
|
|
20c5f8da3d | ||
|
|
39d0e65592 | ||
|
|
58a9588a53 | ||
|
|
9522ac9a27 | ||
|
|
363aa0629e | ||
|
|
f499430aa3 | ||
|
|
06d1640700 | ||
|
|
d50c51fc52 | ||
|
|
e63611deb3 | ||
|
|
c81aec9d1b | ||
|
|
4a95aaa2c4 | ||
|
|
fedc1a47e0 | ||
|
|
db0d965e8d | ||
|
|
d4eb957201 | ||
|
|
4722459bce | ||
|
|
156dbd5046 | ||
|
|
ebd5e0661b | ||
|
|
d6d3213f62 | ||
|
|
7b22cfa741 | ||
|
|
356a86a469 | ||
|
|
c1444c6d14 | ||
|
|
5f86dbe29b | ||
|
|
6fbabeefff | ||
|
|
60c5292781 | ||
|
|
f8b60eab87 | ||
|
|
d88d8ed58b | ||
|
|
9bbd81e6ec | ||
|
|
93190b2bb6 | ||
|
|
a29d9599d7 | ||
|
|
787bbdac96 | ||
|
|
1b6fd01f7e | ||
|
|
65f606e197 | ||
|
|
0d86159de1 | ||
|
|
95f6ef0b22 | ||
|
|
0e26f1b22e | ||
|
|
cc640dc890 | ||
|
|
c211453def | ||
|
|
943a21412e | ||
|
|
f221019208 | ||
|
|
d01e76a9c7 | ||
|
|
5ea6d95fe0 | ||
|
|
270b5f564f | ||
|
|
47a7f929a2 | ||
|
|
130308c28b | ||
|
|
504fc59b68 | ||
|
|
856c72eb67 | ||
|
|
fef9f10c03 | ||
|
|
f8397bdce9 | ||
|
|
5d9d7446b8 | ||
|
|
e10caa2215 | ||
|
|
6551fd6c29 | ||
|
|
fa8378e50c | ||
|
|
7de1f5855d | ||
|
|
d208250123 | ||
|
|
2759beb293 | ||
|
|
9a56513371 | ||
|
|
7f85963314 | ||
|
|
6713523e9b | ||
|
|
3c79677039 | ||
|
|
d85ceb0c23 | ||
|
|
9d1549228f | ||
|
|
c94cfe2031 | ||
|
|
dc07f13dec | ||
|
|
fdda7254a3 | ||
|
|
81ee4f1c2c | ||
|
|
85e053eae6 | ||
|
|
887bb7c2cb | ||
|
|
e3eeb5f2cc | ||
|
|
a34f0e9597 | ||
|
|
6acfdbf502 | ||
|
|
1a870b9b1b | ||
|
|
784191a099 | ||
|
|
c9fa3d613f | ||
|
|
4644f21340 | ||
|
|
9be2fdf5c4 | ||
|
|
c532758693 | ||
|
|
14fc6c2dca | ||
|
|
ebdada92bd | ||
|
|
266bf3f10c | ||
|
|
df9257b8fc | ||
|
|
a0655aed08 | ||
|
|
1e43de0a41 | ||
|
|
3da10edb47 | ||
|
|
db2aa721f8 | ||
|
|
a05882add1 | ||
|
|
7ad0bc361b | ||
|
|
f307f981f6 | ||
|
|
79858ad9d3 | ||
|
|
1c30493ee0 | ||
|
|
2961045177 | ||
|
|
1f0c278c4e | ||
|
|
32d22efedc | ||
|
|
128358f183 | ||
|
|
ef2b604df6 | ||
|
|
1b7344d6cc | ||
|
|
685929fbd9 | ||
|
|
95add3679d | ||
|
|
9d2dc08e34 | ||
|
|
420b832e8e | ||
|
|
d25016e428 | ||
|
|
b9e9adaab3 | ||
|
|
96b8d53123 | ||
|
|
00cba6e8cf | ||
|
|
0ddb62d024 | ||
|
|
d9b297a1d1 | ||
|
|
4919b84dd7 | ||
|
|
3cf9634907 | ||
|
|
cbd62ef741 | ||
|
|
30f75bd3a9 | ||
|
|
c9159f9f24 | ||
|
|
d5efa5dbac | ||
|
|
6e07b17422 | ||
|
|
ef52dfff05 | ||
|
|
ade8976eaf | ||
|
|
abec8da828 | ||
|
|
675d3e58bc | ||
|
|
c30c9f4b4a | ||
|
|
9203116468 | ||
|
|
a9fe5ccd9a | ||
|
|
f5c50013b6 | ||
|
|
a063f0aa2b | ||
|
|
23ce82f48d | ||
|
|
ea4bc3f875 | ||
|
|
cceecb095b | ||
|
|
a20d784e25 | ||
|
|
ce53f6c99d | ||
|
|
2f942d296a | ||
|
|
06f6286452 | ||
|
|
b987f98795 | ||
|
|
f20a3149c8 | ||
|
|
26987d7cf5 | ||
|
|
370e80d7ec | ||
|
|
8a912c5dde | ||
|
|
323e75844f | ||
|
|
9410f033bb | ||
|
|
b25f0b1ac2 | ||
|
|
e52989280a | ||
|
|
621f8e4d29 | ||
|
|
7a1c3149fd | ||
|
|
5467db4447 | ||
|
|
96bdcb3dea | ||
|
|
54278ea1f0 | ||
|
|
4e9aec6d2a | ||
|
|
db79350caa | ||
|
|
a6e0177cb9 | ||
|
|
d3968822a7 | ||
|
|
b06d979525 | ||
|
|
1ed782560d | ||
|
|
3c41c9721d | ||
|
|
e805212ae6 | ||
|
|
80567df1ea | ||
|
|
fab89d2374 | ||
|
|
a937fa5100 | ||
|
|
418ec5bad2 | ||
|
|
9b52d62bde | ||
|
|
a298ceb18c | ||
|
|
c7c02bb4de | ||
|
|
c1e249e2bb | ||
|
|
a7debec87d | ||
|
|
b4b941fe1b | ||
|
|
3ddc4bd007 | ||
|
|
27c68f293d | ||
|
|
ee3d1768e3 | ||
|
|
f1124e04df | ||
|
|
55c8c07b90 | ||
|
|
6bb5929e09 | ||
|
|
6ac0f84e27 | ||
|
|
162f9ed6d2 | ||
|
|
7600bef8e7 | ||
|
|
9df96572ac | ||
|
|
8ff0d31e57 | ||
|
|
5160d20fbc | ||
|
|
9f730b9fb5 | ||
|
|
a7ecc57432 | ||
|
|
ec5790a90c | ||
|
|
57e5c108de | ||
|
|
84bb44d222 | ||
|
|
026a243f81 | ||
|
|
d1e5e031e3 | ||
|
|
a5bb93ddec | ||
|
|
fe27e44081 | ||
|
|
d21a6b1ee8 | ||
|
|
395d90b5a6 | ||
|
|
635272b892 | ||
|
|
0edb590bb5 | ||
|
|
5aa4216c86 | ||
|
|
1ae8fc798e | ||
|
|
e8ec271f0b | ||
|
|
2e55149d43 | ||
|
|
fb25fda6ef | ||
|
|
838b8c345a | ||
|
|
1f21354f02 | ||
|
|
6c03451279 | ||
|
|
ab983bba27 | ||
|
|
48a1f04060 | ||
|
|
4356a90fc9 | ||
|
|
5cda6e339e | ||
|
|
d6ebc433a7 | ||
|
|
cd2fd0d723 | ||
|
|
3653755996 | ||
|
|
e6686ba71d | ||
|
|
3b5b8709e5 | ||
|
|
aed3feba14 | ||
|
|
9e3a150196 | ||
|
|
acd23bf662 | ||
|
|
5532731910 | ||
|
|
029abbd614 | ||
|
|
46ecb808f7 | ||
|
|
5befed7332 | ||
|
|
15a192c63a | ||
|
|
317485b122 | ||
|
|
0f23c6d425 | ||
|
|
44d531fdb8 | ||
|
|
a3262a8f9c | ||
|
|
7e9dca632b | ||
|
|
cc7de0edb7 | ||
|
|
808814bce7 | ||
|
|
89000c1c33 | ||
|
|
71cbd82ef4 | ||
|
|
dff15cb8d6 | ||
|
|
befa12bb23 | ||
|
|
f86a2d3a82 | ||
|
|
3d7c1ba1ec | ||
|
|
a64a2fc243 | ||
|
|
b65d2cc73f | ||
|
|
207260aca1 | ||
|
|
81b27340e5 | ||
|
|
a92665e85c | ||
|
|
e7bf00aa95 | ||
|
|
392c6930bb | ||
|
|
ed253fef99 | ||
|
|
cef25cc601 | ||
|
|
e1b13e369c | ||
|
|
ed09f05424 | ||
|
|
369831eb76 | ||
|
|
0d17a8e6b5 | ||
|
|
2e4f05e8ec | ||
|
|
1891b2edde | ||
|
|
477faee1c7 | ||
|
|
5957b250f0 | ||
|
|
7d27e67966 | ||
|
|
c31d96dabe | ||
|
|
4ab9ae5a71 | ||
|
|
888da4e208 | ||
|
|
8f480ead6a | ||
|
|
cdc3b4d6b1 | ||
|
|
bae1797e83 | ||
|
|
7319a026ab | ||
|
|
509b6da063 | ||
|
|
10ea37b732 | ||
|
|
6ce126d6d8 | ||
|
|
ab76a47eb8 | ||
|
|
5261efebc3 | ||
|
|
ca5486dc5a | ||
|
|
eecc0dea91 | ||
|
|
cf7c95e700 | ||
|
|
366bc21d1e | ||
|
|
526ddacd2f | ||
|
|
ab45b19e7e | ||
|
|
6289d9cab0 | ||
|
|
85e0a544fe | ||
|
|
6f47cd318a | ||
|
|
66758448cb | ||
|
|
c8673fe517 | ||
|
|
b5a958745b | ||
|
|
cfd079bbcb | ||
|
|
0e8365ff80 | ||
|
|
758b9d60bd | ||
|
|
b40cddd144 | ||
|
|
dabd18c0de | ||
|
|
e1ffd0e8a9 | ||
|
|
731d580a83 | ||
|
|
d4f95eca45 | ||
|
|
0477499b77 | ||
|
|
29de4d245e | ||
|
|
2012ec7bcb | ||
|
|
5b3ce80a34 | ||
|
|
c3641de724 | ||
|
|
822dd4d555 | ||
|
|
09e88da71a | ||
|
|
31a2b26144 | ||
|
|
f7713a32aa | ||
|
|
c97a885f9e | ||
|
|
b1cc5c3564 | ||
|
|
91d8691bc4 | ||
|
|
072072e9b2 | ||
|
|
148bb63aa8 | ||
|
|
d60443751a | ||
|
|
1421f2c743 | ||
|
|
751ea27263 | ||
|
|
78b6020ea4 | ||
|
|
d08d22dec6 | ||
|
|
ee1f15eebe | ||
|
|
d755bca7c8 | ||
|
|
5565241ef5 | ||
|
|
a4cf2604ee | ||
|
|
3e860a5449 | ||
|
|
0305ceed45 | ||
|
|
4add8c871e | ||
|
|
6f566b5518 | ||
|
|
d6d71a0366 | ||
|
|
4c903cf7f2 | ||
|
|
b061f4fd84 | ||
|
|
2b73aea438 | ||
|
|
db958d9df8 | ||
|
|
f699f087db | ||
|
|
7d7e5f17a5 | ||
|
|
d486dd1083 | ||
|
|
5123085037 | ||
|
|
43041c26c5 | ||
|
|
ca2902cc46 | ||
|
|
2365ed47e1 | ||
|
|
78689590e6 | ||
|
|
d73187f3dd | ||
|
|
d1df96a953 | ||
|
|
c1650d3770 | ||
|
|
a72c5044f4 | ||
|
|
4e7f69a502 | ||
|
|
6d719fcd60 | ||
|
|
ac09b58620 | ||
|
|
b9cc02ecb9 | ||
|
|
00b00d2f9a | ||
|
|
5a9e85a60f | ||
|
|
fea77d86a7 | ||
|
|
11466ff261 | ||
|
|
80549933fc | ||
|
|
a428fac98d | ||
|
|
a109b264ef | ||
|
|
389b8968c2 | ||
|
|
b000d162e7 | ||
|
|
66e1672f37 | ||
|
|
52e7fe1d30 | ||
|
|
b61c5f23c6 | ||
|
|
0285bfa50a | ||
|
|
0069d6b289 | ||
|
|
ebba78528e | ||
|
|
da2bc575e0 | ||
|
|
25ea63042d | ||
|
|
24debfe377 | ||
|
|
414ee9166d | ||
|
|
edc4dd33b0 | ||
|
|
22b970e4d4 | ||
|
|
e367e307fb | ||
|
|
69ffca50d5 | ||
|
|
01fd82c43b | ||
|
|
f04bdb2491 | ||
|
|
aa6c673316 | ||
|
|
b2752ac6c6 | ||
|
|
7c038f1d45 | ||
|
|
b6547ba58e | ||
|
|
fde9cd4735 | ||
|
|
1d3e423617 | ||
|
|
9fc66563dd | ||
|
|
8a82f4a516 | ||
|
|
17d1934ed5 | ||
|
|
21fcf1bd04 | ||
|
|
ebd606ff00 | ||
|
|
f1241e4fe8 | ||
|
|
9de14b5c67 | ||
|
|
706c2d19b9 | ||
|
|
7a81c13543 | ||
|
|
4648276cc6 | ||
|
|
2c235dfce9 | ||
|
|
dacd8f5455 | ||
|
|
da261ff8cf | ||
|
|
a5ae72d7ae | ||
|
|
fd568e2419 | ||
|
|
7c7c3d83ef | ||
|
|
03dba9c7a2 | ||
|
|
1d0616640e | ||
|
|
f6437ab778 | ||
|
|
7ad22c73a8 | ||
|
|
54903b1cc0 | ||
|
|
e566ac9eb3 | ||
|
|
f95f81eaf3 | ||
|
|
f2ede43d0e | ||
|
|
f2ef9f817c | ||
|
|
7bba367f98 | ||
|
|
977892ad1f | ||
|
|
2ca696e064 | ||
|
|
3bb384d789 | ||
|
|
ac1fa2edca | ||
|
|
4ce9d76b65 | ||
|
|
1a1a2d27ce | ||
|
|
c9598ef732 | ||
|
|
4c01eb4a3d | ||
|
|
f9471184d7 | ||
|
|
bb0e4ec059 | ||
|
|
7f8b6d3c5e | ||
|
|
c6a756352a | ||
|
|
66456a893c | ||
|
|
8704a46e96 | ||
|
|
3de8ca609f | ||
|
|
586387ab3a | ||
|
|
37a988fd9f | ||
|
|
cb6045b026 | ||
|
|
9956d5dd82 | ||
|
|
b103ce4eb7 | ||
|
|
22df9a5252 | ||
|
|
b030d1dc9f | ||
|
|
387a759614 | ||
|
|
5dd8910e02 | ||
|
|
b19c812f58 | ||
|
|
e58939235f | ||
|
|
b8e1612511 | ||
|
|
a0abf2dee7 | ||
|
|
f5bced4510 | ||
|
|
a8d91b0ae5 | ||
|
|
307821ca25 | ||
|
|
c5d052ed16 | ||
|
|
e2f33faa67 | ||
|
|
1f6694ba50 | ||
|
|
01922f849e | ||
|
|
3fb6a7ba6e | ||
|
|
16b8102900 | ||
|
|
acb5dd0dd4 | ||
|
|
b325eebe00 | ||
|
|
eb0041d275 | ||
|
|
3ccd74d93e | ||
|
|
e0d06f9a64 | ||
|
|
d63e7b4677 | ||
|
|
0210f51944 | ||
|
|
073500bf75 | ||
|
|
60d33ca140 | ||
|
|
e65892c80d | ||
|
|
24320405b4 | ||
|
|
b5ea2cd1cb | ||
|
|
c26814da87 | ||
|
|
da8761bd96 | ||
|
|
b8a2fb50d4 | ||
|
|
310b566c9b | ||
|
|
43c31f933c | ||
|
|
3a54afe506 | ||
|
|
891b8523f0 | ||
|
|
bc12bd38b4 | ||
|
|
0cb66e5551 | ||
|
|
b81e92ca94 | ||
|
|
7ca5b01a75 | ||
|
|
44753536f3 | ||
|
|
d774c91d7b | ||
|
|
2abb76e167 | ||
|
|
75d4b9f20a | ||
|
|
056401a858 | ||
|
|
b678ac929e | ||
|
|
b7b19b0357 | ||
|
|
46ac1812d8 | ||
|
|
b21a478528 | ||
|
|
65f2bbba57 | ||
|
|
d0111bf41d | ||
|
|
31db574e63 | ||
|
|
c930dd06ec | ||
|
|
83dd92726e | ||
|
|
59f3e19d2d | ||
|
|
73ba5f039b | ||
|
|
e76dec7e15 | ||
|
|
8a67ca073a | ||
|
|
2b7cd6ab23 | ||
|
|
9b0568c5e7 | ||
|
|
48f6c8d3c1 | ||
|
|
2544d7293d | ||
|
|
c71d9458c8 | ||
|
|
c28c9abd5b | ||
|
|
c69ee8046a | ||
|
|
65894c9e39 | ||
|
|
55fd9fca1f | ||
|
|
f24e846272 | ||
|
|
d91113c9e7 | ||
|
|
94a9b73117 | ||
|
|
4ef027462f | ||
|
|
a788623ea3 | ||
|
|
94d3f3829b | ||
|
|
321e01157a | ||
|
|
97b2ba2d6a | ||
|
|
5ee8fcfea2 | ||
|
|
c9d4f2b82e | ||
|
|
aa003d0161 | ||
|
|
7decfc7d1e | ||
|
|
cea854edf5 | ||
|
|
9a61b3c41b | ||
|
|
113d4b697a | ||
|
|
2260cdd3de | ||
|
|
377ca27cc9 | ||
|
|
9d5a477064 | ||
|
|
7fa7907219 | ||
|
|
60d5660286 | ||
|
|
94b4c22594 | ||
|
|
86d15c6c90 | ||
|
|
c1e4d79c56 | ||
|
|
ac6dd7bca4 | ||
|
|
cbec7f8580 | ||
|
|
a7c00ca5da | ||
|
|
2a5dc6757c | ||
|
|
b85035c45e | ||
|
|
687f8d9621 | ||
|
|
668f2f063f | ||
|
|
f93c712dca | ||
|
|
88f995803f | ||
|
|
71164fd0c1 | ||
|
|
c5ac30c303 | ||
|
|
0d72846b40 | ||
|
|
5d8da9633c | ||
|
|
325bd46740 |
@@ -0,0 +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
|
||||
@@ -4,13 +4,15 @@ updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: "weekly"
|
||||
commit-message:
|
||||
prefix: ⬆️
|
||||
target-branch: "develop"
|
||||
# Python
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: "weekly"
|
||||
commit-message:
|
||||
prefix: ⬆️
|
||||
target-branch: "develop"
|
||||
|
||||
@@ -1,33 +1,42 @@
|
||||
|
||||
name: Clear cache
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 1 * *'
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 0 1 * *" # Run at midnight on the first day of every month
|
||||
workflow_dispatch:
|
||||
|
||||
# Restrict permissions by default
|
||||
permissions:
|
||||
actions: write
|
||||
actions: write # Required for cache management
|
||||
|
||||
jobs:
|
||||
clear-cache:
|
||||
name: Clear cache
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Clear cache
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
with:
|
||||
script: |
|
||||
console.log("About to clear")
|
||||
console.log("Starting cache cleanup...")
|
||||
const caches = await github.rest.actions.getActionsCacheList({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
})
|
||||
|
||||
let deletedCount = 0
|
||||
for (const cache of caches.data.actions_caches) {
|
||||
console.log(cache)
|
||||
github.rest.actions.deleteActionsCacheById({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
cache_id: cache.id,
|
||||
})
|
||||
console.log(`Deleting cache: ${cache.key} (${cache.size_in_bytes} bytes)`)
|
||||
try {
|
||||
await github.rest.actions.deleteActionsCacheById({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
cache_id: cache.id,
|
||||
})
|
||||
deletedCount++
|
||||
} catch (error) {
|
||||
console.error(`Failed to delete cache ${cache.key}: ${error.message}`)
|
||||
}
|
||||
}
|
||||
console.log("Clear completed")
|
||||
console.log(`Cache cleanup completed. Deleted ${deletedCount} caches.`)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
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
|
||||
@@ -1,53 +0,0 @@
|
||||
name: Docs WorkFlow - Develop Tag 📚
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.ref}}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pages: write
|
||||
pull-requests: write
|
||||
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 🔄 Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: 🐍 Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: 📦 Install mkdocs-material
|
||||
run: pip install "mkdocs-material"
|
||||
- name: 📦 Install mkdocstrings[python]
|
||||
run: pip install "mkdocstrings[python]"
|
||||
- name: 📦 Install mkdocs-material[imaging]
|
||||
run: pip install "mkdocs-material[imaging]"
|
||||
- name: 📦 Install mike
|
||||
run: pip install "mike"
|
||||
- name: 📦 Install mkdocs-git-revision-date-localized-plugin
|
||||
run: pip install "mkdocs-git-revision-date-localized-plugin"
|
||||
- name: 📦 Install JupyterLab
|
||||
run: pip install jupyterlab
|
||||
- name: 📦 Install mkdocs-jupyter
|
||||
run: pip install mkdocs-jupyter
|
||||
- name: 📦 Install mkdocs-git-committers-plugin-2
|
||||
run: pip install mkdocs-git-committers-plugin-2
|
||||
- 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"
|
||||
- name: 🚀 Deploy MkDoc-Material with mike
|
||||
run: |
|
||||
MKDOCS_GIT_COMMITTERS_APIKEY=${{ secrets.GITHUB_TOKEN }} mike deploy --push develop
|
||||
@@ -0,0 +1,70 @@
|
||||
name: Build and Publish Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
# Ensure only one concurrent deployment
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.ref}}
|
||||
cancel-in-progress: true
|
||||
|
||||
# Restrict permissions by default
|
||||
permissions:
|
||||
contents: write # Required for committing to gh-pages
|
||||
pages: write # Required for deploying to Pages
|
||||
pull-requests: write # Required for PR comments
|
||||
|
||||
jobs:
|
||||
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
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 🐍 Install uv and set Python ${{ matrix.python-version }}
|
||||
uses: astral-sh/setup-uv@4db96194c378173c656ce18a155ffc14a9fc4355 # v5.2.2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: 🔑 Create GitHub App token (mkdocs)
|
||||
id: mkdocs_token
|
||||
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
|
||||
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 --extra 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]"
|
||||
|
||||
- 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"
|
||||
|
||||
- name: 🚀 Deploy Development Docs
|
||||
if: (github.event_name == 'push' && github.ref == 'refs/heads/develop') || github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
MKDOCS_GIT_COMMITTERS_APIKEY=${{ secrets.GITHUB_TOKEN }} uv run mike deploy --push develop
|
||||
|
||||
- name: 🚀 Deploy Release Docs
|
||||
if: github.event_name == 'release' && github.event.action == 'published'
|
||||
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
|
||||
@@ -0,0 +1,45 @@
|
||||
name: Publish Supervision Pre-Releases to PyPI
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "[0-9]+.[0-9]+[0-9]+.[0-9]+a[0-9]"
|
||||
- "[0-9]+.[0-9]+[0-9]+.[0-9]+b[0-9]"
|
||||
- "[0-9]+.[0-9]+[0-9]+.[0-9]+rc[0-9]"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: {} # Explicitly remove all permissions by default
|
||||
|
||||
jobs:
|
||||
publish-pre-release:
|
||||
name: Publish Pre-release Package
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: test
|
||||
url: https://pypi.org/project/supervision/
|
||||
timeout-minutes: 10
|
||||
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@4db96194c378173c656ce18a155ffc14a9fc4355 # v5.2.2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: 🏗️ Build source and wheel distributions
|
||||
run: |
|
||||
uv pip install -r pyproject.toml --extra build
|
||||
uv build
|
||||
uv run twine check --strict dist/*
|
||||
|
||||
- name: 🚀 Publish to PyPi
|
||||
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
|
||||
with:
|
||||
attestations: true
|
||||
@@ -1,55 +0,0 @@
|
||||
name: Supervision Release Documentation Workflow 📚
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.ref}}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pages: write
|
||||
pull-requests: write
|
||||
|
||||
|
||||
jobs:
|
||||
doc-build-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.10"]
|
||||
steps:
|
||||
- name: 🛎️ Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.head_ref }}
|
||||
|
||||
- name: 🐍 Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: 📦 Install mkdocs-material
|
||||
run: pip install "mkdocs-material"
|
||||
- name: 📦 Install mkdocstrings[python]
|
||||
run: pip install "mkdocstrings[python]"
|
||||
- name: 📦 Install mkdocs-material[imaging]
|
||||
run: pip install "mkdocs-material[imaging]"
|
||||
- name: 📦 Install mike
|
||||
run: pip install "mike"
|
||||
- name: 📦 Install mkdocs-git-revision-date-localized-plugin
|
||||
run: pip install "mkdocs-git-revision-date-localized-plugin"
|
||||
- name: 📦 Install JupyterLab
|
||||
run: pip install jupyterlab
|
||||
- name: 📦 Install mkdocs-jupyter
|
||||
run: pip install mkdocs-jupyter
|
||||
- name: 📦 Install mkdocs-git-committers-plugin-2
|
||||
run: pip install mkdocs-git-committers-plugin-2
|
||||
- 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"
|
||||
- name: 🚀 Deploy MkDoc-Material 📚
|
||||
run: |
|
||||
latest_tag=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
MKDOCS_GIT_COMMITTERS_APIKEY=${{ secrets.GITHUB_TOKEN }} mike deploy --push --update-aliases $latest_tag latest
|
||||
@@ -0,0 +1,43 @@
|
||||
name: Publish Supervision Releases to PyPI
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "[0-9]+.[0-9]+[0-9]+.[0-9]"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: {} # Explicitly remove all permissions by default
|
||||
|
||||
jobs:
|
||||
publish-release:
|
||||
name: Publish Release Package
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: release
|
||||
url: https://pypi.org/project/supervision/
|
||||
timeout-minutes: 10
|
||||
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@4db96194c378173c656ce18a155ffc14a9fc4355 # v5.2.2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: 🏗️ Build source and wheel distributions
|
||||
run: |
|
||||
uv pip install -r pyproject.toml --extra build
|
||||
uv build
|
||||
uv run twine check --strict dist/*
|
||||
|
||||
- name: 🚀 Publish to PyPi
|
||||
uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70 # v1.12.3
|
||||
with:
|
||||
attestations: true
|
||||
@@ -1,43 +0,0 @@
|
||||
name: Supervision Test Releases to PyPi
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '[0-9]+.[0-9]+[0-9]+.[0-9]+a[0-9]'
|
||||
- '[0-9]+.[0-9]+[0-9]+.[0-9]+b[0-9]'
|
||||
- '[0-9]+.[0-9]+[0-9]+.[0-9]+rc[0-9]'
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-n-publish:
|
||||
name: Build and publish to PyPI
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.10"]
|
||||
steps:
|
||||
- name: 🛎️ Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
- name: 🐍 Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: 🏗️ Build source and wheel distributions
|
||||
run: |
|
||||
python -m pip install --upgrade build twine
|
||||
python -m build
|
||||
twine check --strict dist/*
|
||||
- name: 🚀 Publish distribution to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: ${{ secrets.PYPI_USERNAME }}
|
||||
password: ${{ secrets.PYPI_PASSWORD }}
|
||||
- name: 🚀 Publish to Test-PyPi
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
repository-url: https://test.pypi.org/legacy/
|
||||
user: ${{ secrets.PYPI_TEST_USERNAME }}
|
||||
password: ${{ secrets.PYPI_TEST_PASSWORD }}
|
||||
@@ -0,0 +1,41 @@
|
||||
name: Publish Supervision Releases to TestPyPI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: {} # Explicitly remove all permissions by default
|
||||
|
||||
jobs:
|
||||
publish-testpypi:
|
||||
name: Publish Release Package
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: release
|
||||
url: https://pypi.org/project/supervision/
|
||||
timeout-minutes: 10
|
||||
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@4db96194c378173c656ce18a155ffc14a9fc4355 # v5.2.2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: 🏗️ Build source and wheel distributions
|
||||
run: |
|
||||
uv pip install -r pyproject.toml --extra build
|
||||
uv build
|
||||
uv run twine check --strict dist/*
|
||||
|
||||
- name: 🚀 Publish to Test-PyPi
|
||||
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
|
||||
with:
|
||||
repository-url: https://test.pypi.org/legacy/
|
||||
attestations: true
|
||||
@@ -1,40 +0,0 @@
|
||||
name: Supervision Releases to PyPi
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '[0-9]+.[0-9]+[0-9]+.[0-9]'
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.10"]
|
||||
steps:
|
||||
- name: 🛎️ Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
- name: 🐍 Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: 🏗️ Build source and wheel distributions
|
||||
run: |
|
||||
python -m pip install --upgrade build twine
|
||||
python -m build
|
||||
twine check --strict dist/*
|
||||
- name: 🚀 Publish to PyPi
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: ${{ secrets.PYPI_USERNAME }}
|
||||
password: ${{ secrets.PYPI_PASSWORD }}
|
||||
- name: 🚀 Publish to Test-PyPi
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
repository-url: https://test.pypi.org/legacy/
|
||||
user: ${{ secrets.PYPI_TEST_USERNAME }}
|
||||
password: ${{ secrets.PYPI_TEST_PASSWORD }}
|
||||
@@ -4,31 +4,34 @@ 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 code
|
||||
uses: actions/checkout@v4
|
||||
- name: 🐍 Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
- name: 📥 Checkout the repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: 📦 Install mkdocs-material
|
||||
run: pip install "mkdocs-material[all]"
|
||||
- name: 📦 Install mkdocstrings[python]
|
||||
run: pip install "mkdocstrings[python]"
|
||||
- name: 📦 Install mkdocs-material[imaging]
|
||||
run: pip install "mkdocs-material[imaging]"
|
||||
- name: 📦 Install mike
|
||||
run: pip install "mike"
|
||||
- name: 📦 Install mkdocs-git-revision-date-localized-plugin
|
||||
run: pip install "mkdocs-git-revision-date-localized-plugin"
|
||||
- name: 📦 Install JupyterLab
|
||||
run: pip install jupyterlab
|
||||
- name: 📦 Install mkdocs-jupyter
|
||||
run: pip install mkdocs-jupyter
|
||||
- name: 📦 Install mkdocs-git-committers-plugin-2
|
||||
run: pip install mkdocs-git-committers-plugin-2
|
||||
- name: 🧪 Test documentation build
|
||||
run: mkdocs build --verbose
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 🐍 Install uv and set Python ${{ matrix.python-version }}
|
||||
uses: astral-sh/setup-uv@4db96194c378173c656ce18a155ffc14a9fc4355 # v5.2.2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: 🏗️ Install dependencies
|
||||
run: |
|
||||
uv pip install -r pyproject.toml --extra docs
|
||||
|
||||
|
||||
- name: 🧪 Test Docs Build
|
||||
run: uv run mkdocs build --verbose
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
name: Python 3.8 - Min Dep Test WorkFlow
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
|
||||
jobs:
|
||||
build-min-dep-test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.8"]
|
||||
steps:
|
||||
- name: 🛎️ Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: 🐍 Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
# id based on python version
|
||||
id: python-setup
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
check-latest: true
|
||||
|
||||
- name: 📦 Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install \
|
||||
attrs==23.1.0 \
|
||||
certifi==2023.7.22 \
|
||||
charset-normalizer==2.0.12 \
|
||||
cycler==0.12.1 \
|
||||
exceptiongroup==1.1.3 \
|
||||
fonttools==4.43.1 \
|
||||
idna==3.4 \
|
||||
iniconfig==2.0.0 \
|
||||
kiwisolver==1.4.5 \
|
||||
matplotlib==3.5.0 \
|
||||
numpy==1.21.2 \
|
||||
opencv-python==4.5.5.64 \
|
||||
Pillow==10.1.0 \
|
||||
packaging==23.2 \
|
||||
pluggy==1.3.0 \
|
||||
pyparsing==3.1.1 \
|
||||
pytest==7.2.0 \
|
||||
python-dateutil==2.8.2 \
|
||||
PyYAML==5.3 \
|
||||
requests==2.26.0 \
|
||||
scipy==1.10.0 \
|
||||
setuptools-scm==8.0.4 \
|
||||
six==1.16.0 \
|
||||
tomli==2.0.1 \
|
||||
tqdm==4.62.3 \
|
||||
typing_extensions==4.8.0 \
|
||||
urllib3==1.26.18 \
|
||||
defusedxml==0.7.1
|
||||
|
||||
- name: 🧪 Test
|
||||
run: "python -m pytest ./test"
|
||||
@@ -1,29 +0,0 @@
|
||||
name: Test WorkFlow
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
|
||||
jobs:
|
||||
build-dev-test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
|
||||
steps:
|
||||
- name: 🛎️ Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: 🐍 Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
check-latest: true
|
||||
|
||||
- name: 📦 Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install .
|
||||
pip install pytest
|
||||
|
||||
- name: 🧪 Test
|
||||
run: "python -m pytest ./test"
|
||||
@@ -0,0 +1,33 @@
|
||||
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.8", "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@4db96194c378173c656ce18a155ffc14a9fc4355 # v5.2.2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: 🚀 Install Packages
|
||||
run: uv pip install -r pyproject.toml --extra dev --extra 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
|
||||
@@ -1,18 +0,0 @@
|
||||
name: Welcome WorkFlow
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: 👋 Welcome
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/first-interaction@v1.3.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-message: "Hello there, thank you for opening an Issue ! 🙏🏻 The team was notified and they will get back to you asap."
|
||||
pr-message: "Hello there, thank you for opening an PR ! 🙏🏻 The team was notified and they will get back to you asap."
|
||||
+18
-16
@@ -7,9 +7,8 @@ ci:
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.6.0
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
exclude: test/.*\.py
|
||||
- id: check-yaml
|
||||
@@ -19,35 +18,38 @@ repos:
|
||||
- id: check-case-conflict
|
||||
- id: check-added-large-files
|
||||
- id: detect-private-key
|
||||
- id: forbid-new-submodules
|
||||
- id: pretty-format-json
|
||||
exclude: demo.ipynb
|
||||
args: ['--autofix', '--no-sort-keys', '--indent=4']
|
||||
- id: end-of-file-fixer
|
||||
- id: mixed-line-ending
|
||||
|
||||
|
||||
- repo: https://github.com/PyCQA/bandit
|
||||
rev: '1.7.9'
|
||||
rev: '1.8.0'
|
||||
hooks:
|
||||
- id: bandit
|
||||
args: ["-c", "pyproject.toml"]
|
||||
additional_dependencies: ["bandit[toml]"]
|
||||
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.13.2
|
||||
hooks:
|
||||
- id: isort
|
||||
name: isort (python)
|
||||
- id: isort
|
||||
name: isort (pyi)
|
||||
types: [pyi]
|
||||
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.5.1
|
||||
rev: v0.8.6
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix, --exit-non-zero-on-fix]
|
||||
- id: ruff-format
|
||||
types_or: [ python, pyi, jupyter ]
|
||||
|
||||
# - 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/codespell-project/codespell
|
||||
rev: v2.3.0
|
||||
hooks:
|
||||
- id: codespell
|
||||
additional_dependencies:
|
||||
- tomli
|
||||
|
||||
+16
-17
@@ -1,4 +1,3 @@
|
||||
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
@@ -6,7 +5,7 @@
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
identity and expression, level of experience, education, socioeconomic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||
identity and orientation.
|
||||
|
||||
@@ -18,23 +17,23 @@ diverse, inclusive, and healthy community.
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- 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
|
||||
* Focusing on what is best not just for us as individuals, but for the overall
|
||||
- Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||
- The use of sexualized language or imagery, and sexual attention or advances of
|
||||
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,
|
||||
- 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
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
@@ -121,14 +120,14 @@ version 2.1, available at
|
||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||
[Mozilla's code of conduct enforcement ladder][mozilla coc].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||
[https://www.contributor-covenant.org/faq][faq]. Translations are available at
|
||||
[https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[faq]: https://www.contributor-covenant.org/faq
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[mozilla coc]: https://github.com/mozilla/diversity
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
|
||||
+47
-39
@@ -6,22 +6,22 @@ We are actively improving this library to reduce the amount of work you need to
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Please read and adhere to our [Code of Conduct](CODE_OF_CONDUCT.md). This document outlines the expected behavior for all participants in our project.
|
||||
Please read and adhere to our [Code of Conduct](https://supervision.roboflow.com/latest/code_of_conduct/). This document outlines the expected behavior for all participants in our project.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Contribution Guidelines](#contribution-guidelines)
|
||||
- [Contributing Features](#contributing-features-)
|
||||
- [Contributing Features](#contributing-features)
|
||||
- [How to Contribute Changes](#how-to-contribute-changes)
|
||||
- [Installation for Contributors](#installation-for-contributors)
|
||||
- [Code Style and Quality](#-code-style-and-quality)
|
||||
- [Pre-commit tool](#pre-commit-tool)
|
||||
- [Docstrings](#docstrings)
|
||||
- [Type checking](#type-checking)
|
||||
- [Documentation](#-documentation)
|
||||
- [Cookbooks](#-cookbooks)
|
||||
- [Tests](#-tests)
|
||||
- [License](#-license)
|
||||
- [Code Style and Quality](#code-style-and-quality)
|
||||
- [Pre-commit tool](#pre-commit-tool)
|
||||
- [Docstrings](#docstrings)
|
||||
- [Type checking](#type-checking)
|
||||
- [Documentation](#documentation)
|
||||
- [Cookbooks](#cookbooks)
|
||||
- [Tests](#tests)
|
||||
- [License](#license)
|
||||
|
||||
## Contribution Guidelines
|
||||
|
||||
@@ -83,9 +83,10 @@ 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
|
||||
@@ -127,41 +128,48 @@ 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:
|
||||
```bash
|
||||
git clone https://github.com/YOUR_USERNAME/supervision.git
|
||||
cd supervision
|
||||
```
|
||||
Replace `YOUR_USERNAME` with your GitHub username.
|
||||
1. Clone your fork of the project (recommended to use shallow clone of develop branch):
|
||||
|
||||
**Option A: Recommended for most contributors (shallow clone of develop branch):**
|
||||
|
||||
```bash
|
||||
git clone --depth 1 -b develop https://github.com/YOUR_USERNAME/supervision.git
|
||||
cd supervision
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
**Option B: Full repository clone (if you need complete history):**
|
||||
|
||||
```bash
|
||||
git clone https://github.com/YOUR_USERNAME/supervision.git
|
||||
cd supervision
|
||||
```
|
||||
|
||||
2. Create and activate a virtual environment:
|
||||
```bash
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
3. Install Poetry:
|
||||
```bash
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
Using pip:
|
||||
```bash
|
||||
pip install -U pip setuptools
|
||||
pip install poetry
|
||||
```
|
||||
3. Install `uv`:
|
||||
|
||||
Or using pipx (recommended for global installation):
|
||||
```bash
|
||||
pipx install poetry
|
||||
```
|
||||
Follow the instructions on the [uv installation page](https://docs.astral.sh/uv/getting-started/installation/).
|
||||
|
||||
4. Install project dependencies:
|
||||
```bash
|
||||
poetry install
|
||||
```
|
||||
|
||||
```bash
|
||||
uv pip install -r pyproject.toml --extra dev --extra docs --extra metrics
|
||||
```
|
||||
|
||||
5. Run pytest to verify the setup:
|
||||
```bash
|
||||
poetry run pytest
|
||||
```
|
||||
|
||||
```bash
|
||||
uv run pytest
|
||||
```
|
||||
|
||||
## 🎨 Code Style and Quality
|
||||
|
||||
@@ -173,7 +181,7 @@ 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: `poetry install`. It will not only install pre-commit but also install all the deps and dev-deps of project
|
||||
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
|
||||
|
||||
2. Once pre-commit is installed, navigate to the project's root directory.
|
||||
|
||||
@@ -195,7 +203,7 @@ So far, **there is no type checking with mypy**. See [issue](https://github.com/
|
||||
|
||||
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 `poetry install --with dev`. Then, run `mkdocs serve` to start the documentation server.
|
||||
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.
|
||||
|
||||
You can learn more about mkdocs on the [mkdocs website](https://www.mkdocs.org/).
|
||||
|
||||
|
||||
@@ -8,28 +8,32 @@
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
[notebooks](https://github.com/roboflow/notebooks) | [inference](https://github.com/roboflow/inference) | [autodistill](https://github.com/autodistill/autodistill) | [maestro](https://github.com/roboflow/multimodal-maestro)
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
[](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)
|
||||
[](https://badge.fury.io/py/supervision)
|
||||
[](https://colab.research.google.com/github/roboflow/supervision/blob/main/demo.ipynb)
|
||||
[](https://huggingface.co/spaces/Roboflow/Annotators)
|
||||
[](https://discord.gg/GbfgXGJ8Bk)
|
||||
[](https://discord.gg/GbfgXGJ8Bk)
|
||||
[](https://squidfunk.github.io/mkdocs-material/)
|
||||
|
||||
<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>
|
||||
|
||||
</div>
|
||||
|
||||
## 👋 hello
|
||||
|
||||
**We write your reusable computer vision tools.** Whether you need to load your dataset from your hard drive, draw detections on an image or video, or count how many detections are in a zone. You can count on us! 🤝
|
||||
|
||||
[](https://github.com/orgs/roboflow/projects)
|
||||
|
||||
## 💻 install
|
||||
|
||||
Pip install the supervision package in a
|
||||
@@ -53,7 +57,7 @@ import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
image = cv2.imread(...)
|
||||
model = YOLO('yolov8s.pt')
|
||||
model = YOLO("yolov8s.pt")
|
||||
result = model(image)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
|
||||
@@ -66,21 +70,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 cv2
|
||||
import supervision as sv
|
||||
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 = 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)
|
||||
|
||||
len(detections)
|
||||
# 5
|
||||
```
|
||||
len(detections)
|
||||
# 5
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
@@ -97,9 +101,8 @@ detections = sv.Detections(...)
|
||||
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
annotated_frame = box_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
scene=image.copy(),
|
||||
detections=detections)
|
||||
```
|
||||
|
||||
https://github.com/roboflow/supervision/assets/26109316/691e219c-0565-4403-9218-ab5644f39bce
|
||||
@@ -110,18 +113,21 @@ Supervision provides a set of [utils](https://supervision.roboflow.com/latest/da
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
from roboflow import Roboflow
|
||||
|
||||
dataset = sv.DetectionDataset.from_yolo(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...,
|
||||
data_yaml_path=...
|
||||
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",
|
||||
annotations_path=f"{dataset.location}/train/_annotations.coco.json",
|
||||
)
|
||||
|
||||
dataset.classes
|
||||
['dog', 'person']
|
||||
path, image, annotation = ds[0]
|
||||
# loads image on demand
|
||||
|
||||
len(dataset)
|
||||
# 1000
|
||||
for path, image, annotation in ds:
|
||||
# loads image on demand
|
||||
```
|
||||
|
||||
<details close>
|
||||
@@ -154,7 +160,7 @@ len(dataset)
|
||||
test_dataset, valid_dataset = test_dataset.split(split_ratio=0.5)
|
||||
|
||||
len(train_dataset), len(test_dataset), len(valid_dataset)
|
||||
# (700, 150, 150)
|
||||
# (700, 150, 150)
|
||||
```
|
||||
|
||||
- merge
|
||||
@@ -162,21 +168,21 @@ len(dataset)
|
||||
```python
|
||||
ds_1 = sv.DetectionDataset(...)
|
||||
len(ds_1)
|
||||
# 100
|
||||
# 100
|
||||
ds_1.classes
|
||||
# ['dog', 'person']
|
||||
# ['dog', 'person']
|
||||
|
||||
ds_2 = sv.DetectionDataset(...)
|
||||
len(ds_2)
|
||||
# 200
|
||||
ds_2.classes
|
||||
# ['cat']
|
||||
# ['cat']
|
||||
|
||||
ds_merged = sv.DetectionDataset.merge([ds_1, ds_2])
|
||||
len(ds_merged)
|
||||
# 300
|
||||
# 300
|
||||
ds_merged.classes
|
||||
# ['cat', 'dog', 'person']
|
||||
# ['cat', 'dog', 'person']
|
||||
```
|
||||
|
||||
- save
|
||||
@@ -262,7 +268,7 @@ We love your input! Please see our [contributing guide](https://github.com/robof
|
||||
|
||||
<div align="center">
|
||||
|
||||
<div align="center">
|
||||
<div align="center">
|
||||
<a href="https://youtube.com/roboflow">
|
||||
<img
|
||||
src="https://media.roboflow.com/notebooks/template/icons/purple/youtube.png?ik-sdk-version=javascript-1.4.3&updatedAt=1672949634652"
|
||||
@@ -291,7 +297,7 @@ We love your input! Please see our [contributing guide](https://github.com/robof
|
||||
/>
|
||||
</a>
|
||||
<img src="https://raw.githubusercontent.com/ultralytics/assets/main/social/logo-transparent.png" width="3%"/>
|
||||
<a href="https://disuss.roboflow.com">
|
||||
<a href="https://discuss.roboflow.com">
|
||||
<img
|
||||
src="https://media.roboflow.com/notebooks/template/icons/purple/forum.png?ik-sdk-version=javascript-1.4.3&updatedAt=1672949633584"
|
||||
width="3%"
|
||||
|
||||
Vendored
+3
-3
@@ -353,7 +353,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install -q ultralytics"
|
||||
"!pip install -q \"ultralytics<=8.3.40\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -972,7 +972,7 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from supervision.assets import download_assets, VideoAssets\n",
|
||||
"from supervision.assets import VideoAssets, download_assets\n",
|
||||
"\n",
|
||||
"download_assets(VideoAssets.VEHICLES)\n",
|
||||
"VIDEO_PATH = VideoAssets.VEHICLES.value"
|
||||
@@ -1357,7 +1357,7 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"IMAGE_NAME = list(ds.images.keys())[0]\n",
|
||||
"IMAGE_NAME = next(iter(ds.images.keys()))\n",
|
||||
"\n",
|
||||
"image = ds.images[IMAGE_NAME]\n",
|
||||
"annotations = ds.annotations[IMAGE_NAME]\n",
|
||||
|
||||
@@ -7,17 +7,6 @@ comments: true
|
||||
Supervision offers an assets download utility that allows you to download video files
|
||||
that you can use in your demos.
|
||||
|
||||
## Install extra
|
||||
|
||||
To install the Supervision assets utility, you can use `pip`. This utility is available
|
||||
as an extra within the Supervision package.
|
||||
|
||||
!!! example "pip install"
|
||||
|
||||
```bash
|
||||
pip install "supervision[assets]"
|
||||
```
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.assets.downloader.download_assets.download_assets">download_assets</a></h2>
|
||||
</div>
|
||||
|
||||
+449
-32
@@ -1,6 +1,423 @@
|
||||
# CHANGELOG
|
||||
|
||||
### 0.25.0 <small>Nov 12, 2024</small>
|
||||
|
||||
- No removals or deprecations in this release!
|
||||
|
||||
- Essential update to the [`LineZone`](https://supervision.roboflow.com/0.25.0/detection/tools/line_zone/): when computing line crossings, detections that jitter might be counted twice (or more). This can now be solved with the `minimum_crossing_threshold` argument. If you set it to `2` or more, extra frames will be used to confirm the crossing, improving the accuracy significantly. ([#1540](https://github.com/roboflow/supervision/pull/1540))
|
||||
|
||||
- It is now possible to track objects detected as [`KeyPoints`](https://supervision.roboflow.com/0.25.0/keypoint/core/#supervision.keypoint.core.KeyPoints). See the complete step-by-step guide in the [Object Tracking Guide](https://supervision.roboflow.com/latest/how_to/track_objects/#keypoints). ([#1658](https://github.com/roboflow/supervision/pull/1658))
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8m-pose.pt")
|
||||
tracker = sv.ByteTrack()
|
||||
trace_annotator = sv.TraceAnnotator()
|
||||
|
||||
def callback(frame: np.ndarray, _: int) -> np.ndarray:
|
||||
results = model(frame)[0]
|
||||
key_points = sv.KeyPoints.from_ultralytics(results)
|
||||
|
||||
detections = key_points.as_detections()
|
||||
detections = tracker.update_with_detections(detections)
|
||||
|
||||
annotated_image = trace_annotator.annotate(frame.copy(), detections)
|
||||
return annotated_image
|
||||
|
||||
sv.process_video(
|
||||
source_path="input_video.mp4",
|
||||
target_path="output_video.mp4",
|
||||
callback=callback
|
||||
)
|
||||
```
|
||||
|
||||
- Added `is_empty` method to [`KeyPoints`](https://supervision.roboflow.com/0.25.0/keypoint/core/#supervision.keypoint.core.KeyPoints) to check if there are any keypoints in the object. ([#1658](https://github.com/roboflow/supervision/pull/1658))
|
||||
|
||||
- Added `as_detections` method to [`KeyPoints`](https://supervision.roboflow.com/0.25.0/keypoint/core/#supervision.keypoint.core.KeyPoints) that converts `KeyPoints` to `Detections`. ([#1658](https://github.com/roboflow/supervision/pull/1658))
|
||||
|
||||
- Added a new video to `supervision[assets]`. ([#1657](https://github.com/roboflow/supervision/pull/1657))
|
||||
|
||||
```python
|
||||
from supervision.assets import download_assets, VideoAssets
|
||||
|
||||
path_to_video = download_assets(VideoAssets.SKIING)
|
||||
```
|
||||
|
||||
- Supervision can now be used with [`Python 3.13`](https://docs.python.org/3/whatsnew/3.13.html). The most renowned update is the ability to run Python [without Global Interpreter Lock (GIL)](https://docs.python.org/3/whatsnew/3.13.html#whatsnew313-free-threaded-cpython). We expect support for this among our dependencies to be inconsistent, but if you do attempt it - let us know the results! ([#1595](https://github.com/roboflow/supervision/pull/1595))
|
||||
|
||||
- Added [`Mean Average Recall`](https://supervision.roboflow.com/latest/metrics/mean_average_recall/) mAR metric, which returns a recall score, averaged over IoU thresholds, detected object classes, and limits imposed on maximum considered detections. ([#1661](https://github.com/roboflow/supervision/pull/1661))
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
from supervision.metrics import MeanAverageRecall
|
||||
|
||||
predictions = sv.Detections(...)
|
||||
targets = sv.Detections(...)
|
||||
|
||||
map_metric = MeanAverageRecall()
|
||||
map_result = map_metric.update(predictions, targets).compute()
|
||||
|
||||
map_result.plot()
|
||||
```
|
||||
|
||||
- Added [`Precision`](https://supervision.roboflow.com/latest/metrics/precision/) and [`Recall`](https://supervision.roboflow.com/latest/metrics/recall/) metrics, providing a baseline for comparing model outputs to ground truth or another model ([#1609](https://github.com/roboflow/supervision/pull/1609))
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
from supervision.metrics import Recall
|
||||
|
||||
predictions = sv.Detections(...)
|
||||
targets = sv.Detections(...)
|
||||
|
||||
recall_metric = Recall()
|
||||
recall_result = recall_metric.update(predictions, targets).compute()
|
||||
|
||||
recall_result.plot()
|
||||
```
|
||||
|
||||
- All Metrics now support Oriented Bounding Boxes (OBB) ([#1593](https://github.com/roboflow/supervision/pull/1593))
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
from supervision.metrics import F1_Score
|
||||
|
||||
predictions = sv.Detections(...)
|
||||
targets = sv.Detections(...)
|
||||
|
||||
f1_metric = MeanAverageRecall(metric_target=sv.MetricTarget.ORIENTED_BOUNDING_BOXES)
|
||||
f1_result = f1_metric.update(predictions, targets).compute()
|
||||
```
|
||||
|
||||
- Introducing Smart Labels! When `smart_position` is set for [`LabelAnnotator`](https://supervision.roboflow.com/0.25.0/detection/annotators/#supervision.annotators.core.LabelAnnotator), [`RichLabelAnnotator`](https://supervision.roboflow.com/0.25.0/detection/annotators/#supervision.annotators.core.RichLabelAnnotator) or [`VertexLabelAnnotator`](https://supervision.roboflow.com/0.25.0/detection/annotators/#supervision.annotators.core.RichLabelAnnotator), the labels will move around to avoid overlapping others. ([#1625](https://github.com/roboflow/supervision/pull/1625))
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
image = cv2.imread("image.jpg")
|
||||
|
||||
label_annotator = sv.LabelAnnotator(smart_position=True)
|
||||
|
||||
model = YOLO("yolo11m.pt")
|
||||
results = model(image)[0]
|
||||
detections = sv.Detections.from_ultralytics(results)
|
||||
|
||||
annotated_frame = label_annotator.annotate(first_frame.copy(), detections)
|
||||
sv.plot_image(annotated_frame)
|
||||
```
|
||||
|
||||
- Added the `metadata` variable to [`Detections`](https://supervision.roboflow.com/0.25.0/detection/core/#supervision.detection.core.Detections). It allows you to store custom data per-image, rather than per-detected-object as was possible with `data` variable. For example, `metadata` could be used to store the source video path, camera model or camera parameters. ([#1589](https://github.com/roboflow/supervision/pull/1589))
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8m")
|
||||
|
||||
result = model("image.png")[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
|
||||
# Items in `data` must match length of detections
|
||||
object_ids = [num for num in range(len(detections))]
|
||||
detections.data["object_number"] = object_ids
|
||||
|
||||
# Items in `metadata` can be of any length.
|
||||
detections.metadata["camera_model"] = "Luxonis OAK-D"
|
||||
```
|
||||
|
||||
- Added a `py.typed` type hints metafile. It should provide a stronger signal to type annotators and IDEs that type support is available. ([#1586](https://github.com/roboflow/supervision/pull/1586))
|
||||
|
||||
- `ByteTrack` no longer requires `detections` to have a `class_id` ([#1637](https://github.com/roboflow/supervision/pull/1637))
|
||||
- `draw_line`, `draw_rectangle`, `draw_filled_rectangle`, `draw_polygon`, `draw_filled_polygon` and `PolygonZoneAnnotator` now comes with a default color ([#1591](https://github.com/roboflow/supervision/pull/1591))
|
||||
- Dataset classes are treated as case-sensitive when merging multiple datasets. ([#1643](https://github.com/roboflow/supervision/pull/1643))
|
||||
- Expanded [metrics documentation](https://supervision.roboflow.com/0.25.0/metrics/f1_score/) with example plots and printed results ([#1660](https://github.com/roboflow/supervision/pull/1660))
|
||||
- Added usage example for polygon zone ([#1608](https://github.com/roboflow/supervision/pull/1608))
|
||||
- Small improvements to error handling in polygons: ([#1602](https://github.com/roboflow/supervision/pull/1602))
|
||||
|
||||
- Updated [`ByteTrack`](https://supervision.roboflow.com/0.25.0/trackers/#supervision.tracker.byte_tracker.core.ByteTrack), removing shared variables. Previously, multiple instances of `ByteTrack` would share some date, requiring liberal use of `tracker.reset()`. ([#1603](https://github.com/roboflow/supervision/pull/1603)), ([#1528](https://github.com/roboflow/supervision/pull/1528))
|
||||
- Fixed a bug where `class_agnostic` setting in `MeanAveragePrecision` would not work. ([#1577](https://github.com/roboflow/supervision/pull/1577)) hacktoberfest
|
||||
- Removed welcome workflow from our CI system. ([#1596](https://github.com/roboflow/supervision/pull/1596))
|
||||
|
||||
- Large refactor of `ByteTrack`: STrack moved to separate class, removed superfluous `BaseTrack` class, removed unused variables ([#1603](https://github.com/roboflow/supervision/pull/1603))
|
||||
- Large refactor of `RichLabelAnnotator`, matching its contents with `LabelAnnotator`. ([#1625](https://github.com/roboflow/supervision/pull/1625))
|
||||
|
||||
### 0.24.0 <small>Oct 4, 2024</small>
|
||||
|
||||
- Added [F1 score](https://supervision.roboflow.com/0.24.0/metrics/f1_score/#supervision.metrics.f1_score.F1Score) as a new metric for detection and segmentation. [#1521](https://github.com/roboflow/supervision/pull/1521)
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
from supervision.metrics import F1Score
|
||||
|
||||
predictions = sv.Detections(...)
|
||||
targets = sv.Detections(...)
|
||||
|
||||
f1_metric = F1Score()
|
||||
f1_result = f1_metric.update(predictions, targets).compute()
|
||||
|
||||
print(f1_result)
|
||||
print(f1_result.f1_50)
|
||||
print(f1_result.small_objects.f1_50)
|
||||
```
|
||||
|
||||
- Added new cookbook: [Small Object Detection with SAHI](https://supervision.roboflow.com/0.24.0/notebooks/small-object-detection-with-sahi/). This cookbook provides a detailed guide on using [`InferenceSlicer`](https://supervision.roboflow.com/0.24.0/detection/tools/inference_slicer/) for small object detection. [#1483](https://github.com/roboflow/supervision/pull/1483)
|
||||
|
||||
- Added an [Embedded Workflow](https://roboflow.com/workflows), which allows you to [preview annotators](https://supervision.roboflow.com/0.24.0/detection/annotators/). [#1533](https://github.com/roboflow/supervision/pull/1533)
|
||||
|
||||
- Enhanced [`LineZoneAnnotator`](https://supervision.roboflow.com/0.24.0/detection/tools/line_zone/#supervision.detection.line_zone.LineZoneAnnotator), allowing the labels to align with the line, even when it's not horizontal. Also, you can now disable text background, and choose to draw labels off-center which minimizes overlaps for multiple [`LineZone`](https://supervision.roboflow.com/0.24.0/detection/tools/line_zone/#supervision.detection.line_zone.LineZone) labels. [#854](https://github.com/roboflow/supervision/pull/854)
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
import cv2
|
||||
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
|
||||
line_zone = sv.LineZone(
|
||||
start=sv.Point(0, 100),
|
||||
end=sv.Point(50, 200)
|
||||
)
|
||||
line_zone_annotator = sv.LineZoneAnnotator(
|
||||
text_orient_to_line=True,
|
||||
display_text_box=False,
|
||||
text_centered=False
|
||||
)
|
||||
|
||||
annotated_frame = line_zone_annotator.annotate(
|
||||
frame=image.copy(), line_counter=line_zone
|
||||
)
|
||||
|
||||
sv.plot_image(frame)
|
||||
```
|
||||
|
||||
- Added per-class counting capabilities to [`LineZone`](https://supervision.roboflow.com/0.24.0/detection/tools/line_zone/#supervision.detection.line_zone.LineZone) and introduced [`LineZoneAnnotatorMulticlass`](https://supervision.roboflow.com/0.24.0/detection/tools/line_zone/#supervision.detection.line_zone.LineZoneAnnotatorMulticlass) for visualizing the counts per class. This feature allows tracking of individual classes crossing a line, enhancing the flexibility of use cases like traffic monitoring or crowd analysis. [#1555](https://github.com/roboflow/supervision/pull/1555)
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
import cv2
|
||||
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
|
||||
line_zone = sv.LineZone(
|
||||
start=sv.Point(0, 100),
|
||||
end=sv.Point(50, 200)
|
||||
)
|
||||
line_zone_annotator = sv.LineZoneAnnotatorMulticlass()
|
||||
|
||||
frame = line_zone_annotator.annotate(
|
||||
frame=frame, line_zones=[line_zone]
|
||||
)
|
||||
|
||||
sv.plot_image(frame)
|
||||
```
|
||||
|
||||
- Added [`from_easyocr`](https://supervision.roboflow.com/0.24.0/detection/core/#supervision.detection.core.Detections.from_easyocr), allowing integration of OCR results into the supervision framework. [EasyOCR](https://github.com/JaidedAI/EasyOCR) is an open-source optical character recognition (OCR) library that can read text from images. [#1515](https://github.com/roboflow/supervision/pull/1515)
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
import easyocr
|
||||
import cv2
|
||||
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
|
||||
reader = easyocr.Reader(["en"])
|
||||
result = reader.readtext("<SOURCE_IMAGE_PATH>", paragraph=True)
|
||||
detections = sv.Detections.from_easyocr(result)
|
||||
|
||||
box_annotator = sv.BoxAnnotator(color_lookup=sv.ColorLookup.INDEX)
|
||||
label_annotator = sv.LabelAnnotator(color_lookup=sv.ColorLookup.INDEX)
|
||||
|
||||
annotated_image = image.copy()
|
||||
annotated_image = box_annotator.annotate(scene=annotated_image, detections=detections)
|
||||
annotated_image = label_annotator.annotate(scene=annotated_image, detections=detections)
|
||||
|
||||
sv.plot_image(annotated_image)
|
||||
```
|
||||
|
||||
- Added [`oriented_box_iou_batch`](https://supervision.roboflow.com/0.24.0/detection/utils/#supervision.detection.utils.oriented_box_iou_batch) function to `detection.utils`. This function computes Intersection over Union (IoU) for oriented or rotated bounding boxes (OBB). [#1502](https://github.com/roboflow/supervision/pull/1502)
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
|
||||
boxes_true = np.array([[[1, 0], [0, 1], [3, 4], [4, 3]]])
|
||||
boxes_detection = np.array([[[1, 1], [2, 0], [4, 2], [3, 3]]])
|
||||
ious = sv.oriented_box_iou_batch(boxes_true, boxes_detection)
|
||||
print("IoU between true and detected boxes:", ious)
|
||||
```
|
||||
|
||||
- Extended [`PolygonZoneAnnotator`](https://supervision.roboflow.com/0.24.0/detection/tools/polygon_zone/#supervision.detection.tools.polygon_zone.PolygonZoneAnnotator) to allow setting opacity when drawing zones, providing enhanced visualization by filling the zone with adjustable transparency. [#1527](https://github.com/roboflow/supervision/pull/1527)
|
||||
|
||||
```python
|
||||
import cv2
|
||||
from ncnn.model_zoo import get_model
|
||||
import supervision as sv
|
||||
|
||||
image = cv2.imread("<SOURCE_IMAGE_PATH>")
|
||||
model = get_model(
|
||||
"yolov8s",
|
||||
target_size=640,
|
||||
prob_threshold=0.5,
|
||||
nms_threshold=0.45,
|
||||
num_threads=4,
|
||||
use_gpu=True,
|
||||
)
|
||||
result = model(image)
|
||||
detections = sv.Detections.from_ncnn(result)
|
||||
```
|
||||
|
||||
!!! failure "Removed"
|
||||
|
||||
The `frame_resolution_wh` parameter in [`PolygonZone`](https://supervision.roboflow.com/0.24.0/detection/tools/polygon_zone/#supervision.detection.tools.polygon_zone.PolygonZone) has been removed.
|
||||
|
||||
!!! failure "Removed"
|
||||
|
||||
Supervision installation methods `"headless"` and `"desktop"` were removed, as they are no longer needed. `pip install supervision[headless]` will install the base library and harmlessly warn of non-existent extras.
|
||||
|
||||
- Supervision now depends on `opencv-python` rather than `opencv-python-headless`. [#1530](https://github.com/roboflow/supervision/pull/1530)
|
||||
|
||||
- Fixed the COCO 101 point Average Precision algorithm to correctly interpolate precision, providing a more precise calculation of average precision without averaging out intermediate values. [#1500](https://github.com/roboflow/supervision/pull/1500)
|
||||
|
||||
- Resolved miscellaneous issues highlighted when building documentation. This mostly includes whitespace adjustments and type inconsistencies. Updated documentation for clarity and fixed formatting issues. Added explicit version for `mkdocstrings-python`. [#1549](https://github.com/roboflow/supervision/pull/1549)
|
||||
|
||||
- Enabled and fixed Ruff rules for code formatting, including changes like avoiding unnecessary iterable allocations and using Optional for default mutable arguments. [#1526](https://github.com/roboflow/supervision/pull/1526)
|
||||
|
||||
### 0.23.0 <small>Aug 28, 2024</small>
|
||||
|
||||
- Added [#930](https://github.com/roboflow/supervision/pull/930): `IconAnnotator`, a [new annotator](https://supervision.roboflow.com/0.23.0/detection/annotators/#supervision.annotators.core.IconAnnotator) that allows drawing icons on each detection. Useful if you want to draw a specific icon for each class.
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
from inference import get_model
|
||||
|
||||
image = <SOURCE_IMAGE_PATH>
|
||||
icon_dog = <DOG_PNG_PATH>
|
||||
icon_cat = <CAT_PNG_PATH>
|
||||
|
||||
model = get_model(model_id="yolov8n-640")
|
||||
results = model.infer(image)[0]
|
||||
detections = sv.Detections.from_inference(results)
|
||||
|
||||
icon_paths = []
|
||||
for class_name in detections.data["class_name"]:
|
||||
if class_name == "dog":
|
||||
icon_paths.append(icon_dog)
|
||||
elif class_name == "cat":
|
||||
icon_paths.append(icon_cat)
|
||||
else:
|
||||
icon_paths.append("")
|
||||
|
||||
icon_annotator = sv.IconAnnotator()
|
||||
annotated_frame = icon_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
icon_path=icon_paths
|
||||
)
|
||||
```
|
||||
|
||||
- Added [#1385](https://github.com/roboflow/supervision/pull/1385): [`BackgroundColorAnnotator`](https://supervision.roboflow.com/0.23.0/detection/annotators/#supervision.annotators.core.BackgroundColorAnnotator), that draws an overlay on the background images of the detections.
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
from inference import get_model
|
||||
|
||||
image = <SOURCE_IMAGE_PATH>
|
||||
|
||||
model = get_model(model_id="yolov8n-640")
|
||||
results = model.infer(image)[0]
|
||||
detections = sv.Detections.from_inference(results)
|
||||
|
||||
background_overlay_annotator = sv.BackgroundOverlayAnnotator()
|
||||
annotated_frame = background_overlay_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
- Added [#1386](https://github.com/roboflow/supervision/pull/1386): Support for Transformers v5 functions in [`sv.Detections.from_transformers`](https://supervision.roboflow.com/0.23.0/detection/core/#supervision.detection.core.Detections.from_transformers). This includes the `DetrImageProcessor` methods `post_process_object_detection`, `post_process_panoptic_segmentation`, `post_process_semantic_segmentation`, and `post_process_instance_segmentation`.
|
||||
|
||||
```python
|
||||
import torch
|
||||
import supervision as sv
|
||||
from PIL import Image
|
||||
from transformers import DetrImageProcessor, DetrForObjectDetection
|
||||
|
||||
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
|
||||
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
|
||||
|
||||
image = Image.open(<SOURCE_IMAGE_PATH>)
|
||||
inputs = processor(images=image, return_tensors="pt")
|
||||
|
||||
with torch.no_grad():
|
||||
outputs = model(**inputs)
|
||||
|
||||
width, height = image.size
|
||||
target_size = torch.tensor([[height, width]])
|
||||
results = processor.post_process_object_detection(
|
||||
outputs=outputs, target_sizes=target_size)[0]
|
||||
detections = sv.Detections.from_transformers(
|
||||
transformers_results=results,
|
||||
id2label=model.config.id2label)
|
||||
```
|
||||
|
||||
- Added [#1354](https://github.com/roboflow/supervision/pull/1354): Ultralytics SAM (Segment Anything Model) support in [`sv.Detections.from_ultralytics`](https://supervision.roboflow.com/0.23.0/detection/core/#supervision.detection.core.Detections.from_ultralytics). [SAM2](https://sam2.metademolab.com/) was released during this update, and is already supported via [`sv.Detections.from_sam`](https://supervision.roboflow.com/0.23.0/detection/core/#supervision.detection.core.Detections.from_sam).
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
from segment_anything import (
|
||||
sam_model_registry,
|
||||
SamAutomaticMaskGenerator
|
||||
)
|
||||
sam_model_reg = sam_model_registry[MODEL_TYPE]
|
||||
sam = sam_model_reg(checkpoint=CHECKPOINT_PATH).to(device=DEVICE)
|
||||
mask_generator = SamAutomaticMaskGenerator(sam)
|
||||
sam_result = mask_generator.generate(IMAGE)
|
||||
detections = sv.Detections.from_sam(sam_result=sam_result)
|
||||
```
|
||||
|
||||
- Added [#1458](https://github.com/roboflow/supervision/pull/1458): `outline_color` options for [`TriangleAnnotator`](https://supervision.roboflow.com/0.23.0/detection/annotators/#supervision.annotators.core.TriangleAnnotator) and [`DotAnnotator`](https://supervision.roboflow.com/0.23.0/detection/annotators/#supervision.annotators.core.DotAnnotator).
|
||||
|
||||
- Added [#1409](https://github.com/roboflow/supervision/pull/1409): `text_color` option for [`VertexLabelAnnotator`](https://supervision.roboflow.com/0.23.0/keypoint/annotators/#supervision.keypoint.annotators.VertexLabelAnnotator) keypoint annotator.
|
||||
|
||||
- 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.
|
||||
|
||||
- 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`.
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
for frame in sv.get_video_frames_generator(
|
||||
source_path=<SOURCE_VIDEO_PATH>,
|
||||
start=60,
|
||||
iterative_seek=True
|
||||
):
|
||||
...
|
||||
```
|
||||
|
||||
- Fix [#1424](https://github.com/roboflow/supervision/pull/1424): `plot_image` function now clearly indicates that the size is in inches.
|
||||
|
||||
!!! failure "Removed"
|
||||
|
||||
The `track_buffer`, `track_thresh`, and `match_thresh` parameters in [`ByteTrack`](trackers.md/#supervision.tracker.byte_tracker.core.ByteTrack) are deprecated and were removed as of `supervision-0.23.0`. Use `lost_track_buffer,` `track_activation_threshold`, and `minimum_matching_threshold` instead.
|
||||
|
||||
!!! failure "Removed"
|
||||
|
||||
The `triggering_position` parameter in [`sv.PolygonZone`](detection/tools/polygon_zone.md/#supervision.detection.tools.polygon_zone.PolygonZone) was removed as of `supervision-0.23.0`. Use `triggering_anchors` instead.
|
||||
|
||||
!!! failure "Deprecated"
|
||||
|
||||
`overlap_filter_strategy` in `InferenceSlicer.__init__` is deprecated and will be removed in `supervision-0.27.0`. Use `overlap_strategy` instead.
|
||||
|
||||
!!! failure "Deprecated"
|
||||
|
||||
`overlap_ratio_wh` in `InferenceSlicer.__init__` is deprecated and will be removed in `supervision-0.27.0`. Use `overlap_wh` instead.
|
||||
|
||||
### 0.22.0 <small>Jul 12, 2024</small>
|
||||
|
||||
- Added [#1326](https://github.com/roboflow/supervision/pull/1326): [`sv.DetectionsDataset`](https://supervision.roboflow.com/latest/datasets/core/#supervision.dataset.core.DetectionDataset) and [`sv.ClassificationDataset`](https://supervision.roboflow.com/latest/datasets/core/#supervision.dataset.core.ClassificationDataset) allowing to load the images into memory only when necessary (lazy loading).
|
||||
- Added [#1326](https://github.com/roboflow/supervision/pull/1326): [`sv.DetectionsDataset`](https://supervision.roboflow.com/0.22.0/datasets/core/#supervision.dataset.core.DetectionDataset) and [`sv.ClassificationDataset`](https://supervision.roboflow.com/0.22.0/datasets/core/#supervision.dataset.core.ClassificationDataset) allowing to load the images into memory only when necessary (lazy loading).
|
||||
|
||||
!!! failure "Deprecated"
|
||||
|
||||
@@ -33,15 +450,15 @@ for path, image, annotation in ds_train:
|
||||
# loads image on demand
|
||||
```
|
||||
|
||||
- Added [#1296](https://github.com/roboflow/supervision/pull/1296): [`sv.Detections.from_lmm`](https://supervision.roboflow.com/latest/detection/core/#supervision.detection.core.Detections.from_lmm) now supports parsing results from the [Florence 2](https://huggingface.co/microsoft/Florence-2-large) model, extending the capability to handle outputs from this Large Multimodal Model (LMM). This includes detailed object detection, OCR with region proposals, segmentation, and more. Find out more in our [Colab notebook](https://colab.research.google.com/github/roboflow-ai/notebooks/blob/main/notebooks/how-to-finetune-florence-2-on-detection-dataset.ipynb).
|
||||
- Added [#1296](https://github.com/roboflow/supervision/pull/1296): [`sv.Detections.from_lmm`](https://supervision.roboflow.com/0.22.0/detection/core/#supervision.detection.core.Detections.from_lmm) now supports parsing results from the [Florence 2](https://huggingface.co/microsoft/Florence-2-large) model, extending the capability to handle outputs from this Large Multimodal Model (LMM). This includes detailed object detection, OCR with region proposals, segmentation, and more. Find out more in our [Colab notebook](https://colab.research.google.com/github/roboflow-ai/notebooks/blob/main/notebooks/how-to-finetune-florence-2-on-detection-dataset.ipynb).
|
||||
|
||||
- Added [#1232](https://github.com/roboflow/supervision/pull/1232) to support keypoint detection with Mediapipe. Both [legacy](https://colab.research.google.com/github/googlesamples/mediapipe/blob/main/examples/pose_landmarker/python/%5BMediaPipe_Python_Tasks%5D_Pose_Landmarker.ipynb) and [modern](https://ai.google.dev/edge/mediapipe/solutions/vision/pose_landmarker/python) pipelines are supported. See [`sv.KeyPoints.from_mediapipe`](https://supervision.roboflow.com/latest/keypoint/core/#supervision.keypoint.core.KeyPoints.from_mediapipe) for more.
|
||||
- Added [#1232](https://github.com/roboflow/supervision/pull/1232) to support keypoint detection with Mediapipe. Both [legacy](https://colab.research.google.com/github/googlesamples/mediapipe/blob/main/examples/pose_landmarker/python/%5BMediaPipe_Python_Tasks%5D_Pose_Landmarker.ipynb) and [modern](https://ai.google.dev/edge/mediapipe/solutions/vision/pose_landmarker/python) pipelines are supported. See [`sv.KeyPoints.from_mediapipe`](https://supervision.roboflow.com/0.22.0/keypoint/core/#supervision.keypoint.core.KeyPoints.from_mediapipe) for more.
|
||||
|
||||
- Added [#1316](https://github.com/roboflow/supervision/pull/1316): [`sv.KeyPoints.from_mediapipe`](https://supervision.roboflow.com/latest/keypoint/core/#supervision.keypoint.core.KeyPoints.from_mediapipe) extended to support FaceMesh from Mediapipe. This enhancement allows for processing both face landmarks from `FaceLandmarker`, and legacy results from `FaceMesh`.
|
||||
- Added [#1316](https://github.com/roboflow/supervision/pull/1316): [`sv.KeyPoints.from_mediapipe`](https://supervision.roboflow.com/0.22.0/keypoint/core/#supervision.keypoint.core.KeyPoints.from_mediapipe) extended to support FaceMesh from Mediapipe. This enhancement allows for processing both face landmarks from `FaceLandmarker`, and legacy results from `FaceMesh`.
|
||||
|
||||
- Added [#1310](https://github.com/roboflow/supervision/pull/1310): [`sv.KeyPoints.from_detectron2`](https://supervision.roboflow.com/latest/keypoint/core/#supervision.keypoint.core.KeyPoints.from_detectron2) is a new `KeyPoints` method, adding support for extracting keypoints from the popular [Detectron 2](https://github.com/facebookresearch/detectron2) platform.
|
||||
- Added [#1310](https://github.com/roboflow/supervision/pull/1310): [`sv.KeyPoints.from_detectron2`](https://supervision.roboflow.com/0.22.0/keypoint/core/#supervision.keypoint.core.KeyPoints.from_detectron2) is a new `KeyPoints` method, adding support for extracting keypoints from the popular [Detectron 2](https://github.com/facebookresearch/detectron2) platform.
|
||||
|
||||
- Added [#1300](https://github.com/roboflow/supervision/pull/1300): [`sv.Detections.from_detectron2`](https://supervision.roboflow.com/latest/detection/core/#supervision.detection.core.Detections.from_detectron2) now supports segmentation models detectron2. The resulting masks can be used with [`sv.MaskAnnotator`](https://supervision.roboflow.com/latest/annotators/#supervision.annotators.core.MaskAnnotator) for displaying annotations.
|
||||
- Added [#1300](https://github.com/roboflow/supervision/pull/1300): [`sv.Detections.from_detectron2`](https://supervision.roboflow.com/0.22.0/detection/core/#supervision.detection.core.Detections.from_detectron2) now supports segmentation models detectron2. The resulting masks can be used with [`sv.MaskAnnotator`](https://supervision.roboflow.com/0.22.0/detection/annotators/#supervision.annotators.core.MaskAnnotator) for displaying annotations.
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
@@ -63,7 +480,7 @@ mask_annotator = sv.MaskAnnotator()
|
||||
annotated_frame = mask_annotator.annotate(scene=image.copy(), detections=detections)
|
||||
```
|
||||
|
||||
- Added [#1277](https://github.com/roboflow/supervision/pull/1277): if you provide a font that supports symbols of a language, [`sv.RichLabelAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.LabelAnnotator.annotate) will draw them on your images.
|
||||
- Added [#1277](https://github.com/roboflow/supervision/pull/1277): if you provide a font that supports symbols of a language, [`sv.RichLabelAnnotator`](https://supervision.roboflow.com/0.22.0/detection/annotators/#supervision.annotators.core.LabelAnnotator.annotate) will draw them on your images.
|
||||
- Various other annotators have been revised to ensure proper in-place functionality when used with `numpy` arrays. Additionally, we fixed a bug where `sv.ColorAnnotator` was filling boxes with solid color when used in-place.
|
||||
|
||||
```python
|
||||
@@ -90,7 +507,7 @@ train_ds = sv.DetectionDataset.from_yolo(
|
||||
images_directory_path="/content/dataset/train/images",
|
||||
annotations_directory_path="/content/dataset/train/labels",
|
||||
data_yaml_path="/content/dataset/data.yaml",
|
||||
is_obb=True
|
||||
is_obb=True,
|
||||
)
|
||||
|
||||
_, image, detections in train_ds[0]
|
||||
@@ -99,25 +516,25 @@ obb_annotator = OrientedBoxAnnotator()
|
||||
annotated_image = obb_annotator.annotate(scene=image.copy(), detections=detections)
|
||||
```
|
||||
|
||||
- Fixed [#1312](https://github.com/roboflow/supervision/pull/1312): Fixed [`CropAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.TraceAnnotator.annotate).
|
||||
- Fixed [#1312](https://github.com/roboflow/supervision/pull/1312): Fixed [`CropAnnotator`](https://supervision.roboflow.com/0.22.0/detection/annotators/#supervision.annotators.core.TraceAnnotator.annotate).
|
||||
|
||||
!!! failure "Removed"
|
||||
|
||||
`BoxAnnotator` was removed, however `BoundingBoxAnnotator` has been renamed to `BoxAnnotator`. Use a combination of [`BoxAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator) and [`LabelAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.LabelAnnotator) to simulate old `BoundingBox` behavior.
|
||||
`BoxAnnotator` was removed, however `BoundingBoxAnnotator` has been renamed to `BoxAnnotator`. Use a combination of [`BoxAnnotator`](https://supervision.roboflow.com/0.22.0/detection/annotators/#supervision.annotators.core.BoxAnnotator) and [`LabelAnnotator`](https://supervision.roboflow.com/0.22.0/detection/annotators/#supervision.annotators.core.LabelAnnotator) to simulate old `BoundingBox` behavior.
|
||||
|
||||
!!! failure "Deprecated"
|
||||
|
||||
The name `BoundingBoxAnnotator` has been deprecated and will be removed in `supervision-0.26.0`. It has been renamed to [`BoxAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator).
|
||||
The name `BoundingBoxAnnotator` has been deprecated and will be removed in `supervision-0.26.0`. It has been renamed to [`BoxAnnotator`](https://supervision.roboflow.com/0.22.0/detection/annotators/#supervision.annotators.core.BoxAnnotator).
|
||||
|
||||
- Added [#975](https://github.com/roboflow/supervision/pull/975) 📝 New Cookbooks: serialize detections into [json](https://github.com/roboflow/supervision/blob/de896189b83a1f9434c0a37dd9192ee00d2a1283/docs/notebooks/serialise-detections-to-json.ipynb) and [csv](https://github.com/roboflow/supervision/blob/de896189b83a1f9434c0a37dd9192ee00d2a1283/docs/notebooks/serialise-detections-to-csv.ipynb).
|
||||
|
||||
- Added [#1290](https://github.com/roboflow/supervision/pull/1290): Mostly an internal change, our file utility function now support both `str` and `pathlib` paths.
|
||||
|
||||
- Added [#1340](https://github.com/roboflow/supervision/pull/1340): Two new methods for converting between bounding box formats - [`xywh_to_xyxy`](https://supervision.roboflow.com/latest/detection/utils/#supervision.detection.utils.xywh_to_xyxy) and [`xcycwh_to_xyxy`](https://supervision.roboflow.com/latest/detection/utils/#supervision.detection.utils.xcycwh_to_xyxy)
|
||||
- Added [#1340](https://github.com/roboflow/supervision/pull/1340): Two new methods for converting between bounding box formats - [`xywh_to_xyxy`](https://supervision.roboflow.com/0.22.0/detection/utils/#supervision.detection.utils.xywh_to_xyxy) and [`xcycwh_to_xyxy`](https://supervision.roboflow.com/0.22.0/detection/utils/#supervision.detection.utils.xcycwh_to_xyxy)
|
||||
|
||||
!!! failure "Removed"
|
||||
|
||||
`from_roboflow` method has been removed due to deprecation. Use [from_inference](https://supervision.roboflow.com/latest/detection/core/#supervision.detection.core.Detections.from_inference) instead.
|
||||
`from_roboflow` method has been removed due to deprecation. Use [from_inference](https://supervision.roboflow.com/0.22.0/detection/core/#supervision.detection.core.Detections.from_inference) instead.
|
||||
|
||||
!!! failure "Removed"
|
||||
|
||||
@@ -141,17 +558,17 @@ annotated_image = obb_annotator.annotate(scene=image.copy(), detections=detectio
|
||||
|
||||
!!! failure "Removed"
|
||||
|
||||
`ColorPalette.default()` has been removed due to deprecation. Use [ColorPalette.DEFAULT](https://supervision.roboflow.com/latest/utils/draw/#supervision.draw.color.ColorPalette.DEFAULT) instead.
|
||||
`ColorPalette.default()` has been removed due to deprecation. Use [ColorPalette.DEFAULT](https://supervision.roboflow.com/0.22.0/utils/draw/#supervision.draw.color.ColorPalette.DEFAULT) instead.
|
||||
|
||||
!!! failure "Removed"
|
||||
|
||||
`FPSMonitor.__call__` has been removed due to deprecation. Use the attribute [FPSMonitor.fps](https://supervision.roboflow.com/latest/utils/video/#supervision.utils.video.FPSMonitor.fps) instead.
|
||||
`FPSMonitor.__call__` has been removed due to deprecation. Use the attribute [FPSMonitor.fps](https://supervision.roboflow.com/0.22.0/utils/video/#supervision.utils.video.FPSMonitor.fps) instead.
|
||||
|
||||
### 0.21.0 <small>Jun 5, 2024</small>
|
||||
|
||||
- Added [#500](https://github.com/roboflow/supervision/pull/500): [`sv.Detections.with_nmm`](https://supervision.roboflow.com/latest/detection/core/#supervision.detection.core.Detections.with_nmm) to perform non-maximum merging on the current set of object detections.
|
||||
- Added [#500](https://github.com/roboflow/supervision/pull/500): [`sv.Detections.with_nmm`](https://supervision.roboflow.com/0.21.0/detection/core/#supervision.detection.core.Detections.with_nmm) to perform non-maximum merging on the current set of object detections.
|
||||
|
||||
- Added [#1221](https://github.com/roboflow/supervision/pull/1221): [`sv.Detections.from_lmm`](https://supervision.roboflow.com/latest/detection/core/#supervision.detection.core.Detections.from_lmm) allowing to parse Large Multimodal Model (LMM) text result into [`sv.Detections`](https://supervision.roboflow.com/latest/detection/core/) object. For now `from_lmm` supports only [PaliGemma](https://colab.research.google.com/github/roboflow-ai/notebooks/blob/main/notebooks/how-to-finetune-paligemma-on-detection-dataset.ipynb) result parsing.
|
||||
- Added [#1221](https://github.com/roboflow/supervision/pull/1221): [`sv.Detections.from_lmm`](https://supervision.roboflow.com/0.21.0/detection/core/#supervision.detection.core.Detections.from_lmm) allowing to parse Large Multimodal Model (LMM) text result into [`sv.Detections`](https://supervision.roboflow.com/0.21.0/detection/core/) object. For now `from_lmm` supports only [PaliGemma](https://colab.research.google.com/github/roboflow-ai/notebooks/blob/main/notebooks/how-to-finetune-paligemma-on-detection-dataset.ipynb) result parsing.
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
@@ -161,7 +578,7 @@ detections = sv.Detections.from_lmm(
|
||||
sv.LMM.PALIGEMMA,
|
||||
paligemma_result,
|
||||
resolution_wh=(1000, 1000),
|
||||
classes=['cat', 'dog']
|
||||
classes=["cat", "dog"],
|
||||
)
|
||||
detections.xyxy
|
||||
# array([[250., 250., 750., 750.]])
|
||||
@@ -170,7 +587,7 @@ detections.class_id
|
||||
# array([0])
|
||||
```
|
||||
|
||||
- Added [#1236](https://github.com/roboflow/supervision/pull/1236): [`sv.VertexLabelAnnotator`](https://supervision.roboflow.com/latest/keypoint/annotators/#supervision.keypoint.annotators.EdgeAnnotator.annotate) allowing to annotate every vertex of a keypoint skeleton with custom text and color.
|
||||
- Added [#1236](https://github.com/roboflow/supervision/pull/1236): [`sv.VertexLabelAnnotator`](https://supervision.roboflow.com/0.21.0/keypoint/annotators/#supervision.keypoint.annotators.EdgeAnnotator.annotate) allowing to annotate every vertex of a keypoint skeleton with custom text and color.
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
@@ -188,15 +605,15 @@ annotated_frame = edge_annotator.annotate(
|
||||
)
|
||||
```
|
||||
|
||||
- Added [#1147](https://github.com/roboflow/supervision/pull/1147): [`sv.KeyPoints.from_inference`](https://supervision.roboflow.com/develop/keypoint/core/#supervision.keypoint.core.KeyPoints.from_inference) allowing to create [`sv.KeyPoints`](https://supervision.roboflow.com/develop/keypoint/core/#supervision.keypoint.core.KeyPoints) from [Inference](https://github.com/roboflow/inference) result.
|
||||
- Added [#1147](https://github.com/roboflow/supervision/pull/1147): [`sv.KeyPoints.from_inference`](https://supervision.roboflow.com/0.21.0/keypoint/core/#supervision.keypoint.core.KeyPoints.from_inference) allowing to create [`sv.KeyPoints`](https://supervision.roboflow.com/0.21.0/keypoint/core/#supervision.keypoint.core.KeyPoints) from [Inference](https://github.com/roboflow/inference) result.
|
||||
|
||||
- Added [#1138](https://github.com/roboflow/supervision/pull/1138): [`sv.KeyPoints.from_yolo_nas`](https://supervision.roboflow.com/develop/keypoint/core/#supervision.keypoint.core.KeyPoints.from_yolo_nas) allowing to create [`sv.KeyPoints`](https://supervision.roboflow.com/develop/keypoint/core/#supervision.keypoint.core.KeyPoints) from [YOLO-NAS](https://github.com/Deci-AI/super-gradients/blob/master/YOLONAS.md) result.
|
||||
- Added [#1138](https://github.com/roboflow/supervision/pull/1138): [`sv.KeyPoints.from_yolo_nas`](https://supervision.roboflow.com/0.21.0/keypoint/core/#supervision.keypoint.core.KeyPoints.from_yolo_nas) allowing to create [`sv.KeyPoints`](https://supervision.roboflow.com/0.21.0/keypoint/core/#supervision.keypoint.core.KeyPoints) from [YOLO-NAS](https://github.com/Deci-AI/super-gradients/blob/master/YOLONAS.md) result.
|
||||
|
||||
- Added [#1163](https://github.com/roboflow/supervision/pull/1163): [`sv.mask_to_rle`](https://supervision.roboflow.com/develop/datasets/utils/#supervision.dataset.utils.rle_to_mask) and [`sv.rle_to_mask`](https://supervision.roboflow.com/develop/datasets/utils/#supervision.dataset.utils.rle_to_mask) allowing for easy conversion between mask and rle formats.
|
||||
- Added [#1163](https://github.com/roboflow/supervision/pull/1163): [`sv.mask_to_rle`](https://supervision.roboflow.com/0.21.0/datasets/utils/#supervision.dataset.utils.rle_to_mask) and [`sv.rle_to_mask`](https://supervision.roboflow.com/0.21.0/datasets/utils/#supervision.dataset.utils.rle_to_mask) allowing for easy conversion between mask and rle formats.
|
||||
|
||||
- Changed [#1236](https://github.com/roboflow/supervision/pull/1236): [`sv.InferenceSlicer`](https://supervision.roboflow.com/develop/detection/tools/inference_slicer/) allowing to select overlap filtering strategy (`NONE`, `NON_MAX_SUPPRESSION` and `NON_MAX_MERGE`).
|
||||
- Changed [#1236](https://github.com/roboflow/supervision/pull/1236): [`sv.InferenceSlicer`](https://supervision.roboflow.com/0.21.0/detection/tools/inference_slicer/) allowing to select overlap filtering strategy (`NONE`, `NON_MAX_SUPPRESSION` and `NON_MAX_MERGE`).
|
||||
|
||||
- Changed [#1178](https://github.com/roboflow/supervision/pull/1178): [`sv.InferenceSlicer`](https://supervision.roboflow.com/develop/detection/tools/inference_slicer/) adding instance segmentation model support.
|
||||
- Changed [#1178](https://github.com/roboflow/supervision/pull/1178): [`sv.InferenceSlicer`](https://supervision.roboflow.com/0.21.0/detection/tools/inference_slicer/) adding instance segmentation model support.
|
||||
|
||||
```python
|
||||
import cv2
|
||||
@@ -223,9 +640,9 @@ annotated_image = label_annotator.annotate(
|
||||
scene=annotated_image, detections=detections)
|
||||
```
|
||||
|
||||
- Changed [#1228](https://github.com/roboflow/supervision/pull/1228): [`sv.LineZone`](https://supervision.roboflow.com/develop/detection/tools/line_zone/) making it 10-20 times faster, depending on the use case.
|
||||
- Changed [#1228](https://github.com/roboflow/supervision/pull/1228): [`sv.LineZone`](https://supervision.roboflow.com/0.21.0/detection/tools/line_zone/) making it 10-20 times faster, depending on the use case.
|
||||
|
||||
- Changed [#1163](https://github.com/roboflow/supervision/pull/1163): [`sv.DetectionDataset.from_coco`](https://supervision.roboflow.com/develop/datasets/core/#supervision.dataset.core.DetectionDataset.from_coco) and [`sv.DetectionDataset.as_coco`](https://supervision.roboflow.com/develop/datasets/core/#supervision.dataset.core.DetectionDataset.as_coco) adding support for run-length encoding (RLE) mask format.
|
||||
- Changed [#1163](https://github.com/roboflow/supervision/pull/1163): [`sv.DetectionDataset.from_coco`](https://supervision.roboflow.com/0.21.0/datasets/core/#supervision.dataset.core.DetectionDataset.from_coco) and [`sv.DetectionDataset.as_coco`](https://supervision.roboflow.com/0.21.0/datasets/core/#supervision.dataset.core.DetectionDataset.as_coco) adding support for run-length encoding (RLE) mask format.
|
||||
|
||||
### 0.20.0 <small>April 24, 2024</small>
|
||||
|
||||
@@ -352,15 +769,15 @@ annotated_frame = crop_annotator.annotate(
|
||||
)
|
||||
```
|
||||
|
||||
- Changed [#827](https://github.com/roboflow/supervision/pull/827): [`sv.ByteTrack.reset`](/0.19.0/tracking/#supervision.tracking.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`](/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 [#787](https://github.com/roboflow/supervision/pull/787): [`sv.ByteTrack`](/0.19.0/tracking/#supervision.tracking.ByteTrack) input arguments and docstrings updated to improve readability and ease of use.
|
||||
- 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.
|
||||
|
||||
!!! failure "Deprecated"
|
||||
|
||||
The `track_buffer`, `track_thresh`, and `match_thresh` parameters in `sv.ByterTrack` are deprecated and will be removed in `supervision-0.23.0`. Use `lost_track_buffer,` `track_activation_threshold`, and `minimum_matching_threshold` instead.
|
||||
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.
|
||||
|
||||
@@ -783,7 +1200,7 @@ array([
|
||||
|
||||
- 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 [#122](https://github.com/roboflow/supervision/pull/122): ability to crop image using [`sv.crop`](/latest/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`](/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.
|
||||
|
||||
@@ -795,11 +1212,11 @@ array([
|
||||
... sink.save_image(image=image)
|
||||
```
|
||||
|
||||
- Fixed [#106](https://github.com/roboflow/supervision/issues/106): inconvenient handling of [`sv.PolygonZone`](/0.8.0/supervision/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`](/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/supervision/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): 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).
|
||||
|
||||
```python
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# 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`.
|
||||
|
||||
|
||||
+27
-8
@@ -7,21 +7,40 @@ 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.
|
||||
|
||||
- The `track_buffer`, `track_thresh`, and `match_thresh` parameters in [`ByterTrack`](trackers.md/#supervision.tracker.byte_tracker.core.ByteTrack) are deprecated and will be removed in `supervision-0.23.0`. Use `lost_track_buffer,` `track_activation_threshold`, and `minimum_matching_threshold` instead.
|
||||
- The `triggering_position ` parameter in [`sv.PolygonZone`](detection/tools/polygon_zone.md/#supervision.detection.tools.polygon_zone.PolygonZone) will be removed in `supervision-0.23.0`. Use `triggering_anchors ` instead.
|
||||
- The `frame_resolution_wh ` parameter in [`sv.PolygonZone`](detection/tools/polygon_zone.md/#supervision.detection.tools.polygon_zone.PolygonZone) will be removed in `supervision-0.24.0`.
|
||||
- Constructing `DetectionDataset` and `ClassificationDataset` with parameter `images` as `Dict[str, np.ndarray]` will be removed in `supervision-0.26.0`. Please pass a list of paths `List[str]` instead.
|
||||
- Constructing [`DetectionDataset`](https://supervision.roboflow.com/latest/datasets/core/#supervision.dataset.core.DetectionDataset) and [`ClassificationDataset`](https://supervision.roboflow.com/latest/datasets/core/#supervision.dataset.core.ClassificationDataset) with parameter `images` as `Dict[str, np.ndarray]` will be removed in `supervision-0.26.0`. Please pass a list of paths `List[str]` instead.
|
||||
|
||||
- The `DetectionDataset.images` property will be removed in `supervision-0.26.0`. Please loop over images with `for path, image, annotation in dataset:`, as that does not require loading all images into memory.
|
||||
- `BoundingBoxAnnotator` has been renamed to `BoxAnnotator` after the old implementation of `BoxAnnotator` has been removed. `BoundingBoxAnnotator` will be removed in `supervision-0.26.0`.
|
||||
|
||||
- `BoundingBoxAnnotator` has been renamed to `BoxAnnotator` after the old implementation of [`BoxAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator) has been removed. `BoundingBoxAnnotator` will be removed in `supervision-0.26.0`.
|
||||
|
||||
- `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.
|
||||
|
||||
# Removed
|
||||
|
||||
- [`Detections.from_froboflow`](detection/core.md/#supervision.detection.core.Detections.from_roboflow) is removed as of `supervision-0.22.0`. Use [`Detections.from_inference`](detection/core.md/#supervision.detection.core.Detections.from_inference) instead.
|
||||
### 0.25.0
|
||||
|
||||
No removals in this version!
|
||||
|
||||
### 0.24.0
|
||||
|
||||
- The `frame_resolution_wh ` parameter in [`sv.PolygonZone`](detection/tools/polygon_zone.md/#supervision.detection.tools.polygon_zone.PolygonZone) has been removed.
|
||||
- Supervision installation methods `"headless"` and `"desktop"` were removed, as they are no longer needed. `pip install supervision[headless]` will install the base library and harmlessly warn of non-existent extras.
|
||||
|
||||
### 0.23.0
|
||||
|
||||
- The `track_buffer`, `track_thresh`, and `match_thresh` parameters in [`ByteTrack`](trackers.md/#supervision.tracker.byte_tracker.core.ByteTrack) are deprecated and were removed as of `supervision-0.23.0`. Use `lost_track_buffer,` `track_activation_threshold`, and `minimum_matching_threshold` instead.
|
||||
- The `triggering_position ` parameter in [`sv.PolygonZone`](detection/tools/polygon_zone.md/#supervision.detection.tools.polygon_zone.PolygonZone) was removed as of `supervision-0.23.0`. Use `triggering_anchors` instead.
|
||||
|
||||
### 0.22.0
|
||||
|
||||
- `Detections.from_roboflow` is removed as of `supervision-0.22.0`. Use [`Detections.from_inference`](detection/core.md/#supervision.detection.core.Detections.from_inference) instead.
|
||||
- The method `Color.white()` was removed as of `supervision-0.22.0`. Use the constant `Color.WHITE` instead.
|
||||
- The method `Color.black()` was removed as of `supervision-0.22.0`. Use the constant `Color.BLACK` instead.
|
||||
- The method `Color.red()` was removed as of `supervision-0.22.0`. Use the constant `Color.RED` instead.
|
||||
- The method `Color.green()` was removed as of `supervision-0.22.0`. Use the constant `Color.GREEN` instead.
|
||||
- The method `Color.blue()` was removed as of `supervision-0.22.0`. Use the constant `Color.BLUE` instead.
|
||||
- The method [`ColorPalette.default()`](draw/color.md/#supervision.draw.color.ColorPalette.default) was removed as of `supervision-0.22.0`. Use the constant [`ColorPalette.DEFAULT`](draw/color.md/#supervision.draw.color.ColorPalette.DEFAULT) instead.
|
||||
- The method `ColorPalette.default()` was removed as of `supervision-0.22.0`. Use the constant [`ColorPalette.DEFAULT`](/utils/draw/#supervision.draw.color.ColorPalette.DEFAULT) instead.
|
||||
- `BoxAnnotator` was removed as of `supervision-0.22.0`, however `BoundingBoxAnnotator` was immediately renamed to `BoxAnnotator`. Use [`BoxAnnotator`](detection/annotators.md/#supervision.annotators.core.BoxAnnotator) and [`LabelAnnotator`](detection/annotators.md/#supervision.annotators.core.LabelAnnotator) instead of the old `BoxAnnotator`.
|
||||
- The method [`FPSMonitor.__call__`](utils/video.md/#supervision.utils.video.FPSMonitor.__call__) was removed as of `supervision-0.22.0`. Use the attribute [`FPSMonitor.fps`](utils/video.md/#supervision.utils.video.FPSMonitor.fps) instead.
|
||||
- The method `FPSMonitor.__call__` was removed as of `supervision-0.22.0`. Use the attribute [`FPSMonitor.fps`](utils/video.md/#supervision.utils.video.FPSMonitor.fps) instead.
|
||||
|
||||
+146
-29
@@ -5,6 +5,8 @@ status: new
|
||||
|
||||
# Annotators
|
||||
|
||||
Annotators accept detections and apply box or mask visualizations to the detections. Annotators have many available styles.
|
||||
|
||||
=== "Box"
|
||||
|
||||
```python
|
||||
@@ -26,26 +28,28 @@ status: new
|
||||
|
||||
</div>
|
||||
|
||||
=== "RoundBox"
|
||||
<!--
|
||||
=== "RoundBox"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
round_box_annotator = sv.RoundBoxAnnotator()
|
||||
annotated_frame = round_box_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
round_box_annotator = sv.RoundBoxAnnotator()
|
||||
annotated_frame = round_box_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
|
||||
=== "BoxCorner"
|
||||
|
||||
@@ -232,7 +236,8 @@ status: new
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -253,7 +258,8 @@ status: new
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -281,7 +287,8 @@ status: new
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -303,7 +310,7 @@ status: new
|
||||
font_path="<TTF_FONT_PATH>",
|
||||
text_position=sv.Position.CENTER
|
||||
)
|
||||
annotated_frame = label_annotator.annotate(
|
||||
annotated_frame = rich_label_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
labels=labels
|
||||
@@ -312,11 +319,12 @@ status: new
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Crop"
|
||||
=== "Icon"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
@@ -324,13 +332,48 @@ status: new
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
crop_annotator = sv.CropAnnotator()
|
||||
annotated_frame = crop_annotator.annotate(
|
||||
icon_paths = [
|
||||
"<ICON_PATH>"
|
||||
for _ in detections
|
||||
]
|
||||
|
||||
icon_annotator = sv.IconAnnotator()
|
||||
annotated_frame = icon_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
detections=detections,
|
||||
icon_path=icon_paths
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
<!-- === "Crop"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
crop_annotator = sv.CropAnnotator()
|
||||
annotated_frame = crop_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
-->
|
||||
|
||||
=== "Blur"
|
||||
|
||||
```python
|
||||
@@ -348,7 +391,8 @@ status: new
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -369,7 +413,8 @@ status: new
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -384,7 +429,7 @@ status: new
|
||||
trace_annotator = sv.TraceAnnotator()
|
||||
|
||||
video_info = sv.VideoInfo.from_video_path(video_path='...')
|
||||
frames_generator = get_video_frames_generator(source_path='...')
|
||||
frames_generator = sv.get_video_frames_generator(source_path='...')
|
||||
tracker = sv.ByteTrack()
|
||||
|
||||
with sv.VideoSink(target_path='...', video_info=video_info) as sink:
|
||||
@@ -400,7 +445,8 @@ status: new
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -415,7 +461,7 @@ status: new
|
||||
heat_map_annotator = sv.HeatMapAnnotator()
|
||||
|
||||
video_info = sv.VideoInfo.from_video_path(video_path='...')
|
||||
frames_generator = get_video_frames_generator(source_path='...')
|
||||
frames_generator = sv.get_video_frames_generator(source_path='...')
|
||||
|
||||
with sv.VideoSink(target_path='...', video_info=video_info) as sink:
|
||||
for frame in frames_generator:
|
||||
@@ -429,10 +475,63 @@ status: new
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Background Color"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
background_overlay_annotator = sv.BackgroundOverlayAnnotator()
|
||||
annotated_frame = background_overlay_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Comparison"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections_1 = sv.Detections(...)
|
||||
detections_2 = sv.Detections(...)
|
||||
|
||||
comparison_annotator = sv.ComparisonAnnotator()
|
||||
annotated_frame = comparison_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections_1=detections_1,
|
||||
detections_2=detections_2
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2>Try Supervision Annotators on your own image</h2>
|
||||
Visualize annotators on images with COCO classes such as people, vehicles, animals, household items.
|
||||
</div>
|
||||
|
||||
<div style="height: 400px; width: 100%; border-radius: 8px; overflow: hidden;"><iframe src="https://app.roboflow.com/workflows/embed/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ3b3JrZmxvd0lkIjoiNDdtd2xuWW16S25VNWtOYUZjMG8iLCJ3b3Jrc3BhY2VJZCI6ImtyT1RBYm5jRmhvUU1DZExPbGU0IiwidXNlcklkIjoiRVJNUFBZY3FQMmZWWjB1NkRpNXZaYXJDdlZPMiIsImlhdCI6MTcyNjgzOTM2N30.gj2F6SnmmURAScJe4PTC1raUXsAK5mZyrUIGIJ44NhM?hideToolbar=true&hideHeader=true&defaultVisual=true" loading="lazy" title="Roboflow Workflow for Supervision Annotators" style="width: 100%; height: 100%; min-height: 400px; border: none;"></iframe></div>
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.BoxAnnotator">BoxAnnotator</a></h2>
|
||||
</div>
|
||||
@@ -529,6 +628,12 @@ status: new
|
||||
|
||||
:::supervision.annotators.core.RichLabelAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.IconAnnotator">IconAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.IconAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.BlurAnnotator">BlurAnnotator</a></h2>
|
||||
</div>
|
||||
@@ -553,6 +658,18 @@ status: new
|
||||
|
||||
:::supervision.annotators.core.CropAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.BackgroundOverlayAnnotator">BackgroundOverlayAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.BackgroundOverlayAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.ComparisonAnnotator">ComparisonAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.ComparisonAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.ColorLookup">ColorLookup</a></h2>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
# Metrics
|
||||
# Legacy Metrics
|
||||
|
||||
Starting with `0.23.0`, a new metrics module is being introduced to supervision.
|
||||
Metrics here are part of the legacy evaluation API and will be deprecated in the future.
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.metrics.detection.ConfusionMatrix">ConfusionMatrix</a></h2>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
<div class="md-typeset">
|
||||
@@ -13,3 +14,9 @@ comments: true
|
||||
</div>
|
||||
|
||||
:::supervision.detection.line_zone.LineZoneAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2>LineZoneAnnotatorMulticlass</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.line_zone.LineZoneAnnotatorMulticlass
|
||||
|
||||
+15
-3
@@ -17,6 +17,12 @@ status: new
|
||||
|
||||
:::supervision.detection.utils.mask_iou_batch
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.detection.utils.oriented_box_iou_batch">oriented_box_iou_batch</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.oriented_box_iou_batch
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.detection.utils.polygon_to_mask">polygon_to_mask</a></h2>
|
||||
</div>
|
||||
@@ -78,19 +84,25 @@ status: new
|
||||
:::supervision.detection.utils.pad_boxes
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.detection.utils.contains_holes">contains_holes</a></h2>
|
||||
<h2><a href="#supervision.detection.utils.xywh_to_xyxy">xywh_to_xyxy</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.xywh_to_xyxy
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.detection.utils.xywh_to_xyxy">xywh_to_xyxy</a></h2>
|
||||
<h2><a href="#supervision.detection.utils.xyxy_to_xywh">xyxy_to_xywh</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.xyxy_to_xywh
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.detection.utils.xcycwh_to_xyxy">xcycwh_to_xyxy</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.xcycwh_to_xyxy
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.detection.utils.xcycwh_to_xyxy">xcycwh_to_xyxy</a></h2>
|
||||
<h2><a href="#supervision.detection.utils.contains_holes">contains_holes</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.contains_holes
|
||||
|
||||
@@ -0,0 +1,441 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||

|
||||
|
||||
# Benchmark a Model
|
||||
|
||||
Have you ever trained multiple detection models and wondered which one performs best on your specific use case? Or maybe you've downloaded a pre-trained model and want to verify its performance on your dataset? Model benchmarking is essential for making informed decisions about which model to deploy in production.
|
||||
|
||||
This guide will show an easy way to benchmark your results using `supervision`. It will go over:
|
||||
|
||||
1. [Loading a dataset](#loading-a-dataset)
|
||||
2. [Loading a model](#loading-a-model)
|
||||
3. [Benchmarking Basics](#benchmarking-basics)
|
||||
4. [Running a Model](#running-a-model)
|
||||
5. [Remapping Classes](#remapping-classes)
|
||||
6. [Visual Benchmarking](#visual-benchmarking)
|
||||
7. [Benchmarking Metrics](#benchmarking-metrics)
|
||||
8. [Mean Average Precision (mAP)](#mean-average-precision-map)
|
||||
9. [F1 Score](#f1-score)
|
||||
10. [Bonus: Model Leaderboard](#model-leaderboard)
|
||||
|
||||
This guide will use an instance segmentation model, but it applies to object detection, instance segmentation, and oriented bounding box models (OBB) too.
|
||||
|
||||
A condensed version of this guide is available as a [Colab Notebook](https://colab.research.google.com/drive/1HoOY9pZoVwGiRMmLHtir0qT6Uj45w6Ps?usp=sharing).
|
||||
|
||||
## Loading a Dataset
|
||||
|
||||
Suppose you start with a dataset. Perhaps you found it on [Universe](https://universe.roboflow.com/); perhaps you [labeled your own](https://roboflow.com/how-to-label/yolo11). In either case, this guide assumes you know of a labelled dataset at hand.
|
||||
|
||||
We'll use the following libraries:
|
||||
|
||||
- `roboflow` to manage the dataset and deploy models
|
||||
- `inference` to run the models
|
||||
- `supervision` to evaluate the model results
|
||||
|
||||
```bash
|
||||
pip install roboflow supervision
|
||||
pip install git+https://github.com/roboflow/inference.git@linas/allow-latest-rc-supervision
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
||||
We're updating `inference` at the moment. Please install it as shown above.
|
||||
|
||||
Here's how you can download a dataset:
|
||||
|
||||
```python
|
||||
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>")
|
||||
```
|
||||
|
||||
If your dataset is from Universe, go to `Dataset` > `Download Dataset` > select the format (e.g. `YOLOv11`) > `Show download code`.
|
||||
|
||||
If labeling your own data, go to the [dashboard](https://app.roboflow.com/) and check this [guide](https://docs.roboflow.com/api-reference/workspace-and-project-ids) to find your workspace and project IDs.
|
||||
|
||||
In this guide, we shall use a small [Corgi v2](https://universe.roboflow.com/model-examples/segmented-animals-basic) dataset. It is well-labeled and comes with a test set.
|
||||
|
||||
```python
|
||||
from roboflow import Roboflow
|
||||
|
||||
rf = Roboflow(api_key="<YOUR_API_KEY>")
|
||||
project = rf.workspace("fbamse1-gm2os").project("corgi-v2")
|
||||
dataset = project.version(4).download("yolov11")
|
||||
```
|
||||
|
||||
This will create a folder called `Corgi-v2-4` with the dataset in the current working directory, with `train`, `test`, and `valid` folders and a `data.yaml` file.
|
||||
|
||||
## Loading a Model
|
||||
|
||||
Let's load a model.
|
||||
|
||||
=== "Inference, Local"
|
||||
|
||||
Roboflow supports a range of state-of-the-art [pre-trained models](https://inference.roboflow.com/quickstart/aliases/) for object detection, instance segmentation, and pose tracking. You don't even need an API key!
|
||||
|
||||
Let's load such a model with inference [`inference`](https://inference.roboflow.com/).
|
||||
|
||||
```python
|
||||
from inference import get_model
|
||||
|
||||
model = get_model(model_id="yolov11s-seg-640")
|
||||
```
|
||||
|
||||
=== "Inference, Deployed"
|
||||
|
||||
You can train and deploy a model without leaving the Roboflow platform. See this [guide](https://docs.roboflow.com/train/train/train-from-scratch) for more details.
|
||||
|
||||
To load a model, you can use inference:
|
||||
|
||||
```python
|
||||
from inference import get_model
|
||||
|
||||
model_id = "<PROJECT_NAME>/<MODEL_VERSION>"
|
||||
model = get_model(model_id=model_id)
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
Similarly to Inference, Ultralytics allows you to run a variety of models.
|
||||
|
||||
```bash
|
||||
pip install "ultralytics<=8.3.40"
|
||||
```
|
||||
|
||||
```python
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolo11s-seg.pt")
|
||||
```
|
||||
|
||||
## Benchmarking Basics
|
||||
|
||||
Evaluating your model requires careful selection of the dataset. Which images should you use?Let's go over the different scenarios.
|
||||
|
||||
- **Unrelated Dataset**: If you have a dataset that was not used to train the model, this is the best choice.
|
||||
- **Training Set**: This is the set of images used to train the model. This is fine if the model was not trained on this dataset. Otherwise, **never** use it for benchmarking - the results will seem unrealistically good.
|
||||
- **Validation Set**: This is the set of images used to validate the model during training. Every Nth training epoch, the model is evaluated on the validation set. Often the training is stopped once the validation loss stops improving. Therefore, even while the images aren't used to train the model, it still indirectly influences the training outcome.
|
||||
- **Test Set**: This is the set of images kept aside for model testing. It is exactly the set you should use for benchmarking. If the dataset was split correctly, none of these images would be shown to the model during training.
|
||||
|
||||
Therefore, an unrelated dataset or the `test` set is the best choice for benchmarking.
|
||||
Several other problems may arise:
|
||||
|
||||
- **Extra Classes**: An unrelated dataset may contain additional classes which you may need to [filter out](https://supervision.roboflow.com/how_to/filter_detections/#by-set-of-classes) before computing metrics.
|
||||
- **Class Mismatch**: In an unrelated dataset, the class names or IDs may be different to what your model produces, you'll need to remap them, which is [shown in this guide](#running-a-model).
|
||||
- **Data Contamination**: The `test` set may not be split correctly, with images from the test set also present in `training` or `validation` set and used during training. In this case, the results will be overly optimistic. This also applies when **very similar** images are used for training and testing - e.g. those taken in the same environment, same lighting conditions, similar angle, etc.
|
||||
- **Missing Test Set**: Some datasets do not come with a test set. In this case, you should collect and [label](https://roboflow.com/annotate) your own data. Alternatively, a validation set could be used, but the results could be overly optimistic. Make sure to test in the real world as soon as possible.
|
||||
|
||||
## Running a Model
|
||||
|
||||
At this stage, you should have:
|
||||
|
||||
- A dataset of labeled images to evaluate the model.
|
||||
- A model prepared for benchmarking.
|
||||
|
||||
With these ready, we can now run the model and obtain predictions.
|
||||
We'll use `supervision` to create a dataset iterator, and then run the model on each image.
|
||||
|
||||
=== "Inference"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
image_paths = []
|
||||
predictions_list = []
|
||||
targets_list = []
|
||||
|
||||
for image_path, image, label in test_set:
|
||||
result = model.infer(image)[0]
|
||||
predictions = sv.Detections.from_inference(result)
|
||||
|
||||
image_paths.append(image_path)
|
||||
predictions_list.append(predictions)
|
||||
targets_list.append(label)
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
image_paths = []
|
||||
predictions_list = []
|
||||
targets_list = []
|
||||
|
||||
for image_path, image, label in test_set:
|
||||
result = model(image)[0]
|
||||
predictions = sv.Detections.from_ultralytics(result)
|
||||
|
||||
image_paths.append(image_path)
|
||||
predictions_list.append(predictions)
|
||||
targets_list.append(label)
|
||||
```
|
||||
|
||||
## Remapping classes
|
||||
|
||||
Did you notice an issue in the above logic?
|
||||
Since we're using an unrelated dataset, the class names and IDs may be different from what the model was trained on.
|
||||
|
||||
We need to remap them to match the dataset classes. Here's how to do it:
|
||||
|
||||
```python
|
||||
def remap_classes(
|
||||
detections: sv.Detections,
|
||||
class_ids_from_to: dict[int, int],
|
||||
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]
|
||||
detections.class_id = np.array(new_class_ids)
|
||||
|
||||
new_class_names = [
|
||||
class_names_from_to.get(name, name) for name in detections["class_name"]]
|
||||
predictions["class_name"] = np.array(new_class_names)
|
||||
```
|
||||
|
||||
Let's also remove the predictions that are not in the dataset classes.
|
||||
|
||||
=== "Inference"
|
||||
|
||||
Dataset class names and IDs can be found in the `data.yaml` file, or by printing `dataset.classes`.
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
image_paths = []
|
||||
predictions_list = []
|
||||
targets_list = []
|
||||
|
||||
for image_path, image, label in test_set:
|
||||
result = model.infer(image)[0]
|
||||
predictions = sv.Detections.from_inference(result)
|
||||
|
||||
remap_classes(
|
||||
detections=predictions,
|
||||
class_ids_from_to={16: 0},
|
||||
class_names_from_to={"dog": "Corgi"}
|
||||
)
|
||||
predictions = predictions[
|
||||
np.isin(predictions["class_name"], test_set.classes)
|
||||
]
|
||||
|
||||
image_paths.append(image_path)
|
||||
predictions_list.append(predictions)
|
||||
targets_list.append(label)
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
Dataset class names and IDs can be found in the `data.yaml` file, or by printing `dataset.classes`.
|
||||
|
||||
Each model will have a different class mapping, so make sure to check the model's documentation. In this case, the model was trained on the COCO dataset, with a class
|
||||
configuration found [here](https://github.com/ultralytics/ultralytics/blob/main/ultralytics/cfg/datasets/coco8.yaml).
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
image_paths = []
|
||||
predictions_list = []
|
||||
targets_list = []
|
||||
|
||||
for image_path, image, label in test_set:
|
||||
result = model(image)[0]
|
||||
predictions = sv.Detections.from_ultralytics(result)
|
||||
|
||||
remap_classes(
|
||||
detections=predictions,
|
||||
class_ids_from_to={16: 0},
|
||||
class_names_from_to={"dog": "Corgi"}
|
||||
)
|
||||
predictions = predictions[
|
||||
np.isin(predictions["class_name"], test_set.classes)
|
||||
]
|
||||
|
||||
image_paths.append(image_path)
|
||||
predictions_list.append(predictions)
|
||||
targets_list.append(label)
|
||||
```
|
||||
|
||||
## Visualizing Predictions
|
||||
|
||||
The first step in evaluating your model’s performance is to visualize its predictions.
|
||||
This gives an intuitive sense of how well your model is detecting objects and where it might be failing.
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
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)
|
||||
|
||||
|
||||
annotated_images = []
|
||||
for image_path, predictions, targets in zip(
|
||||
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_images.append(annotated_image)
|
||||
|
||||
sv.plot_images_grid(images=annotated_images, grid_size=GRID_SIZE)
|
||||
```
|
||||
|
||||
Here, predictions in purple are targets (ground truth), and predictions in teal are model predictions.
|
||||
|
||||

|
||||
|
||||
!!! tip
|
||||
|
||||
Use `sv.BoxAnnotator` for object detection and `sv.OrientedBoxAnnotator` for OBB.
|
||||
|
||||
See [annotator documentation](https://supervision.roboflow.com/latest/detection/annotators/) for even more options.
|
||||
|
||||
## Benchmarking Metrics
|
||||
|
||||
With multiple models, fine details matter. Visual inspection may not be enough. `supervision` provides a collection of metrics that help obtain precise numerical results of model performance.
|
||||
|
||||
### Mean Average Precision (mAP)
|
||||
|
||||
We'll start with [MeanAveragePrecision (mAP)](https://supervision.roboflow.com/latest/metrics/mean_average_precision/#supervision.metrics.mean_average_precision.MeanAveragePrecision), which is the most commonly used metric for object detection. It measures the average precision across all classes and IoU thresholds.
|
||||
|
||||
For a thorough explanation, check out our [blog](https://blog.roboflow.com/mean-average-precision/) and [Youtube video](https://www.youtube.com/watch?v=oqXDdxF_Wuw).
|
||||
|
||||
Here, the most popular value is `mAP 50:95`. It represents the average precision across all classes and IoU thresholds (`0.5` to `0.95`), whereas other values such as `mAP 50` or `mAP 75` only consider a single IoU threshold (`0.5` and `0.75` respectively).
|
||||
|
||||
Let's compute the mAP:
|
||||
|
||||
```python
|
||||
from supervision.metrics import MeanAveragePrecision, MetricTarget
|
||||
|
||||
map_metric = MeanAveragePrecision(metric_target=MetricTarget.MASKS)
|
||||
map_result = map_metric.update(predictions_list, targets_list).compute()
|
||||
```
|
||||
|
||||
Try printing the result to see it at a glance:
|
||||
|
||||
```python
|
||||
print(map_result)
|
||||
```
|
||||
|
||||
```
|
||||
MeanAveragePrecisionResult:
|
||||
Metric target: MetricTarget.MASKS
|
||||
Class agnostic: False
|
||||
mAP @ 50:95: 0.2409
|
||||
mAP @ 50: 0.3591
|
||||
mAP @ 75: 0.2915
|
||||
mAP scores: [0.35909 0.3468 0.34556 ...]
|
||||
IoU thresh: [0.5 0.55 0.6 ...]
|
||||
AP per class:
|
||||
0: [0.35909 0.3468 0.34556 ...]
|
||||
...
|
||||
Small objects: ...
|
||||
Medium objects: ...
|
||||
Large objects: ...
|
||||
```
|
||||
|
||||
You can also plot the results:
|
||||
|
||||
```python
|
||||
map_result.plot()
|
||||
```
|
||||
|
||||

|
||||
|
||||
The metric also breaks down the results by detected object area. Small, medium and large are simply those with area less than 32², between 32² and 96², and greater than 96² pixels respectively.
|
||||
|
||||
### F1 Score
|
||||
|
||||
The [F1 Score](https://supervision.roboflow.com/latest/metrics/f1_score/) is another useful metric, especially when dealing with an imbalance between false positives and false negatives. It’s the harmonic mean of **precision** (how many predictions are correct) and **recall** (how many actual instances were detected).
|
||||
|
||||
Here's how you can compute the F1 score:
|
||||
|
||||
```python
|
||||
from supervision.metrics import F1Score, MetricTarget
|
||||
|
||||
f1_metric = F1Score(metric_target=MetricTarget.MASKS)
|
||||
f1_result = f1_metric.update(predictions_list, targets_list).compute()
|
||||
```
|
||||
|
||||
As with mAP, you can also print the result:
|
||||
|
||||
```python
|
||||
print(f1_result)
|
||||
```
|
||||
|
||||
```
|
||||
F1ScoreResult:
|
||||
Metric target: MetricTarget.MASKS
|
||||
Averaging method: AveragingMethod.WEIGHTED
|
||||
F1 @ 50: 0.5341
|
||||
F1 @ 75: 0.4636
|
||||
F1 @ thresh: [0.53406 0.5278 0.52153 ...]
|
||||
IoU thresh: [0.5 0.55 0.6 ...]
|
||||
F1 per class:
|
||||
0: [0.53406 0.5278 0.52153 ...]
|
||||
...
|
||||
Small objects: ...
|
||||
Medium objects: ...
|
||||
Large objects: ...
|
||||
```
|
||||
|
||||
Similarly, you can plot the results:
|
||||
|
||||
```python
|
||||
f1_result.plot()
|
||||
```
|
||||
|
||||

|
||||
|
||||
As with mAP, the metric also breaks down the results by detected object area. Small, medium and large are simply those with area less than 32², between 32² and 96², and greater than 96² pixels respectively.
|
||||
|
||||
## Model Leaderboard
|
||||
|
||||
Here to compare the basic models? We've got you covered. Check out our [Model Leaderboard](https://leaderboard.roboflow.com/) to see how different models perform and to get a sense of the state-of-the-art results. It's a great place to understand what the leading models can achieve and to compare your own results.
|
||||
|
||||
Even better, the repository is open source! You can see how the models were benchmarked, run the evaluation yourself, and even add your own models to the leaderboard. Check it out on [GitHub](https://github.com/roboflow/model-leaderboard)!
|
||||
|
||||

|
||||
|
||||
## Conclusion
|
||||
|
||||
In this guide, you've learned how to set up your environment, train or use pre-trained models, visualize predictions, and evaluate model performance with metrics like [mAP](https://supervision.roboflow.com/latest/metrics/mean_average_precision/), [F1 score](https://supervision.roboflow.com/latest/metrics/f1_score/), and got to know our Model Leaderboard.
|
||||
|
||||
A condensed version of this guide is also available as a [Colab Notebook](https://colab.research.google.com/drive/1HoOY9pZoVwGiRMmLHtir0qT6Uj45w6Ps?usp=sharing).
|
||||
|
||||
For more details, be sure to check out our [documentation](https://supervision.roboflow.com/latest/) and join our community discussions. If you find any issues, please let us know on [GitHub](https://github.com/roboflow/supervision/issues).
|
||||
|
||||
Best of luck with your benchmarking!
|
||||
@@ -6,7 +6,7 @@ comments: true
|
||||
|
||||
Supervision provides a seamless process for annotating predictions generated by various
|
||||
object detection and segmentation models. This guide shows how to perform inference
|
||||
with the [Inference](https://github.com/roboflow/inference),
|
||||
with the [Inference](https://github.com/roboflow/inference),
|
||||
[Ultralytics](https://github.com/ultralytics/ultralytics) or
|
||||
[Transformers](https://github.com/huggingface/transformers) packages. Following this,
|
||||
you'll learn how to import these predictions into Supervision and use them to annotate
|
||||
@@ -20,7 +20,6 @@ First, you'll need to obtain predictions from your object detection or segmentat
|
||||
model.
|
||||
|
||||
=== "Inference"
|
||||
|
||||
```python
|
||||
import cv2
|
||||
from inference import get_model
|
||||
@@ -31,7 +30,6 @@ model.
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```python
|
||||
import cv2
|
||||
from ultralytics import YOLO
|
||||
@@ -42,7 +40,6 @@ model.
|
||||
```
|
||||
|
||||
=== "Transformers"
|
||||
|
||||
```python
|
||||
import torch
|
||||
from PIL import Image
|
||||
@@ -68,8 +65,7 @@ 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`](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`](/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
|
||||
@@ -83,8 +79,7 @@ Now that we have predictions from a model, we can load them into Supervision.
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
We can do so using the [`sv.Detections.from_ultralytics`](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`](/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
|
||||
@@ -98,8 +93,7 @@ Now that we have predictions from a model, we can load them into Supervision.
|
||||
```
|
||||
|
||||
=== "Transformers"
|
||||
|
||||
We can do so using the [`sv.Detections.from_transformers`](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`](/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
|
||||
@@ -135,10 +129,9 @@ You can load predictions from other computer vision frameworks and libraries usi
|
||||
|
||||
## 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/annotators/#supervision.annotators.core.BoxAnnotator) and [`sv.LabelAnnotator`](/latest/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`](/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator) and [`sv.LabelAnnotator`](/latest/detection/annotators/#supervision.annotators.core.LabelAnnotator) classes.
|
||||
|
||||
=== "Inference"
|
||||
|
||||
```{ .py hl_lines="10-16" }
|
||||
import cv2
|
||||
import supervision as sv
|
||||
@@ -159,7 +152,6 @@ 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
|
||||
@@ -180,7 +172,6 @@ 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
|
||||
@@ -217,12 +208,11 @@ Finally, we can annotate the image with the predictions. Since we are working wi
|
||||
|
||||
## Display Custom Labels
|
||||
|
||||
By default, [`sv.LabelAnnotator`](/latest/annotators/#supervision.annotators.core.LabelAnnotator)
|
||||
By default, [`sv.LabelAnnotator`](/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
|
||||
@@ -249,7 +239,6 @@ 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
|
||||
@@ -276,7 +265,6 @@ 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
|
||||
@@ -320,13 +308,12 @@ 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/annotators/#supervision.annotators.core.MaskAnnotator)
|
||||
[`sv.MaskAnnotator`](/latest/detection/annotators/#supervision.annotators.core.MaskAnnotator)
|
||||
is a drop-in replacement for
|
||||
[`sv.BoxAnnotator`](/latest/annotators/#supervision.annotators.core.BoxAnnotator)
|
||||
[`sv.BoxAnnotator`](/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
|
||||
@@ -347,7 +334,6 @@ that will allow you to draw masks instead of boxes.
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```python
|
||||
import cv2
|
||||
import supervision as sv
|
||||
@@ -368,7 +354,6 @@ that will allow you to draw masks instead of boxes.
|
||||
```
|
||||
|
||||
=== "Transformers"
|
||||
|
||||
```python
|
||||
import torch
|
||||
import supervision as sv
|
||||
|
||||
@@ -20,7 +20,6 @@ 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
|
||||
@@ -41,7 +40,6 @@ size relative to the image resolution.
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```python
|
||||
import cv2
|
||||
import supervision as sv
|
||||
@@ -62,7 +60,6 @@ size relative to the image resolution.
|
||||
```
|
||||
|
||||
=== "Transformers"
|
||||
|
||||
```python
|
||||
import torch
|
||||
import supervision as sv
|
||||
@@ -108,7 +105,6 @@ 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
|
||||
@@ -129,7 +125,6 @@ is less effective for ultra-high-resolution images (4K and above).
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```{ .py hl_lines="7" }
|
||||
import cv2
|
||||
import supervision as sv
|
||||
@@ -162,7 +157,6 @@ objects within each, and aggregating the results.
|
||||
</video>
|
||||
|
||||
=== "Inference"
|
||||
|
||||
```{ .py hl_lines="9-14" }
|
||||
import cv2
|
||||
import numpy as np
|
||||
@@ -189,7 +183,6 @@ objects within each, and aggregating the results.
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```{ .py hl_lines="9-14" }
|
||||
import cv2
|
||||
import numpy as np
|
||||
@@ -216,7 +209,6 @@ objects within each, and aggregating the results.
|
||||
```
|
||||
|
||||
=== "Transformers"
|
||||
|
||||
```{ .py hl_lines="13-28" }
|
||||
import cv2
|
||||
import torch
|
||||
@@ -269,7 +261,6 @@ objects within each, and aggregating the results.
|
||||
[`InferenceSlicer`](/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
|
||||
@@ -296,7 +287,6 @@ objects within each, and aggregating the results.
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```{ .py hl_lines="6 16 19-20" }
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
@@ -15,7 +15,6 @@ the filters in their applications.
|
||||
Allows you to select detections that belong only to one selected class.
|
||||
|
||||
=== "After"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -30,7 +29,6 @@ Allows you to select detections that belong only to one selected class.
|
||||
</div>
|
||||
|
||||
=== "Before"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -49,7 +47,6 @@ 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
|
||||
@@ -66,7 +63,6 @@ Allows you to select detections that belong only to selected set of classes.
|
||||
</div>
|
||||
|
||||
=== "Before"
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
import supervision as sv
|
||||
@@ -87,7 +83,6 @@ 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
|
||||
|
||||
@@ -102,7 +97,6 @@ Allows you to select detections with specific confidence value, for example high
|
||||
</div>
|
||||
|
||||
=== "Before"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -122,7 +116,6 @@ 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
|
||||
|
||||
@@ -137,7 +130,6 @@ detection in the image. In the example below, we have sifted out the detections
|
||||
</div>
|
||||
|
||||
=== "Before"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -159,7 +151,6 @@ 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
|
||||
|
||||
@@ -178,7 +169,6 @@ occupied by them. In the example below, we remove too large detections.
|
||||
</div>
|
||||
|
||||
=== "Before"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -203,7 +193,6 @@ can be criteria for rejecting detection. Implementing such filtering requires a
|
||||
simple and fast.
|
||||
|
||||
=== "After"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -220,7 +209,6 @@ simple and fast.
|
||||
</div>
|
||||
|
||||
=== "Before"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -242,7 +230,6 @@ 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
|
||||
|
||||
@@ -259,7 +246,6 @@ zone. In the example below you can see how to filter out all detections located
|
||||
</div>
|
||||
|
||||
=== "Before"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -280,7 +266,6 @@ 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
|
||||
|
||||
@@ -297,7 +282,6 @@ zone. In the example below you can see how to filter out all detections located
|
||||
</div>
|
||||
|
||||
=== "Before"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
|
||||
@@ -0,0 +1,454 @@
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
With Supervision, you can load and manipulate classification, object detection, and
|
||||
segmentation datasets. This tutorial will walk you through how to load, split, merge,
|
||||
visualize, and augment datasets in Supervision.
|
||||
|
||||
## Download Dataset
|
||||
|
||||
In this tutorial, we will use a dataset from
|
||||
[Roboflow Universe](https://universe.roboflow.com/), a public repository of
|
||||
thousands of computer vision datasets. If you already have your dataset in
|
||||
[COCO](https://roboflow.com/formats/coco-json),
|
||||
[YOLO](https://roboflow.com/formats/yolov8-pytorch-txt),
|
||||
or [Pascal VOC](https://roboflow.com/formats/pascal-voc-xml) format, you can skip this
|
||||
section.
|
||||
|
||||
```bash
|
||||
pip install roboflow
|
||||
```
|
||||
|
||||
Next, log into your Roboflow account and download the dataset of your choice in the
|
||||
COCO, YOLO, or Pascal VOC format. You can customize the following code snippet with
|
||||
your workspace ID, project ID, and version number.
|
||||
|
||||
=== "COCO"
|
||||
|
||||
```python
|
||||
import roboflow
|
||||
|
||||
roboflow.login()
|
||||
|
||||
rf = roboflow.Roboflow()
|
||||
project = rf.workspace('<WORKSPACE_ID>').project('<PROJECT_ID>')
|
||||
dataset = project.version('<PROJECT_VERSION>').download("coco")
|
||||
```
|
||||
|
||||
=== "YOLO"
|
||||
|
||||
```python
|
||||
import roboflow
|
||||
|
||||
roboflow.login()
|
||||
|
||||
rf = roboflow.Roboflow()
|
||||
project = rf.workspace('<WORKSPACE_ID>').project('<PROJECT_ID>')
|
||||
dataset = project.version('<PROJECT_VERSION>').download("yolov8")
|
||||
```
|
||||
|
||||
=== "Pascal VOC"
|
||||
|
||||
```python
|
||||
import roboflow
|
||||
|
||||
roboflow.login()
|
||||
|
||||
rf = roboflow.Roboflow()
|
||||
project = rf.workspace('<WORKSPACE_ID>').project('<PROJECT_ID>')
|
||||
dataset = project.version('<PROJECT_VERSION>').download("voc")
|
||||
```
|
||||
|
||||
## Load Dataset
|
||||
|
||||
The Supervision library provides convenient functions to load datasets in various
|
||||
formats. If your dataset is already split into train, test, and valid subsets, you can
|
||||
load each of those as separate [`sv.DetectionDataset`](https://supervision.roboflow.com/latest/datasets/core/#supervision.dataset.core.DetectionDataset)
|
||||
instances.
|
||||
|
||||
=== "COCO"
|
||||
|
||||
We can do so using the [`sv.DetectionDataset.from_coco`](https://supervision.roboflow.com/latest/datasets/core/#supervision.dataset.core.DetectionDataset.from_coco) to load annotations in [COCO](https://roboflow.com/formats/coco-json) format.
|
||||
|
||||
```python
|
||||
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',
|
||||
)
|
||||
ds_valid = sv.DetectionDataset.from_coco(
|
||||
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',
|
||||
)
|
||||
|
||||
ds_train.classes
|
||||
# ['person', 'bicycle', 'car', ...]
|
||||
|
||||
len(ds_train), len(ds_valid), len(ds_test)
|
||||
# 800, 100, 100
|
||||
```
|
||||
|
||||
=== "YOLO"
|
||||
|
||||
We can do so using the [`sv.DetectionDataset.from_yolo`](https://supervision.roboflow.com/latest/datasets/core/#supervision.dataset.core.DetectionDataset.from_yolo) to load annotations in [YOLO](https://roboflow.com/formats/yolov8-pytorch-txt) format.
|
||||
|
||||
```python
|
||||
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'
|
||||
)
|
||||
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'
|
||||
)
|
||||
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'
|
||||
)
|
||||
|
||||
ds_train.classes
|
||||
# ['person', 'bicycle', 'car', ...]
|
||||
|
||||
len(ds_train), len(ds_valid), len(ds_test)
|
||||
# 800, 100, 100
|
||||
```
|
||||
|
||||
=== "Pascal VOC"
|
||||
|
||||
We can do so using the [`sv.DetectionDataset.from_pascal_voc`](https://supervision.roboflow.com/latest/datasets/core/#supervision.dataset.core.DetectionDataset.from_pascal_voc) to load annotations in [Pascal VOC](https://roboflow.com/formats/pascal-voc-xml) format.
|
||||
|
||||
```python
|
||||
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'
|
||||
)
|
||||
ds_valid = sv.DetectionDataset.from_pascal_voc(
|
||||
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'
|
||||
)
|
||||
|
||||
ds_train.classes
|
||||
# ['person', 'bicycle', 'car', ...]
|
||||
|
||||
len(ds_train), len(ds_valid), len(ds_test)
|
||||
# 800, 100, 100
|
||||
```
|
||||
|
||||
## Split Dataset
|
||||
|
||||
If your dataset is not already split into train, test, and valid subsets, you can
|
||||
easily do so using the [`sv.DetectionDataset.split`](https://supervision.roboflow.com/latest/datasets/core/#supervision.dataset.core.DetectionDataset.split)
|
||||
method. We can split it as follows, ensuring a random shuffle of the data.
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
ds = sv.DetectionDataset(...)
|
||||
|
||||
len(ds)
|
||||
# 1000
|
||||
|
||||
ds_train, ds = ds.split(split_ratio=0.8, shuffle=True)
|
||||
ds_valid, ds_test = ds.split(split_ratio=0.5, shuffle=True)
|
||||
|
||||
len(ds_train), len(ds_valid), len(ds_test)
|
||||
# 800, 100, 100
|
||||
```
|
||||
|
||||
## Merge Dataset
|
||||
|
||||
If you have multiple datasets that you would like to merge, you can do so using the
|
||||
[`sv.DetectionDataset.merge`](https://supervision.roboflow.com/latest/datasets/core/#supervision.dataset.core.DetectionDataset.merge)
|
||||
method.
|
||||
|
||||
=== "COCO"
|
||||
|
||||
```{ .py hl_lines="22-28" }
|
||||
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',
|
||||
)
|
||||
ds_valid = sv.DetectionDataset.from_coco(
|
||||
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',
|
||||
)
|
||||
|
||||
ds_train.classes
|
||||
# ['person', 'bicycle', 'car', ...]
|
||||
|
||||
len(ds_train), len(ds_valid), len(ds_test)
|
||||
# 800, 100, 100
|
||||
|
||||
ds = sv.DetectionDataset.merge([ds_train, ds_valid, ds_test])
|
||||
|
||||
ds.classes
|
||||
# ['person', 'bicycle', 'car', ...]
|
||||
|
||||
len(ds)
|
||||
# 1000
|
||||
```
|
||||
|
||||
=== "YOLO"
|
||||
|
||||
```{ .py hl_lines="25-31" }
|
||||
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'
|
||||
)
|
||||
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'
|
||||
)
|
||||
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'
|
||||
)
|
||||
|
||||
ds_train.classes
|
||||
# ['person', 'bicycle', 'car', ...]
|
||||
|
||||
len(ds_train), len(ds_valid), len(ds_test)
|
||||
# 800, 100, 100
|
||||
|
||||
ds = sv.DetectionDataset.merge([ds_train, ds_valid, ds_test])
|
||||
|
||||
ds.classes
|
||||
# ['person', 'bicycle', 'car', ...]
|
||||
|
||||
len(ds)
|
||||
# 1000
|
||||
```
|
||||
|
||||
=== "Pascal VOC"
|
||||
|
||||
```{ .py hl_lines="22-28" }
|
||||
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'
|
||||
)
|
||||
ds_valid = sv.DetectionDataset.from_pascal_voc(
|
||||
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'
|
||||
)
|
||||
|
||||
ds_train.classes
|
||||
# ['person', 'bicycle', 'car', ...]
|
||||
|
||||
len(ds_train), len(ds_valid), len(ds_test)
|
||||
# 800, 100, 100
|
||||
|
||||
ds = sv.DetectionDataset.merge([ds_train, ds_valid, ds_test])
|
||||
|
||||
ds.classes
|
||||
# ['person', 'bicycle', 'car', ...]
|
||||
|
||||
len(ds)
|
||||
# 1000
|
||||
```
|
||||
|
||||
## Iterate over Dataset
|
||||
|
||||
There are two ways to loop over a `sv.DetectionDataset`: using a direct
|
||||
[for loop](https://supervision.roboflow.com/latest/datasets/core/#supervision.dataset.core.DetectionDataset.__iter__)
|
||||
called on the `sv.DetectionDataset` instance or loading `sv.DetectionDataset` entries
|
||||
[by index](https://supervision.roboflow.com/latest/datasets/core/#supervision.dataset.core.DetectionDataset.__getitem__).
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
ds = sv.DetectionDataset(...)
|
||||
|
||||
# Option 1
|
||||
for image_path, image, annotations in ds:
|
||||
... # 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`
|
||||
```
|
||||
|
||||
## Visualize Dataset
|
||||
|
||||
The Supervision library provides tools for easily visualizing your detection dataset.
|
||||
You can create a grid of annotated images to quickly inspect your data and labels.
|
||||
First, initialize 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).
|
||||
Then, iterate through a subset of the dataset (e.g., the first 25 images), drawing
|
||||
bounding boxes and class labels on each image. Finally, combine the annotated images
|
||||
into a grid for display.
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
ds = sv.DetectionDataset(...)
|
||||
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
label_annotator = sv.LabelAnnotator()
|
||||
|
||||
annotated_images = []
|
||||
for i in range(16):
|
||||
_, image, annotations = ds[i]
|
||||
|
||||
labels = [ds.classes[class_id] for class_id in annotations.class_id]
|
||||
|
||||
annotated_image = image.copy()
|
||||
annotated_image = box_annotator.annotate(annotated_image, annotations)
|
||||
annotated_image = label_annotator.annotate(annotated_image, annotations, labels)
|
||||
annotated_images.append(annotated_image)
|
||||
|
||||
grid = sv.create_tiles(
|
||||
annotated_images,
|
||||
grid_size=(4, 4),
|
||||
single_tile_size=(400, 400),
|
||||
tile_padding_color=sv.Color.WHITE,
|
||||
tile_margin_color=sv.Color.WHITE
|
||||
)
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Save Dataset
|
||||
|
||||
=== "COCO"
|
||||
|
||||
We can do so using the [`sv.DetectionDataset.as_coco`](https://supervision.roboflow.com/datasets/#supervision.dataset.core.DetectionDataset.as_coco) method to save annotations in [COCO](https://roboflow.com/formats/coco-json) format.
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
ds = sv.DetectionDataset(...)
|
||||
|
||||
ds.as_coco(
|
||||
images_directory_path='<IMAGE_DIRECTORY_PATH>',
|
||||
annotations_path='<ANNOTATIONS_PATH>'
|
||||
)
|
||||
```
|
||||
|
||||
=== "YOLO"
|
||||
|
||||
We can do so using the [`sv.DetectionDataset.as_yolo`](https://supervision.roboflow.com/datasets/#supervision.dataset.core.DetectionDataset.as_yolo) method to save annotations in [YOLO](https://roboflow.com/formats/yolov8-pytorch-txt) format.
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
ds = sv.DetectionDataset(...)
|
||||
|
||||
ds.as_yolo(
|
||||
images_directory_path='<IMAGE_DIRECTORY_PATH>',
|
||||
annotations_directory_path='<ANNOTATIONS_DIRECTORY_PATH>',
|
||||
data_yaml_path='<DATA_YAML_PATH>'
|
||||
)
|
||||
```
|
||||
|
||||
=== "Pascal VOC"
|
||||
|
||||
We can do so using the [`sv.DetectionDataset.as_pascal_voc`](https://supervision.roboflow.com/datasets/#supervision.dataset.core.DetectionDataset.as_pascal_voc) method to save annotations in [Pascal VOC](https://roboflow.com/formats/pascal-voc-xml) format.
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
ds = sv.DetectionDataset(...)
|
||||
|
||||
ds.as_pascal_voc(
|
||||
images_directory_path='<IMAGE_DIRECTORY_PATH>',
|
||||
annotations_directory_path='<ANNOTATIONS_DIRECTORY_PATH>'
|
||||
)
|
||||
```
|
||||
|
||||
## Augment Dataset
|
||||
|
||||
In this section, we'll explore using Supervision in combination with Albumentations to
|
||||
augment our dataset. Data augmentation is a common technique in computer vision to
|
||||
increase the size and diversity of training datasets, leading to improved model
|
||||
performance and generalization.
|
||||
|
||||
```bash
|
||||
pip install augmentation
|
||||
```
|
||||
|
||||
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)
|
||||
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).
|
||||
|
||||
```python
|
||||
import albumentations as A
|
||||
|
||||
augmentation = A.Compose(
|
||||
transforms=[
|
||||
A.Perspective(p=0.1),
|
||||
A.HorizontalFlip(p=0.5),
|
||||
A.RandomBrightnessContrast(p=0.5)
|
||||
],
|
||||
bbox_params=A.BboxParams(
|
||||
format='pascal_voc',
|
||||
label_fields=['category']
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
The key is to set `format='pascal_voc'`, which corresponds to the
|
||||
`[x_min, y_min, x_max, y_max]` bounding box format used in Supervision.
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
import supervision as sv
|
||||
from dataclasses import replace
|
||||
|
||||
ds = sv.DetectionDataset(...)
|
||||
|
||||
_, original_image, original_annotations = ds[0]
|
||||
|
||||
output = augmentation(
|
||||
image=original_image,
|
||||
bboxes=original_annotations.xyxy,
|
||||
category=original_annotations.class_id
|
||||
)
|
||||
|
||||
augmented_image = output['image']
|
||||
augmented_annotations = replace(
|
||||
original_annotations,
|
||||
xyxy=np.array(output['bboxes']),
|
||||
class_id=np.array(output['category'])
|
||||
)
|
||||
```
|
||||
|
||||

|
||||
@@ -19,7 +19,6 @@ model. You can learn more on this topic in our
|
||||
[How to Detect and Annotate](/latest/how_to/detect_and_annotate.md) guide.
|
||||
|
||||
=== "Inference"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
from inference import get_model
|
||||
@@ -34,7 +33,6 @@ model. You can learn more on this topic in our
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
@@ -49,7 +47,6 @@ model. You can learn more on this topic in our
|
||||
```
|
||||
|
||||
=== "Transformers"
|
||||
|
||||
```python
|
||||
import torch
|
||||
import supervision as sv
|
||||
@@ -83,7 +80,6 @@ and then pass the
|
||||
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
|
||||
@@ -100,7 +96,6 @@ 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
|
||||
@@ -117,7 +112,6 @@ 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
|
||||
@@ -144,11 +138,11 @@ object resulting from the inference to it. Its fields are parsed and saved on di
|
||||
sink.append(detections, {})
|
||||
```
|
||||
|
||||
| x_min | y_min | x_max | y_max | class_id | confidence | tracker_id | class_name |
|
||||
|---------|----------|---------|----------|----------|------------|------------|------------|
|
||||
| 2941.14 | 1269.31 | 3220.77 | 1500.67 | 2 | 0.8517 | | car |
|
||||
| 944.889 | 899.641 | 1235.42 | 1308.80 | 7 | 0.6752 | | truck |
|
||||
| 1439.78 | 1077.79 | 1621.27 | 1231.40 | 2 | 0.6450 | | car |
|
||||
| x_min | y_min | x_max | y_max | class_id | confidence | tracker_id | class_name |
|
||||
| ------- | ------- | ------- | ------- | -------- | ---------- | ---------- | ---------- |
|
||||
| 2941.14 | 1269.31 | 3220.77 | 1500.67 | 2 | 0.8517 | | car |
|
||||
| 944.889 | 899.641 | 1235.42 | 1308.80 | 7 | 0.6752 | | truck |
|
||||
| 1439.78 | 1077.79 | 1621.27 | 1231.40 | 2 | 0.6450 | | car |
|
||||
|
||||
## Custom Fields
|
||||
|
||||
@@ -160,7 +154,6 @@ also allows you to add custom information to each row, which can be passed via t
|
||||
frame index from which the detections originate.
|
||||
|
||||
=== "Inference"
|
||||
|
||||
```{ .py hl_lines="8 12" }
|
||||
import supervision as sv
|
||||
from inference import get_model
|
||||
@@ -177,7 +170,6 @@ frame index from which the detections originate.
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```{ .py hl_lines="8 12" }
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
@@ -194,7 +186,6 @@ frame index from which the detections originate.
|
||||
```
|
||||
|
||||
=== "Transformers"
|
||||
|
||||
```{ .py hl_lines="10 23" }
|
||||
import torch
|
||||
import supervision as sv
|
||||
@@ -221,11 +212,11 @@ frame index from which the detections originate.
|
||||
sink.append(detections, {"frame_index": frame_index})
|
||||
```
|
||||
|
||||
| x_min | y_min | x_max | y_max | class_id | confidence | tracker_id | class_name | frame_index |
|
||||
|---------|----------|---------|----------|----------|------------|------------|------------|-------------|
|
||||
| 2941.14 | 1269.31 | 3220.77 | 1500.67 | 2 | 0.8517 | | car | 0 |
|
||||
| 944.889 | 899.641 | 1235.42 | 1308.80 | 7 | 0.6752 | | truck | 0 |
|
||||
| 1439.78 | 1077.79 | 1621.27 | 1231.40 | 2 | 0.6450 | | car | 0 |
|
||||
| x_min | y_min | x_max | y_max | class_id | confidence | tracker_id | class_name | frame_index |
|
||||
| ------- | ------- | ------- | ------- | -------- | ---------- | ---------- | ---------- | ----------- |
|
||||
| 2941.14 | 1269.31 | 3220.77 | 1500.67 | 2 | 0.8517 | | car | 0 |
|
||||
| 944.889 | 899.641 | 1235.42 | 1308.80 | 7 | 0.6752 | | truck | 0 |
|
||||
| 1439.78 | 1077.79 | 1621.27 | 1231.40 | 2 | 0.6450 | | car | 0 |
|
||||
|
||||
## Save Detections as JSON
|
||||
|
||||
@@ -236,7 +227,6 @@ with
|
||||
[`sv.JSONSink`](/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
|
||||
@@ -253,7 +243,6 @@ with
|
||||
```
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```{ .py hl_lines="7" }
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
@@ -270,7 +259,6 @@ with
|
||||
```
|
||||
|
||||
=== "Transformers"
|
||||
|
||||
```{ .py hl_lines="9" }
|
||||
import torch
|
||||
import supervision as sv
|
||||
|
||||
+356
-19
@@ -1,18 +1,21 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Track Objects
|
||||
|
||||
Leverage Supervision's advanced capabilities for enhancing your video analysis by
|
||||
seamlessly [tracking](/latest/trackers/) objects recognized by
|
||||
a multitude of object detection and segmentation models. This comprehensive guide will
|
||||
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
|
||||
[Ultralytics](https://github.com/ultralytics/ultralytics) packages. Following this,
|
||||
you'll discover how to track these objects efficiently and annotate your video content
|
||||
for a deeper analysis.
|
||||
|
||||
## Object Detection & Segmentation
|
||||
|
||||
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.
|
||||
@@ -27,12 +30,12 @@ download_assets(VideoAssets.PEOPLE_WALKING)
|
||||
<source src="https://media.roboflow.com/supervision/video-examples/people-walking.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
## Run Inference
|
||||
### Run Inference
|
||||
|
||||
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](/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
|
||||
@@ -40,6 +43,10 @@ by obtaining model predictions and then annotating the frame based on these pred
|
||||
This `callback` function will be essential in the subsequent steps of the tutorial, as
|
||||
it will be modified to include tracking, labeling, and trace annotations.
|
||||
|
||||
!!! tip
|
||||
|
||||
Both object detection and segmentation models are supported. Try it with `yolov8n.pt` or `yolov8n-640-seg`!
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```{ .py }
|
||||
@@ -48,7 +55,7 @@ it will be modified to include tracking, labeling, and trace annotations.
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8n.pt")
|
||||
box_annotator = sv.BoundingBoxAnnotator()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
|
||||
def callback(frame: np.ndarray, _: int) -> np.ndarray:
|
||||
results = model(frame)[0]
|
||||
@@ -70,7 +77,7 @@ it will be modified to include tracking, labeling, and trace annotations.
|
||||
from inference.models.utils import get_roboflow_model
|
||||
|
||||
model = get_roboflow_model(model_id="yolov8n-640", api_key=<ROBOFLOW API KEY>)
|
||||
box_annotator = sv.BoundingBoxAnnotator()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
|
||||
def callback(frame: np.ndarray, _: int) -> np.ndarray:
|
||||
results = model.infer(frame)[0]
|
||||
@@ -88,7 +95,7 @@ it will be modified to include tracking, labeling, and trace annotations.
|
||||
<source src="https://media.roboflow.com/supervision/video-examples/how-to/track-objects/run-inference.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
## Tracking
|
||||
### Tracking
|
||||
|
||||
After running inference and obtaining predictions, the next step is to track the
|
||||
detected objects throughout the video. Utilizing Supervision’s
|
||||
@@ -105,7 +112,7 @@ enabling the continuous following of the object's motion path across different f
|
||||
|
||||
model = YOLO("yolov8n.pt")
|
||||
tracker = sv.ByteTrack()
|
||||
box_annotator = sv.BoundingBoxAnnotator()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
|
||||
def callback(frame: np.ndarray, _: int) -> np.ndarray:
|
||||
results = model(frame)[0]
|
||||
@@ -129,7 +136,7 @@ enabling the continuous following of the object's motion path across different f
|
||||
|
||||
model = get_roboflow_model(model_id="yolov8n-640", api_key=<ROBOFLOW API KEY>)
|
||||
tracker = sv.ByteTrack()
|
||||
box_annotator = sv.BoundingBoxAnnotator()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
|
||||
def callback(frame: np.ndarray, _: int) -> np.ndarray:
|
||||
results = model.infer(frame)[0]
|
||||
@@ -144,11 +151,11 @@ enabling the continuous following of the object's motion path across different f
|
||||
)
|
||||
```
|
||||
|
||||
## Annotate Video with Tracking IDs
|
||||
### Annotate Video with Tracking IDs
|
||||
|
||||
Annotating the video with tracking IDs helps in distinguishing and following each object
|
||||
distinctly. With the
|
||||
[`sv.LabelAnnotator`](/latest/annotators.md/#supervision.annotators.core.LabelAnnotator)
|
||||
[`sv.LabelAnnotator`](/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.
|
||||
|
||||
@@ -161,7 +168,7 @@ offering a clear visual representation of each object's class and unique identif
|
||||
|
||||
model = YOLO("yolov8n.pt")
|
||||
tracker = sv.ByteTrack()
|
||||
box_annotator = sv.BoundingBoxAnnotator()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
label_annotator = sv.LabelAnnotator()
|
||||
|
||||
def callback(frame: np.ndarray, _: int) -> np.ndarray:
|
||||
@@ -196,7 +203,7 @@ offering a clear visual representation of each object's class and unique identif
|
||||
|
||||
model = get_roboflow_model(model_id="yolov8n-640", api_key=<ROBOFLOW API KEY>)
|
||||
tracker = sv.ByteTrack()
|
||||
box_annotator = sv.BoundingBoxAnnotator()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
label_annotator = sv.LabelAnnotator()
|
||||
|
||||
def callback(frame: np.ndarray, _: int) -> np.ndarray:
|
||||
@@ -226,11 +233,11 @@ offering a clear visual representation of each object's class and unique identif
|
||||
<source src="https://media.roboflow.com/supervision/video-examples/how-to/track-objects/annotate-video-with-tracking-ids.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
## Annotate Video with Traces
|
||||
### Annotate Video with Traces
|
||||
|
||||
Adding traces to the video involves overlaying the historical paths of the detected
|
||||
objects. This feature, powered by the
|
||||
[`sv.TraceAnnotator`](/latest/annotators/#supervision.annotators.core.TraceAnnotator),
|
||||
[`sv.TraceAnnotator`](/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.
|
||||
|
||||
@@ -243,7 +250,7 @@ movement patterns and interactions between objects in the video.
|
||||
|
||||
model = YOLO("yolov8n.pt")
|
||||
tracker = sv.ByteTrack()
|
||||
box_annotator = sv.BoundingBoxAnnotator()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
label_annotator = sv.LabelAnnotator()
|
||||
trace_annotator = sv.TraceAnnotator()
|
||||
|
||||
@@ -281,7 +288,7 @@ movement patterns and interactions between objects in the video.
|
||||
|
||||
model = get_roboflow_model(model_id="yolov8n-640", api_key=<ROBOFLOW API KEY>)
|
||||
tracker = sv.ByteTrack()
|
||||
box_annotator = sv.BoundingBoxAnnotator()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
label_annotator = sv.LabelAnnotator()
|
||||
trace_annotator = sv.TraceAnnotator()
|
||||
|
||||
@@ -314,6 +321,336 @@ movement patterns and interactions between objects in the video.
|
||||
<source src="https://media.roboflow.com/supervision/video-examples/how-to/track-objects/annotate-video-with-traces.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
This structured walkthrough should give a detailed pathway to annotate videos
|
||||
effectively using Supervision’s various functionalities, including object tracking and
|
||||
trace annotations.
|
||||
## Keypoints
|
||||
|
||||
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.
|
||||
|
||||
```python
|
||||
from supervision.assets import download_assets, VideoAssets
|
||||
|
||||
download_assets(VideoAssets.SKIING)
|
||||
```
|
||||
|
||||
<video controls muted>
|
||||
<source src="https://media.roboflow.com/supervision/video-examples/how-to/track-objects/skiing-hd.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
### 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.
|
||||
|
||||
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).
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```{ .py hl_lines="5 10-11" }
|
||||
import numpy as np
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8m-pose.pt")
|
||||
edge_annotator = sv.EdgeAnnotator()
|
||||
vertex_annotator = sv.VertexAnnotator()
|
||||
|
||||
def callback(frame: np.ndarray, _: int) -> np.ndarray:
|
||||
results = model(frame)[0]
|
||||
key_points = sv.KeyPoints.from_ultralytics(results)
|
||||
|
||||
annotated_frame = edge_annotator.annotate(
|
||||
frame.copy(), key_points=key_points)
|
||||
return vertex_annotator.annotate(
|
||||
annotated_frame, key_points=key_points)
|
||||
|
||||
sv.process_video(
|
||||
source_path="skiing.mp4",
|
||||
target_path="result.mp4",
|
||||
callback=callback
|
||||
)
|
||||
```
|
||||
|
||||
=== "Inference"
|
||||
|
||||
```{ .py hl_lines="5-6 11-12" }
|
||||
import numpy as np
|
||||
import supervision as sv
|
||||
from inference.models.utils import get_roboflow_model
|
||||
|
||||
model = get_roboflow_model(
|
||||
model_id="yolov8m-pose-640", api_key=<ROBOFLOW API KEY>)
|
||||
edge_annotator = sv.EdgeAnnotator()
|
||||
vertex_annotator = sv.VertexAnnotator()
|
||||
|
||||
def callback(frame: np.ndarray, _: int) -> np.ndarray:
|
||||
results = model.infer(frame)[0]
|
||||
key_points = sv.KeyPoints.from_inference(results)
|
||||
|
||||
annotated_frame = edge_annotator.annotate(
|
||||
frame.copy(), key_points=key_points)
|
||||
return vertex_annotator.annotate(
|
||||
annotated_frame, key_points=key_points)
|
||||
|
||||
sv.process_video(
|
||||
source_path="skiing.mp4",
|
||||
target_path="result.mp4",
|
||||
callback=callback
|
||||
)
|
||||
```
|
||||
|
||||
<video controls>
|
||||
<source src="https://media.roboflow.com/supervision/video-examples/how-to/track-objects/track-keypoints-only-keypoints.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
### 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.
|
||||
|
||||
Let's convert to detections and visualize the results with our [`BoxAnnotator`](/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator).
|
||||
|
||||
!!! tip
|
||||
|
||||
You may use the `selected_keypoint_indices` argument to specify a subset of keypoints to convert. This is useful when some keypoints could be occluded. For example: a person might swing their arm, causing the elbow to be occluded by the torso sometimes.
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```{ .py hl_lines="8 13 19-20" }
|
||||
import numpy as np
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8m-pose.pt")
|
||||
edge_annotator = sv.EdgeAnnotator()
|
||||
vertex_annotator = sv.VertexAnnotator()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
|
||||
def callback(frame: np.ndarray, _: int) -> np.ndarray:
|
||||
results = model(frame)[0]
|
||||
key_points = sv.KeyPoints.from_ultralytics(results)
|
||||
detections = key_points.as_detections()
|
||||
|
||||
annotated_frame = edge_annotator.annotate(
|
||||
frame.copy(), key_points=key_points)
|
||||
annotated_frame = vertex_annotator.annotate(
|
||||
annotated_frame, key_points=key_points)
|
||||
return box_annotator.annotate(
|
||||
annotated_frame, detections=detections)
|
||||
|
||||
sv.process_video(
|
||||
source_path="skiing.mp4",
|
||||
target_path="result.mp4",
|
||||
callback=callback
|
||||
)
|
||||
```
|
||||
|
||||
=== "Inference"
|
||||
|
||||
```{ .py hl_lines="9 14 20-21" }
|
||||
import numpy as np
|
||||
import supervision as sv
|
||||
from inference.models.utils import get_roboflow_model
|
||||
|
||||
model = get_roboflow_model(
|
||||
model_id="yolov8m-pose-640", api_key=<ROBOFLOW API KEY>)
|
||||
edge_annotator = sv.EdgeAnnotator()
|
||||
vertex_annotator = sv.VertexAnnotator()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
|
||||
def callback(frame: np.ndarray, _: int) -> np.ndarray:
|
||||
results = model.infer(frame)[0]
|
||||
key_points = sv.KeyPoints.from_inference(results)
|
||||
detections = key_points.as_detections()
|
||||
|
||||
annotated_frame = edge_annotator.annotate(
|
||||
frame.copy(), key_points=key_points)
|
||||
annotated_frame = vertex_annotator.annotate(
|
||||
annotated_frame, key_points=key_points)
|
||||
return box_annotator.annotate(
|
||||
annotated_frame, detections=detections)
|
||||
|
||||
sv.process_video(
|
||||
source_path="skiing.mp4",
|
||||
target_path="result.mp4",
|
||||
callback=callback
|
||||
)
|
||||
```
|
||||
|
||||
<video controls>
|
||||
<source src="https://media.roboflow.com/supervision/video-examples/how-to/track-objects/track-keypoints-converted-to-detections.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
### 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`.
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```{ .py hl_lines="10-11 17 25-26" }
|
||||
import numpy as np
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8m-pose.pt")
|
||||
edge_annotator = sv.EdgeAnnotator()
|
||||
vertex_annotator = sv.VertexAnnotator()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
|
||||
tracker = sv.ByteTrack()
|
||||
trace_annotator = sv.TraceAnnotator()
|
||||
|
||||
def callback(frame: np.ndarray, _: int) -> np.ndarray:
|
||||
results = model(frame)[0]
|
||||
key_points = sv.KeyPoints.from_ultralytics(results)
|
||||
detections = key_points.as_detections()
|
||||
detections = tracker.update_with_detections(detections)
|
||||
|
||||
annotated_frame = edge_annotator.annotate(
|
||||
frame.copy(), key_points=key_points)
|
||||
annotated_frame = vertex_annotator.annotate(
|
||||
annotated_frame, key_points=key_points)
|
||||
annotated_frame = box_annotator.annotate(
|
||||
annotated_frame, detections=detections)
|
||||
return trace_annotator.annotate(
|
||||
annotated_frame, detections=detections)
|
||||
|
||||
sv.process_video(
|
||||
source_path="skiing.mp4",
|
||||
target_path="result.mp4",
|
||||
callback=callback
|
||||
)
|
||||
```
|
||||
|
||||
=== "Inference"
|
||||
|
||||
```{ .py hl_lines="11-12 18 26-27" }
|
||||
import numpy as np
|
||||
import supervision as sv
|
||||
from inference.models.utils import get_roboflow_model
|
||||
|
||||
model = get_roboflow_model(
|
||||
model_id="yolov8m-pose-640", api_key=<ROBOFLOW API KEY>)
|
||||
edge_annotator = sv.EdgeAnnotator()
|
||||
vertex_annotator = sv.VertexAnnotator()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
|
||||
tracker = sv.ByteTrack()
|
||||
trace_annotator = sv.TraceAnnotator()
|
||||
|
||||
def callback(frame: np.ndarray, _: int) -> np.ndarray:
|
||||
results = model.infer(frame)[0]
|
||||
key_points = sv.KeyPoints.from_inference(results)
|
||||
detections = key_points.as_detections()
|
||||
detections = tracker.update_with_detections(detections)
|
||||
|
||||
annotated_frame = edge_annotator.annotate(
|
||||
frame.copy(), key_points=key_points)
|
||||
annotated_frame = vertex_annotator.annotate(
|
||||
annotated_frame, key_points=key_points)
|
||||
annotated_frame = box_annotator.annotate(
|
||||
annotated_frame, detections=detections)
|
||||
return trace_annotator.annotate(
|
||||
annotated_frame, detections=detections)
|
||||
|
||||
sv.process_video(
|
||||
source_path="skiing.mp4",
|
||||
target_path="result.mp4",
|
||||
callback=callback
|
||||
)
|
||||
```
|
||||
|
||||
<video controls>
|
||||
<source src="https://media.roboflow.com/supervision/video-examples/how-to/track-objects/track-keypoints-with-tracking.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
### 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:
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
```{ .py hl_lines="11 19" }
|
||||
import numpy as np
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8m-pose.pt")
|
||||
edge_annotator = sv.EdgeAnnotator()
|
||||
vertex_annotator = sv.VertexAnnotator()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
|
||||
tracker = sv.ByteTrack()
|
||||
smoother = sv.DetectionsSmoother()
|
||||
trace_annotator = sv.TraceAnnotator()
|
||||
|
||||
def callback(frame: np.ndarray, _: int) -> np.ndarray:
|
||||
results = model(frame)[0]
|
||||
key_points = sv.KeyPoints.from_ultralytics(results)
|
||||
detections = key_points.as_detections()
|
||||
detections = tracker.update_with_detections(detections)
|
||||
detections = smoother.update_with_detections(detections)
|
||||
|
||||
annotated_frame = edge_annotator.annotate(
|
||||
frame.copy(), key_points=key_points)
|
||||
annotated_frame = vertex_annotator.annotate(
|
||||
annotated_frame, key_points=key_points)
|
||||
annotated_frame = box_annotator.annotate(
|
||||
annotated_frame, detections=detections)
|
||||
return trace_annotator.annotate(
|
||||
annotated_frame, detections=detections)
|
||||
|
||||
sv.process_video(
|
||||
source_path="skiing.mp4",
|
||||
target_path="result.mp4",
|
||||
callback=callback
|
||||
)
|
||||
```
|
||||
|
||||
=== "Inference"
|
||||
|
||||
```{ .py hl_lines="12 20" }
|
||||
import numpy as np
|
||||
import supervision as sv
|
||||
from inference.models.utils import get_roboflow_model
|
||||
|
||||
model = get_roboflow_model(
|
||||
model_id="yolov8m-pose-640", api_key=<ROBOFLOW API KEY>)
|
||||
edge_annotator = sv.EdgeAnnotator()
|
||||
vertex_annotator = sv.VertexAnnotator()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
|
||||
tracker = sv.ByteTrack()
|
||||
smoother = sv.DetectionsSmoother()
|
||||
trace_annotator = sv.TraceAnnotator()
|
||||
|
||||
def callback(frame: np.ndarray, _: int) -> np.ndarray:
|
||||
results = model.infer(frame)[0]
|
||||
key_points = sv.KeyPoints.from_inference(results)
|
||||
detections = key_points.as_detections()
|
||||
detections = tracker.update_with_detections(detections)
|
||||
detections = smoother.update_with_detections(detections)
|
||||
|
||||
annotated_frame = edge_annotator.annotate(
|
||||
frame.copy(), key_points=key_points)
|
||||
annotated_frame = vertex_annotator.annotate(
|
||||
annotated_frame, key_points=key_points)
|
||||
annotated_frame = box_annotator.annotate(
|
||||
annotated_frame, detections=detections)
|
||||
return trace_annotator.annotate(
|
||||
annotated_frame, detections=detections)
|
||||
|
||||
sv.process_video(
|
||||
source_path="skiing.mp4",
|
||||
target_path="result.mp4",
|
||||
callback=callback
|
||||
)
|
||||
```
|
||||
|
||||
<video controls>
|
||||
<source src="https://media.roboflow.com/supervision/video-examples/how-to/track-objects/track-keypoints-with-smoothing.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
This structured walkthrough should give a detailed pathway to annotate videos effectively using Supervision’s various functionalities, including object tracking and trace annotations.
|
||||
|
||||
+56
-33
@@ -33,26 +33,57 @@ We write your reusable computer vision tools. Whether you need to load your data
|
||||
You can install `supervision` in a
|
||||
[**Python>=3.8**](https://www.python.org/) environment.
|
||||
|
||||
!!! example "pip install (recommended)"
|
||||
!!! example "Installation"
|
||||
|
||||
=== "headless"
|
||||
The headless installation of `supervision` is designed for environments where graphical user interfaces (GUI) are not needed, making it more lightweight and suitable for server-side applications.
|
||||
=== "pip (recommended)"
|
||||
[](https://badge.fury.io/py/supervision)
|
||||
[](https://pypistats.org/packages/supervision)
|
||||
[](https://github.com/roboflow/supervision/blob/main/LICENSE.md)
|
||||
[](https://badge.fury.io/py/supervision)
|
||||
|
||||
```bash
|
||||
pip install supervision
|
||||
```
|
||||
|
||||
=== "desktop"
|
||||
If you require the full version of `supervision` with GUI support you can install the desktop version. This version includes the GUI components of OpenCV, allowing you to display images and videos on the screen.
|
||||
=== "poetry"
|
||||
[](https://badge.fury.io/py/supervision)
|
||||
[](https://pypistats.org/packages/supervision)
|
||||
[](https://github.com/roboflow/supervision/blob/main/LICENSE.md)
|
||||
[](https://badge.fury.io/py/supervision)
|
||||
|
||||
```bash
|
||||
pip install "supervision[desktop]"
|
||||
poetry add supervision
|
||||
```
|
||||
|
||||
=== "uv"
|
||||
[](https://badge.fury.io/py/supervision)
|
||||
[](https://pypistats.org/packages/supervision)
|
||||
[](https://github.com/roboflow/supervision/blob/main/LICENSE.md)
|
||||
[](https://badge.fury.io/py/supervision)
|
||||
|
||||
```bash
|
||||
uv pip install supervision
|
||||
```
|
||||
|
||||
For uv projects:
|
||||
|
||||
```bash
|
||||
uv add supervision
|
||||
```
|
||||
|
||||
=== "rye"
|
||||
[](https://badge.fury.io/py/supervision)
|
||||
[](https://pypistats.org/packages/supervision)
|
||||
[](https://github.com/roboflow/supervision/blob/main/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
|
||||
@@ -60,7 +91,6 @@ 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
|
||||
@@ -68,12 +98,10 @@ You can install `supervision` in a
|
||||
```
|
||||
|
||||
!!! example "git clone (for development)"
|
||||
|
||||
=== "virtualenv"
|
||||
|
||||
```bash
|
||||
# clone repository and navigate to root directory
|
||||
git clone https://github.com/roboflow/supervision.git
|
||||
git clone --depth 1 -b develop https://github.com/roboflow/supervision.git
|
||||
cd supervision
|
||||
|
||||
# setup python environment and activate it
|
||||
@@ -81,36 +109,30 @@ You can install `supervision` in a
|
||||
source venv/bin/activate
|
||||
pip install --upgrade pip
|
||||
|
||||
# headless install
|
||||
# installation
|
||||
pip install -e "."
|
||||
|
||||
# desktop install
|
||||
pip install -e ".[desktop]"
|
||||
```
|
||||
|
||||
=== "poetry"
|
||||
|
||||
=== "uv"
|
||||
```bash
|
||||
# clone repository and navigate to root directory
|
||||
git clone https://github.com/roboflow/supervision.git
|
||||
git clone --depth 1 -b develop https://github.com/roboflow/supervision.git
|
||||
cd supervision
|
||||
|
||||
# setup python environment and activate it
|
||||
poetry env use python3.10
|
||||
poetry shell
|
||||
uv venv
|
||||
source .venv/bin/activate
|
||||
|
||||
# headless install
|
||||
poetry install
|
||||
# installation
|
||||
uv pip install -r pyproject.toml -e . --all-extras
|
||||
|
||||
# desktop install
|
||||
poetry install --extras "desktop"
|
||||
```
|
||||
|
||||
## 🚀 Quickstart
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- __Detect and Annotate__
|
||||
- **Detect and Annotate**
|
||||
|
||||
---
|
||||
|
||||
@@ -118,7 +140,7 @@ You can install `supervision` in a
|
||||
|
||||
[:octicons-arrow-right-24: Tutorial](how_to/detect_and_annotate.md)
|
||||
|
||||
- __Track Objects__
|
||||
- **Track Objects**
|
||||
|
||||
---
|
||||
|
||||
@@ -126,7 +148,7 @@ You can install `supervision` in a
|
||||
|
||||
[:octicons-arrow-right-24: Tutorial](how_to/track_objects.md)
|
||||
|
||||
- __Detect Small Objects__
|
||||
- **Detect Small Objects**
|
||||
|
||||
---
|
||||
|
||||
@@ -134,25 +156,26 @@ You can install `supervision` in a
|
||||
|
||||
[:octicons-arrow-right-24: Tutorial](how_to/detect_small_objects.md)
|
||||
|
||||
- > __Count Objects Crossing Line__
|
||||
- **Count Objects Crossing Line**
|
||||
|
||||
---
|
||||
|
||||
Explore methods to accurately count and analyze objects crossing a predefined line
|
||||
|
||||
- > __Filter Objects in Zone__
|
||||
[:octicons-arrow-right-24: Notebook](https://supervision.roboflow.com/latest/notebooks/count-objects-crossing-the-line/)
|
||||
|
||||
- > **Filter Objects in Zone**
|
||||
|
||||
---
|
||||
|
||||
Master the techniques to selectively filter and focus on objects within a specific zone
|
||||
|
||||
- **Cheatsheet**
|
||||
- **Cheatsheet**
|
||||
|
||||
***
|
||||
---
|
||||
|
||||
Access a quick reference guide to the most common `supervision` functions
|
||||
|
||||
[:octicons-arrow-right-24: Cheatsheet](https://roboflow.github.io/cheatsheet-supervision/)
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@@ -28,16 +28,15 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
}, {});
|
||||
|
||||
|
||||
async function renderCard(element, elementIndex) {
|
||||
const url = element.getAttribute('data-url');
|
||||
const name = element.getAttribute('data-name');
|
||||
const labels = element.getAttribute('data-labels');
|
||||
const version = element.getAttribute('data-version');
|
||||
const authors = element.getAttribute('data-author');
|
||||
async function renderCard(element, elementIndex) {
|
||||
const name = element.getAttribute('data-name');
|
||||
const labels = element.getAttribute('data-labels');
|
||||
const version = element.getAttribute('data-version');
|
||||
const authors = element.getAttribute('data-author');
|
||||
|
||||
const labelHTML = labels ? labels.split(',').filter(label => label !== '').map((label, index) => {
|
||||
const color = labelToColor[label.trim()];
|
||||
return `
|
||||
const labelHTML = labels ? labels.split(',').filter(label => label !== '').map((label, index) => {
|
||||
const color = labelToColor[label.trim()];
|
||||
return `
|
||||
<span
|
||||
class="label non-selectable-text"
|
||||
style="background-color: ${color}"
|
||||
@@ -45,18 +44,18 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
${label.trim()}
|
||||
</span>
|
||||
`;
|
||||
}).join(' ') : '';
|
||||
}).join(' ') : '';
|
||||
|
||||
const authorArray = authors.split(',');
|
||||
const authorDataArray = await Promise.all(authorArray.map(async (author) => {
|
||||
const response = await fetch(`https://api.github.com/users/${author.trim()}`);
|
||||
return await response.json();
|
||||
}));
|
||||
const authorArray = authors.split(',');
|
||||
const authorDataArray = await Promise.all(authorArray.map(async (author) => {
|
||||
const response = await fetch(`https://api.github.com/users/${author.trim()}`);
|
||||
return await response.json();
|
||||
}));
|
||||
|
||||
let authorAvatarsHTML = authorDataArray.map((authorData, index) => {
|
||||
const marginLeft = index === 0 ? '0' : '-10px';
|
||||
const zIndex = 4 - index;
|
||||
return `
|
||||
let authorAvatarsHTML = authorDataArray.map((authorData, index) => {
|
||||
const marginLeft = index === 0 ? '0' : '-10px';
|
||||
const zIndex = 4 - index;
|
||||
return `
|
||||
<div
|
||||
class="author-container"
|
||||
data-login="${authorData.login}-${elementIndex}"
|
||||
@@ -75,10 +74,10 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
}).join('');
|
||||
|
||||
let authorNamesHTML = authorDataArray.map(
|
||||
authorData => `
|
||||
let authorNamesHTML = authorDataArray.map(
|
||||
authorData => `
|
||||
<span
|
||||
class="author-name"
|
||||
data-login="${authorData.login}-${elementIndex}"
|
||||
@@ -88,54 +87,52 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
${authorData.login}
|
||||
</a>
|
||||
</span>`
|
||||
).join(', ');
|
||||
).join(', ');
|
||||
|
||||
let authorsHTML = `
|
||||
let authorsHTML = `
|
||||
<div class="authors">
|
||||
${authorAvatarsHTML}
|
||||
<div class="author-names">${authorNamesHTML}</div>
|
||||
</div>
|
||||
`;
|
||||
`;
|
||||
|
||||
element.innerText = `
|
||||
<a style="text-decoration: none; color: inherit;" href="${url}">
|
||||
<div style="flex-direction: column; height: 100%; display: flex;
|
||||
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji; background: ${theme.background}; font-size: 14px; line-height: 1.5; color: ${theme.color}">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<span style="font-weight: 700; font-size: 1rem; color: ${theme.linkColor};">
|
||||
${name}
|
||||
</span>
|
||||
</div>
|
||||
${authorsHTML}
|
||||
<div style="font-size: 12px; color: ${theme.color}; display: flex; flex: 0; justify-content: space-between">
|
||||
element.innerText = `
|
||||
<div style="flex-direction: column; height: 100%; display: flex;
|
||||
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji; background: ${theme.background}; font-size: 14px; line-height: 1.5; color: ${theme.color}">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<img src="/assets/supervision-lenny.png" aria-label="stars" width="20" height="20" role="img" />
|
||||
|
||||
<span style="margin-left: 4px">${version}</span>
|
||||
<span style="font-weight: 700; font-size: 1rem; color: ${theme.linkColor};">
|
||||
${name}
|
||||
</span>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; flex-wrap: wrap">
|
||||
${labelHTML}
|
||||
${authorsHTML}
|
||||
<div style="font-size: 12px; color: ${theme.color}; display: flex; flex: 0; justify-content: space-between">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<img src="/assets/supervision-lenny.png" aria-label="stars" width="20" height="20" role="img" />
|
||||
|
||||
<span style="margin-left: 4px">${version}</span>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; flex-wrap: wrap">
|
||||
${labelHTML}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
`
|
||||
`;
|
||||
|
||||
let sanitizedHTML = DOMPurify.sanitize(element.innerText);
|
||||
element.innerHTML = sanitizedHTML;
|
||||
let sanitizedHTML = DOMPurify.sanitize(element.innerText);
|
||||
element.innerHTML = sanitizedHTML;
|
||||
|
||||
document.querySelectorAll('.author-name').forEach(element => {
|
||||
element.addEventListener('mouseenter', function() {
|
||||
const login = this.getAttribute('data-login');
|
||||
document.querySelector(`.author-container[data-login="${login}"]`).classList.add('hover');
|
||||
document.querySelectorAll('.author-name').forEach(element => {
|
||||
element.addEventListener('mouseenter', function () {
|
||||
const login = this.getAttribute('data-login');
|
||||
document.querySelector(`.author-container[data-login="${login}"]`).classList.add('hover');
|
||||
});
|
||||
|
||||
element.addEventListener('mouseleave', function () {
|
||||
const login = this.getAttribute('data-login');
|
||||
document.querySelector(`.author-container[data-login="${login}"]`).classList.remove('hover');
|
||||
});
|
||||
});
|
||||
|
||||
element.addEventListener('mouseleave', function() {
|
||||
const login = this.getAttribute('data-login');
|
||||
document.querySelector(`.author-container[data-login="${login}"]`).classList.remove('hover');
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
repoCards.forEach((element, index) => {
|
||||
renderCard(element, index);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Annotators
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Common Values
|
||||
|
||||
This page contains supplementary values, types and enums that metrics use.
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.metrics.core.MetricTarget">MetricTarget</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.metrics.core.MetricTarget
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.metrics.core.AveragingMethod">AveragingMethod</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.metrics.core.AveragingMethod
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# F1 Score
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.metrics.f1_score.F1Score">F1Score</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.metrics.f1_score.F1Score
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.metrics.f1_score.F1ScoreResult">F1ScoreResult</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.metrics.f1_score.F1ScoreResult
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Mean Average Precision
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.metrics.mean_average_precision.MeanAveragePrecision">MeanAveragePrecision</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.metrics.mean_average_precision.MeanAveragePrecision
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.metrics.mean_average_precision.MeanAveragePrecisionResult">MeanAveragePrecisionResult</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.metrics.mean_average_precision.MeanAveragePrecisionResult
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Mean Average Recall
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.metrics.mean_average_recall.MeanAverageRecall">MeanAverageRecall</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.metrics.mean_average_recall.MeanAverageRecall
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.metrics.mean_average_recall.MeanAverageRecallResult">MeanAverageRecallResult</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.metrics.mean_average_recall.MeanAverageRecallResult
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Precision
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.metrics.precision.Precision">Precision</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.metrics.precision.Precision
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.metrics.precision.PrecisionResult">PrecisionResult</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.metrics.precision.PrecisionResult
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Recall
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.metrics.recall.Recall">Recall</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.metrics.recall.Recall
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.metrics.recall.RecallResult">RecallResult</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.metrics.recall.RecallResult
|
||||
+5
-5
@@ -287,10 +287,10 @@
|
||||
],
|
||||
"source": [
|
||||
"# Create a bounding box annotator object.\n",
|
||||
"bounding_box = sv.BoundingBoxAnnotator()\n",
|
||||
"box_annotator = sv.BoxAnnotator()\n",
|
||||
"\n",
|
||||
"# Annotate our frame with detections.\n",
|
||||
"annotated_frame = bounding_box.annotate(scene=frame.copy(), detections=detections)\n",
|
||||
"annotated_frame = box_annotator.annotate(scene=frame.copy(), detections=detections)\n",
|
||||
"\n",
|
||||
"# Display the frame.\n",
|
||||
"sv.plot_image(annotated_frame)"
|
||||
@@ -302,7 +302,7 @@
|
||||
"id": "o8SsyCid6YV3"
|
||||
},
|
||||
"source": [
|
||||
"Notice that we create a `box_annoator` variable by initalizing a [BoundingBoxAnnotator](https://supervision.roboflow.com/latest/annotators/#boundingboxannotator). We can change the color and thickness, but for simplicity we keep the defaults. There are a ton of easy to use [annotators](https://supervision.roboflow.com/latest/annotators/) available in the Supervision package other than a bounding box that are fun to play with."
|
||||
"Notice that we create a `box_annotator` variable by initalizing a [BoxAnnotator](https://supervision.roboflow.com/latest/detection/annotators/#boxannotator). We can change the color and thickness, but for simplicity we keep the defaults. There are a ton of easy to use [annotators](https://supervision.roboflow.com/latest/detection/annotators/) available in the Supervision package other than a bounding box that are fun to play with."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -342,7 +342,7 @@
|
||||
" detections = sv.Detections.from_inference(result)\n",
|
||||
"\n",
|
||||
" # Apply bounding box to detections on a copy of the frame.\n",
|
||||
" annotated_frame = bounding_box.annotate(\n",
|
||||
" annotated_frame = box_annotator.annotate(\n",
|
||||
" scene=frame.copy(),\n",
|
||||
" detections=detections\n",
|
||||
" )\n",
|
||||
@@ -368,7 +368,7 @@
|
||||
"<br>\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"In the code above we've created a`video_info` variable to pass information about the video to our `VideoSink`. The `VideoSink` is a cool little context manager that allows us to `write_frames()` to a video ouput file. We're also optionally using `tqdm` to display a progress bar with a % complete. We only scratched the surface of all of the customizable Annotators and additional features that Supervision and Inference have to offer. Stay tuned for more cookbooks on how to take advantge of them in your computer vision applications. Happy building! \ud83d\ude80"
|
||||
"In the code above we've created a`video_info` variable to pass information about the video to our `VideoSink`. The `VideoSink` is a cool little context manager that allows us to `write_frames()` to a video output file. We're also optionally using `tqdm` to display a progress bar with a % complete. We only scratched the surface of all of the customizable Annotators and additional features that Supervision and Inference have to offer. Stay tuned for more cookbooks on how to take advantage of them in your computer vision applications. Happy building! \ud83d\ude80"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
+223
-223
File diff suppressed because one or more lines are too long
+218
-218
File diff suppressed because one or more lines are too long
Vendored
+7
-7
File diff suppressed because one or more lines are too long
+1
-1
@@ -951,7 +951,7 @@
|
||||
},
|
||||
"source": [
|
||||
"#### Import your data\n",
|
||||
"To load your data back in, upload the saved file to the Colab enviorment and run the code cell."
|
||||
"To load your data back in, upload the saved file to the Colab environment and run the code cell."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
Vendored
+263
-270
File diff suppressed because one or more lines are too long
+1
-1
@@ -10,7 +10,7 @@
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"[](https://colab.research.google.com/github/roboflow/supervision/blob/develop/docs/notebooks/detections-to-jsonsink.ipynb)\n",
|
||||
"[](https://colab.research.google.com/github/roboflow/supervision/blob/develop/docs/notebooks/serialise-detections-to-csv.ipynb)\n",
|
||||
"\n",
|
||||
"This cookbook introduce [sv.CSVSink](https://supervision.roboflow.com/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink) tool designed to write captured object detection data to file from video streams/file"
|
||||
]
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"[](https://colab.research.google.com/github/roboflow/supervision/blob/develop/docs/notebooks/detections-to-jsonsink.ipynb)\n",
|
||||
"[](https://colab.research.google.com/github/roboflow/supervision/blob/develop/docs/notebooks/serialise-detections-to-json.ipynb)\n",
|
||||
"\n",
|
||||
"This cookbook introduce [sv.JSONSink](https://supervision.roboflow.com/develop/detection/tools/save_detections/#supervision.detection.tools.json_sink.JSONSink) tool designed to write captured object detection data to file from video streams/file"
|
||||
]
|
||||
|
||||
+1074
File diff suppressed because one or more lines are too long
+347
-347
File diff suppressed because one or more lines are too long
@@ -19,6 +19,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
.custom-grid a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.custom-grid a:hover {
|
||||
color: inherit; /* Ensure color does not change on hover */
|
||||
}
|
||||
|
||||
.repo-card {
|
||||
background: radial-gradient(at right top, #A351FB25, #A5F9EA25);
|
||||
border-radius: 0.5rem;
|
||||
|
||||
Vendored
+43
-11
@@ -11,17 +11,49 @@
|
||||
<h1>Supervision Cookbooks</h1>
|
||||
</div>
|
||||
<div class="custom-grid">
|
||||
<p class="card repo-card" data-url="/develop/notebooks/quickstart" data-name="Supervision Quickstart" data-labels="ANNOTATOR,DETECTION,SAM" data-version="v0.18.0" data-author="SkalskiP,onuralpszr"></p>
|
||||
<p class="card repo-card" data-url="/develop/notebooks/count-objects-crossing-the-line" data-name="Count Objects Crossing the Line" data-labels="ANNOTATORS,LINE ZONE,TRACKING" data-version="v0.18.0" data-author="SkalskiP"></p>
|
||||
<p class="card repo-card" data-url="/develop/notebooks/zero-shot-object-detection-with-yolo-world" data-name="Zero-Shot Object Detection with YOLO-World" data-labels="ANNOTATORS,DETECTION,INFERENCE" data-version="v0.19.0" data-author="SkalskiP"></p>
|
||||
<p class="card repo-card" data-url="/develop/notebooks/download-supervision-assets" data-name="Downloading Supervision Assets" data-labels="ASSETS" data-version="v0.18.0" data-author="nickherrig"></p>
|
||||
<p class="card repo-card" data-url="/develop/notebooks/annotate-video-with-detections" data-name="Annotate Video with Detections" data-labels="INFERENCE,YOLOV8" data-version="v0.18.0" data-author="nickherrig"></p>
|
||||
<p class="card repo-card" data-url="/develop/notebooks/object-tracking" data-name="Object Tracking" data-labels="TRACKING, ANNOTATOR" data-version="v0.18.0" data-author="nickherrig"></p>
|
||||
<p class="card repo-card" data-url="/develop/notebooks/occupancy_analytics" data-name="Analyzing Zone Occupancy" data-labels="ANNOTATOR,DETECTION,ZONES" data-version="v0.19.0" data-author="stellasphere"></p>
|
||||
<p class="card repo-card" data-url="/develop/notebooks/evaluating-alignment-of-text-to-image-diffusion-models" data-name="Evaluating Alignment of Text-to-image Diffusion Models" data-labels="ANNOTATORS,YOLO WORLD" data-version="v0.19.0rc5" data-author="iamhatesz"></p>
|
||||
<p class="card repo-card" data-url="/develop/notebooks/serialise-detections-to-csv" data-name="Serialise Detections to a CSV File" data-labels="DETECTIONS,CSV SINK,INFERENCE" data-version="v0.21.0" data-author="onuralpszr"></p>
|
||||
<p class="card repo-card" data-url="/develop/notebooks/serialise-detections-to-json" data-name="Serialise Detections to a JSON File" data-labels="DETECTIONS,JSON SINK,INFERENCE" data-version="v0.21.0" data-author="onuralpszr"></p>
|
||||
|
||||
<a href="/develop/notebooks/quickstart"> <p class="card repo-card" data-name="Supervision Quickstart" data-labels="ANNOTATOR,DETECTION,SAM"
|
||||
data-version="v0.18.0" data-author="SkalskiP,onuralpszr"></p>
|
||||
</a>
|
||||
<a href="/develop/notebooks/count-objects-crossing-the-line">
|
||||
<p class="card repo-card" data-name="Count Objects Crossing the Line"
|
||||
data-labels="ANNOTATORS,LINE ZONE,TRACKING" data-version="v0.18.0" data-author="SkalskiP"></p>
|
||||
</a>
|
||||
<a href="/develop/notebooks/zero-shot-object-detection-with-yolo-world">
|
||||
<p class="card repo-card" data-name="Zero-Shot Object Detection with YOLO-World"
|
||||
data-labels="ANNOTATORS,DETECTION,INFERENCE" data-version="v0.19.0" data-author="SkalskiP"></p>
|
||||
</a>
|
||||
<a href="/develop/notebooks/download-supervision-assets">
|
||||
<p class="card repo-card" data-name="Downloading Supervision Assets" data-labels="ASSETS" data-version="v0.18.0"
|
||||
data-author="nickherrig"></p>
|
||||
</a>
|
||||
<a href="/develop/notebooks/annotate-video-with-detections">
|
||||
<p class="card repo-card" data-name="Annotate Video with Detections" data-labels="INFERENCE,YOLOV8"
|
||||
data-version="v0.18.0" data-author="nickherrig"></p>
|
||||
</a>
|
||||
<a href="/develop/notebooks/object-tracking">
|
||||
<p class="card repo-card" data-name="Object Tracking" data-labels="TRACKING, ANNOTATOR" data-version="v0.18.0"
|
||||
data-author="nickherrig"></p>
|
||||
</a>
|
||||
<a href="/develop/notebooks/occupancy_analytics">
|
||||
<p class="card repo-card" data-name="Analyzing Zone Occupancy" data-labels="ANNOTATOR,DETECTION,ZONES"
|
||||
data-version="v0.19.0" data-author="stellasphere"></p>
|
||||
</a>
|
||||
<a href="/develop/notebooks/evaluating-alignment-of-text-to-image-diffusion-models">
|
||||
<p class="card repo-card" data-name="Evaluating Alignment of Text-to-image Diffusion Models"
|
||||
data-labels="ANNOTATORS,YOLO WORLD" data-version="v0.19.0rc5" data-author="iamhatesz"></p>
|
||||
</a>
|
||||
<a href="/develop/notebooks/serialise-detections-to-csv">
|
||||
<p class="card repo-card" data-name="Serialise Detections to a CSV File"
|
||||
data-labels="DETECTIONS,CSV SINK,INFERENCE" data-version="v0.21.0" data-author="onuralpszr"></p>
|
||||
</a>
|
||||
<a href="/develop/notebooks/serialise-detections-to-json">
|
||||
<p class="card repo-card" data-name="Serialise Detections to a JSON File"
|
||||
data-labels="DETECTIONS,JSON SINK,INFERENCE" data-version="v0.21.0" data-author="onuralpszr"></p>
|
||||
</a>
|
||||
<a href="/develop/notebooks/small-object-detection-with-sahi">
|
||||
<p class="card repo-card" data-name="Small Object Detection with SAHI"
|
||||
data-labels="DETECTIONS,SAHI,SMALL,OBJECT,INFERENCE" data-version="v0.23.0" data-author="ediardo"></p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# ByteTrack
|
||||
|
||||
@@ -28,6 +28,12 @@ comments: true
|
||||
|
||||
:::supervision.draw.utils.draw_polygon
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.draw.utils.draw_filled_polygon">draw_filled_polygon</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.draw.utils.draw_filled_polygon
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.draw.utils.draw_text">draw_text</a></h2>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# File Utils
|
||||
|
||||
@@ -16,67 +16,76 @@ https://github.com/roboflow/supervision/assets/26109316/f84db7b5-79e2-4142-a1da-
|
||||
|
||||
- clone repository and navigate to example directory
|
||||
|
||||
```bash
|
||||
git clone https://github.com/roboflow/supervision.git
|
||||
cd supervision/examples/count_people_in_zone
|
||||
```
|
||||
```bash
|
||||
git clone --depth 1 -b develop https://github.com/roboflow/supervision.git
|
||||
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
|
||||
```
|
||||
```bash
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
```
|
||||
|
||||
- install required dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
- download `traffic_analysis.pt` and `traffic_analysis.mov` files
|
||||
|
||||
```bash
|
||||
./setup.sh
|
||||
```
|
||||
```bash
|
||||
./setup.sh
|
||||
```
|
||||
|
||||
## 🛠️ script arguments
|
||||
|
||||
- ultralytics
|
||||
|
||||
- `--source_weights_path` (optional): The path to the YOLO model's weights file.
|
||||
Defaults to `"yolov8x.pt"` if not specified.
|
||||
- `--source_weights_path` (optional): The path to the YOLO model's weights file.
|
||||
Defaults to `"yolov8x.pt"` if not specified.
|
||||
|
||||
- `--zone_configuration_path`: Specifies the path to the JSON file containing zone
|
||||
configurations. This file defines the polygonal areas in the video where objects will
|
||||
be counted.
|
||||
- `--source_video_path`: The path to the source video file that will be analyzed.
|
||||
- `--target_video_path` (optional): The path to save the output video with annotations.
|
||||
If not provided, the processed video will be displayed in real-time.
|
||||
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO model
|
||||
to filter detections. Default is `0.3`.
|
||||
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
|
||||
for the model. Default is `0.7`.
|
||||
- `--zone_configuration_path`: Specifies the path to the JSON file containing zone
|
||||
configurations. This file defines the polygonal areas in the video where objects will
|
||||
be counted.
|
||||
|
||||
- `--source_video_path`: The path to the source video file that will be analyzed.
|
||||
|
||||
- `--target_video_path` (optional): The path to save the output video with annotations.
|
||||
If not provided, the processed video will be displayed in real-time.
|
||||
|
||||
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO model
|
||||
to filter detections. Default is `0.3`.
|
||||
|
||||
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
|
||||
for the model. Default is `0.7`.
|
||||
|
||||
- inference
|
||||
|
||||
- `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided
|
||||
directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment
|
||||
variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key)
|
||||
to acquire your `API KEY`.
|
||||
- `--model_id` (optional): Designates the Roboflow model ID to be used. The default
|
||||
value is `"yolov8x-1280"`.
|
||||
- `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided
|
||||
directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment
|
||||
variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key)
|
||||
to acquire your `API KEY`.
|
||||
|
||||
- `--zone_configuration_path`: Specifies the path to the JSON file containing zone
|
||||
configurations. This file defines the polygonal areas in the video where objects will
|
||||
be counted.
|
||||
- `--source_video_path`: The path to the source video file that will be analyzed.
|
||||
- `--target_video_path` (optional): The path to save the output video with annotations.
|
||||
If not provided, the processed video will be displayed in real-time.
|
||||
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO model
|
||||
to filter detections. Default is `0.3`.
|
||||
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
|
||||
for the model. Default is `0.7`.
|
||||
- `--model_id` (optional): Designates the Roboflow model ID to be used. The default
|
||||
value is `"yolov8x-1280"`.
|
||||
|
||||
- `--zone_configuration_path`: Specifies the path to the JSON file containing zone
|
||||
configurations. This file defines the polygonal areas in the video where objects will
|
||||
be counted.
|
||||
|
||||
- `--source_video_path`: The path to the source video file that will be analyzed.
|
||||
|
||||
- `--target_video_path` (optional): The path to save the output video with annotations.
|
||||
If not provided, the processed video will be displayed in real-time.
|
||||
|
||||
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO model
|
||||
to filter detections. Default is `0.3`.
|
||||
|
||||
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
|
||||
for the model. Default is `0.7`.
|
||||
|
||||
## 📌 zone configuration
|
||||
|
||||
@@ -89,35 +98,35 @@ https://github.com/roboflow/supervision/assets/26109316/f84db7b5-79e2-4142-a1da-
|
||||
|
||||
- ultralytics
|
||||
|
||||
```bash
|
||||
python ultralytics_example.py \
|
||||
--zone_configuration_path data/multi-zone-config.json \
|
||||
--source_video_path data/market-square.mp4 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.5
|
||||
```
|
||||
```bash
|
||||
python ultralytics_example.py \
|
||||
--zone_configuration_path data/multi-zone-config.json \
|
||||
--source_video_path data/market-square.mp4 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.5
|
||||
```
|
||||
|
||||
- inference
|
||||
|
||||
```bash
|
||||
python inference_example.py \
|
||||
--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 \
|
||||
--iou_threshold 0.5
|
||||
```
|
||||
```bash
|
||||
python inference_example.py \
|
||||
--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 \
|
||||
--iou_threshold 0.5
|
||||
```
|
||||
|
||||
## © license
|
||||
|
||||
This demo integrates two main components, each with its own licensing:
|
||||
|
||||
- ultralytics: The object detection model used in this demo, YOLOv8, is distributed
|
||||
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
|
||||
You can find more details about this license here.
|
||||
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
|
||||
You can find more details about this license here.
|
||||
|
||||
- supervision: The analytics code that powers the zone-based analysis in this demo is
|
||||
based on the Supervision library, which is licensed under the
|
||||
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
|
||||
makes the Supervision part of the code fully open source and freely usable in your
|
||||
projects.
|
||||
based on the Supervision library, which is licensed under the
|
||||
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
|
||||
makes the Supervision part of the code fully open source and freely usable in your
|
||||
projects.
|
||||
|
||||
@@ -35,9 +35,7 @@ def load_zones_config(file_path: str) -> List[np.ndarray]:
|
||||
|
||||
def initiate_annotators(
|
||||
polygons: List[np.ndarray], resolution_wh: Tuple[int, int]
|
||||
) -> Tuple[
|
||||
List[sv.PolygonZone], List[sv.PolygonZoneAnnotator], List[sv.BoundingBoxAnnotator]
|
||||
]:
|
||||
) -> Tuple[List[sv.PolygonZone], List[sv.PolygonZoneAnnotator], List[sv.BoxAnnotator]]:
|
||||
line_thickness = sv.calculate_optimal_line_thickness(resolution_wh=resolution_wh)
|
||||
text_scale = sv.calculate_optimal_text_scale(resolution_wh=resolution_wh)
|
||||
|
||||
@@ -54,7 +52,7 @@ def initiate_annotators(
|
||||
text_thickness=line_thickness * 2,
|
||||
text_scale=text_scale * 2,
|
||||
)
|
||||
box_annotator = sv.BoundingBoxAnnotator(
|
||||
box_annotator = sv.BoxAnnotator(
|
||||
color=COLORS.by_idx(index), thickness=line_thickness
|
||||
)
|
||||
zones.append(zone)
|
||||
@@ -75,7 +73,7 @@ def detect(
|
||||
frame (np.ndarray): The frame to process, expected to be a NumPy array.
|
||||
model (RoboflowInferenceModel): The Inference model used for processing the
|
||||
frame.
|
||||
confidence_threshold (float, optional): The confidence threshold for filtering
|
||||
confidence_threshold (float): The confidence threshold for filtering
|
||||
detections. Default is 0.5.
|
||||
|
||||
Returns:
|
||||
@@ -97,7 +95,7 @@ def annotate(
|
||||
frame: np.ndarray,
|
||||
zones: List[sv.PolygonZone],
|
||||
zone_annotators: List[sv.PolygonZoneAnnotator],
|
||||
box_annotators: List[sv.BoundingBoxAnnotator],
|
||||
box_annotators: List[sv.BoxAnnotator],
|
||||
detections: sv.Detections,
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
@@ -108,7 +106,7 @@ def annotate(
|
||||
zones (List[sv.PolygonZone]): A list of polygon zones used for detection.
|
||||
zone_annotators (List[sv.PolygonZoneAnnotator]): A list of annotators for
|
||||
drawing zone annotations.
|
||||
box_annotators (List[sv.BoundingBoxAnnotator]): A list of annotators for
|
||||
box_annotators (List[sv.BoxAnnotator]): A list of annotators for
|
||||
drawing box annotations.
|
||||
detections (sv.Detections): Detections to be used for annotation.
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@ gdown
|
||||
inference==0.9.17
|
||||
supervision>=0.20.0
|
||||
tqdm
|
||||
ultralytics
|
||||
ultralytics<=8.3.40
|
||||
|
||||
@@ -33,9 +33,7 @@ def load_zones_config(file_path: str) -> List[np.ndarray]:
|
||||
|
||||
def initiate_annotators(
|
||||
polygons: List[np.ndarray], resolution_wh: Tuple[int, int]
|
||||
) -> Tuple[
|
||||
List[sv.PolygonZone], List[sv.PolygonZoneAnnotator], List[sv.BoundingBoxAnnotator]
|
||||
]:
|
||||
) -> Tuple[List[sv.PolygonZone], List[sv.PolygonZoneAnnotator], List[sv.BoxAnnotator]]:
|
||||
line_thickness = sv.calculate_optimal_line_thickness(resolution_wh=resolution_wh)
|
||||
text_scale = sv.calculate_optimal_text_scale(resolution_wh=resolution_wh)
|
||||
|
||||
@@ -52,7 +50,7 @@ def initiate_annotators(
|
||||
text_thickness=line_thickness * 2,
|
||||
text_scale=text_scale * 2,
|
||||
)
|
||||
box_annotator = sv.BoundingBoxAnnotator(
|
||||
box_annotator = sv.BoxAnnotator(
|
||||
color=COLORS.by_idx(index), thickness=line_thickness
|
||||
)
|
||||
zones.append(zone)
|
||||
@@ -72,7 +70,7 @@ def detect(
|
||||
Args:
|
||||
frame (np.ndarray): The frame to process, expected to be a NumPy array.
|
||||
model (YOLO): The YOLO model used for processing the frame.
|
||||
confidence_threshold (float, optional): The confidence threshold for filtering
|
||||
confidence_threshold (float): The confidence threshold for filtering
|
||||
detections. Default is 0.5.
|
||||
|
||||
Returns:
|
||||
@@ -94,7 +92,7 @@ def annotate(
|
||||
frame: np.ndarray,
|
||||
zones: List[sv.PolygonZone],
|
||||
zone_annotators: List[sv.PolygonZoneAnnotator],
|
||||
box_annotators: List[sv.BoundingBoxAnnotator],
|
||||
box_annotators: List[sv.BoxAnnotator],
|
||||
detections: sv.Detections,
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
@@ -105,7 +103,7 @@ def annotate(
|
||||
zones (List[sv.PolygonZone]): A list of polygon zones used for detection.
|
||||
zone_annotators (List[sv.PolygonZoneAnnotator]): A list of annotators for
|
||||
drawing zone annotations.
|
||||
box_annotators (List[sv.BoundingBoxAnnotator]): A list of annotators for
|
||||
box_annotators (List[sv.BoxAnnotator]): A list of annotators for
|
||||
drawing box annotations.
|
||||
detections (sv.Detections): Detections to be used for annotation.
|
||||
|
||||
|
||||
@@ -6,17 +6,16 @@ This script performs heatmap and tracking analysis using YOLOv8, an object-detec
|
||||
ByteTrack, a simple yet effective online multi-object tracking method. It uses the
|
||||
supervision package for multiple tasks such as drawing heatmap annotations, tracking objects, etc.
|
||||
|
||||
|
||||
## 💻 install
|
||||
|
||||
- clone repository and navigate to example directory
|
||||
|
||||
```bash
|
||||
git clone https://github.com/roboflow/supervision.git
|
||||
git clone --depth 1 -b develop https://github.com/roboflow/supervision.git
|
||||
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
|
||||
@@ -32,17 +31,17 @@ supervision package for multiple tasks such as drawing heatmap annotations, trac
|
||||
## 🛠️ script arguments
|
||||
|
||||
- `--source_weights_path`: Required. Specifies the path to the weights file for the
|
||||
YOLO model. This file contains the trained model data necessary for object detection.
|
||||
YOLO model. This file contains the trained model data necessary for object detection.
|
||||
- `--source_video_path` (optional): The path to the source video file that will be
|
||||
analyzed. This is the input video on which crowd analysis will be performed.
|
||||
If not specified default is `people-walking.mp4` from supervision assets
|
||||
analyzed. This is the input video on which crowd analysis will be performed.
|
||||
If not specified default is `people-walking.mp4` from supervision assets
|
||||
- `--target_video_path` (optional): The path to save the output.mp4 video with annotations.
|
||||
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO model
|
||||
to filter detections. Default is `0.3`. This determines how confident the model should
|
||||
be to recognize an object in the video.
|
||||
to filter detections. Default is `0.3`. This determines how confident the model should
|
||||
be to recognize an object in the video.
|
||||
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
|
||||
for the model. Default is 0.7. This value is used to manage object detection accuracy,
|
||||
particularly in distinguishing between different objects.
|
||||
for the model. Default is 0.7. This value is used to manage object detection accuracy,
|
||||
particularly in distinguishing between different objects.
|
||||
- `--heatmap_alpha` (optional): Opacity of the overlay mask, between 0 and 1.
|
||||
- `--radius` (optional): Radius of the heat circle.
|
||||
- `--track_threshold` (optional): Detection confidence threshold for track activation.
|
||||
@@ -53,11 +52,11 @@ particularly in distinguishing between different objects.
|
||||
|
||||
```bash
|
||||
python script.py \
|
||||
--source_weights_path weight.pt \
|
||||
--source_video_path input_video.mp4 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.5 \
|
||||
--target_video_path output_video.mp4
|
||||
--source_weights_path weight.pt \
|
||||
--source_video_path input_video.mp4 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.5 \
|
||||
--target_video_path output_video.mp4
|
||||
```
|
||||
|
||||
## © license
|
||||
@@ -65,11 +64,11 @@ python script.py \
|
||||
This demo integrates two main components, each with its own licensing:
|
||||
|
||||
- ultralytics: The object detection model used in this demo, YOLOv8, is distributed
|
||||
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
|
||||
You can find more details about this license here.
|
||||
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
|
||||
You can find more details about this license here.
|
||||
|
||||
- supervision: The analytics code that powers the zone-based analysis in this demo is
|
||||
based on the Supervision library, which is licensed under the
|
||||
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
|
||||
makes the Supervision part of the code fully open source and freely usable in your
|
||||
projects.
|
||||
based on the Supervision library, which is licensed under the
|
||||
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
|
||||
makes the Supervision part of the code fully open source and freely usable in your
|
||||
projects.
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
supervision[assets]==0.19.0
|
||||
ultralytics
|
||||
ultralytics<=8.3.40
|
||||
|
||||
@@ -20,9 +20,9 @@ def heatmap_and_track(
|
||||
iou_threshold: float = 0.5,
|
||||
heatmap_alpha: float = 0.5,
|
||||
radius: int = 25,
|
||||
track_threshold: float = 0.35,
|
||||
track_activation_threshold: float = 0.35,
|
||||
track_seconds: int = 5,
|
||||
match_threshold: float = 0.99,
|
||||
minimum_matching_threshold: float = 0.99,
|
||||
) -> None:
|
||||
### instantiate model
|
||||
model = YOLO(source_weights_path)
|
||||
@@ -47,9 +47,9 @@ def heatmap_and_track(
|
||||
|
||||
### tracker config
|
||||
byte_tracker = sv.ByteTrack(
|
||||
track_thresh=track_threshold,
|
||||
track_buffer=track_seconds * fps,
|
||||
match_thresh=match_threshold,
|
||||
track_activation_threshold=track_activation_threshold,
|
||||
lost_track_buffer=track_seconds * fps,
|
||||
minimum_matching_threshold=minimum_matching_threshold,
|
||||
frame_rate=fps,
|
||||
)
|
||||
|
||||
@@ -175,7 +175,7 @@ if __name__ == "__main__":
|
||||
iou_threshold=args.iou_threshold,
|
||||
heatmap_alpha=args.heatmap_alpha,
|
||||
radius=args.radius,
|
||||
track_threshold=args.track_threshold,
|
||||
track_activation_threshold=args.track_threshold,
|
||||
track_seconds=args.track_seconds,
|
||||
match_threshold=args.match_threshold,
|
||||
minimum_matching_threshold=args.match_threshold,
|
||||
)
|
||||
|
||||
@@ -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
|
||||
@@ -21,97 +21,102 @@ https://github.com/roboflow/supervision/assets/26109316/d50118c1-2ae4-458d-915a-
|
||||
|
||||
- clone repository and navigate to example directory
|
||||
|
||||
```bash
|
||||
git clone https://github.com/roboflow/supervision.git
|
||||
cd supervision/examples/speed_estimation
|
||||
```
|
||||
```bash
|
||||
git clone --depth 1 -b develop https://github.com/roboflow/supervision.git
|
||||
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
|
||||
```
|
||||
```bash
|
||||
python3.10 -m venv venv
|
||||
source venv/bin/activate
|
||||
```
|
||||
|
||||
- install required dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
- download `vehicles.mp4` file
|
||||
|
||||
```bash
|
||||
python3.10 video_downloader.py
|
||||
```
|
||||
```bash
|
||||
python3.10 video_downloader.py
|
||||
```
|
||||
|
||||
## 🛠️ script arguments
|
||||
|
||||
- `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided
|
||||
directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment
|
||||
variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key)
|
||||
to acquire your `API KEY`.
|
||||
directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment
|
||||
variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key)
|
||||
to acquire your `API KEY`.
|
||||
|
||||
- `--model_id` (optional): Designates the Roboflow model ID to be used. The default
|
||||
value is `"yolov8x-1280"`.
|
||||
value is `"yolov8x-1280"`.
|
||||
|
||||
- `--source_weights_path`: Required. Specifies the path to the YOLO model's weights
|
||||
file, which is essential for the object detection process. This file contains the
|
||||
data that the model uses to identify objects in the video.
|
||||
file, which is essential for the object detection process. This file contains the
|
||||
data that the model uses to identify objects in the video.
|
||||
|
||||
- `--source_video_path`: Required. The path to the source video file that will be
|
||||
analyzed. This is the input video on which traffic flow analysis will be performed.
|
||||
analyzed. This is the input video on which traffic flow analysis will be performed.
|
||||
|
||||
- `--target_video_path`: The path to save the output video with
|
||||
annotations. If not specified, the processed video will be displayed in real-time
|
||||
without being saved.
|
||||
annotations. If not specified, the processed video will be displayed in real-time
|
||||
without being saved.
|
||||
|
||||
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO
|
||||
model to filter detections. Default is `0.3`. This determines how confident the
|
||||
model should be to recognize an object in the video.
|
||||
model to filter detections. Default is `0.3`. This determines how confident the
|
||||
model should be to recognize an object in the video.
|
||||
|
||||
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
|
||||
for the model. Default is 0.7. This value is used to manage object detection
|
||||
accuracy, particularly in distinguishing between different objects.
|
||||
for the model. Default is 0.7. This value is used to manage object detection
|
||||
accuracy, particularly in distinguishing between different objects.
|
||||
|
||||
## ⚙️ run
|
||||
|
||||
- yolo-nas
|
||||
|
||||
```bash
|
||||
```bash
|
||||
python yolo_nas_example.py \
|
||||
--source_video_path data/vehicles.mp4 \
|
||||
--target_video_path data/vehicles-result.mp4 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.5
|
||||
```
|
||||
--source_video_path data/vehicles.mp4 \
|
||||
--target_video_path data/vehicles-result.mp4 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.5
|
||||
```
|
||||
|
||||
- inference
|
||||
|
||||
```bash
|
||||
```bash
|
||||
python inference_example.py \
|
||||
--roboflow_api_key <ROBOFLOW API KEY> \
|
||||
--source_video_path data/vehicles.mp4 \
|
||||
--target_video_path data/vehicles-result.mp4 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.5
|
||||
```
|
||||
--roboflow_api_key <ROBOFLOW API KEY> \
|
||||
--source_video_path data/vehicles.mp4 \
|
||||
--target_video_path data/vehicles-result.mp4 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.5
|
||||
```
|
||||
|
||||
- ultralytics
|
||||
|
||||
```bash
|
||||
```bash
|
||||
python ultralytics_example.py \
|
||||
--source_video_path data/vehicles.mp4 \
|
||||
--target_video_path data/vehicles-result.mp4 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.5
|
||||
```
|
||||
--source_video_path data/vehicles.mp4 \
|
||||
--target_video_path data/vehicles-result.mp4 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.5
|
||||
```
|
||||
|
||||
## © license
|
||||
|
||||
This demo integrates two main components, each with its own licensing:
|
||||
|
||||
- ultralytics: The object detection model used in this demo, YOLOv8, is distributed
|
||||
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
|
||||
You can find more details about this license here.
|
||||
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
|
||||
You can find more details about this license here.
|
||||
|
||||
- supervision: The analytics code that powers the zone-based analysis in this demo is
|
||||
based on the Supervision library, which is licensed under the
|
||||
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
|
||||
makes the Supervision part of the code fully open source and freely usable in your
|
||||
projects.
|
||||
based on the Supervision library, which is licensed under the
|
||||
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
|
||||
makes the Supervision part of the code fully open source and freely usable in your
|
||||
projects.
|
||||
|
||||
@@ -95,7 +95,7 @@ if __name__ == "__main__":
|
||||
model = get_roboflow_model(model_id=args.model_id, api_key=args.roboflow_api_key)
|
||||
|
||||
byte_track = sv.ByteTrack(
|
||||
frame_rate=video_info.fps, track_thresh=args.confidence_threshold
|
||||
frame_rate=video_info.fps, track_activation_threshold=args.confidence_threshold
|
||||
)
|
||||
|
||||
thickness = sv.calculate_optimal_line_thickness(
|
||||
|
||||
@@ -73,7 +73,7 @@ if __name__ == "__main__":
|
||||
model = YOLO("yolov8x.pt")
|
||||
|
||||
byte_track = sv.ByteTrack(
|
||||
frame_rate=video_info.fps, track_thresh=args.confidence_threshold
|
||||
frame_rate=video_info.fps, track_activation_threshold=args.confidence_threshold
|
||||
)
|
||||
|
||||
thickness = sv.calculate_optimal_line_thickness(
|
||||
|
||||
@@ -74,7 +74,7 @@ if __name__ == "__main__":
|
||||
model = models.get(Models.YOLO_NAS_L, pretrained_weights="coco")
|
||||
|
||||
byte_track = sv.ByteTrack(
|
||||
frame_rate=video_info.fps, track_thresh=args.confidence_threshold
|
||||
frame_rate=video_info.fps, track_activation_threshold=args.confidence_threshold
|
||||
)
|
||||
|
||||
thickness = sv.calculate_optimal_line_thickness(
|
||||
|
||||
+114
-111
@@ -15,23 +15,23 @@ https://github.com/roboflow/supervision/assets/26109316/d051cc8a-dd15-41d4-aa36-
|
||||
|
||||
- clone repository and navigate to example directory
|
||||
|
||||
```bash
|
||||
git clone https://github.com/roboflow/supervision.git
|
||||
cd supervision/examples/time_in_zone
|
||||
```
|
||||
```bash
|
||||
git clone --depth 1 -b develop https://github.com/roboflow/supervision.git
|
||||
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
|
||||
```
|
||||
```bash
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
```
|
||||
|
||||
- install required dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## 🛠 scripts
|
||||
|
||||
@@ -45,16 +45,16 @@ This script allows you to download a video from YouTube.
|
||||
|
||||
```bash
|
||||
python scripts/download_from_youtube.py \
|
||||
--url "https://www.youtube.com/watch?v=-8zyEwAa50Q" \
|
||||
--output_path "data/checkout" \
|
||||
--file_name "video.mp4"
|
||||
--url "https://www.youtube.com/watch?v=-8zyEwAa50Q" \
|
||||
--output_path "data/checkout" \
|
||||
--file_name "video.mp4"
|
||||
```
|
||||
|
||||
```bash
|
||||
python scripts/download_from_youtube.py \
|
||||
--url "https://www.youtube.com/watch?v=MNn9qKG2UFI" \
|
||||
--output_path "data/traffic" \
|
||||
--file_name "video.mp4"
|
||||
--url "https://www.youtube.com/watch?v=MNn9qKG2UFI" \
|
||||
--output_path "data/traffic" \
|
||||
--file_name "video.mp4"
|
||||
```
|
||||
|
||||
### `stream_from_file`
|
||||
@@ -68,14 +68,14 @@ mock a live video stream for local testing. Video will be streamed in a loop und
|
||||
|
||||
```bash
|
||||
python scripts/stream_from_file.py \
|
||||
--video_directory "data/checkout" \
|
||||
--number_of_streams 1
|
||||
--video_directory "data/checkout" \
|
||||
--number_of_streams 1
|
||||
```
|
||||
|
||||
```bash
|
||||
python scripts/stream_from_file.py \
|
||||
--video_directory "data/traffic" \
|
||||
--number_of_streams 1
|
||||
--video_directory "data/traffic" \
|
||||
--number_of_streams 1
|
||||
```
|
||||
|
||||
### `draw_zones`
|
||||
@@ -86,24 +86,27 @@ 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
|
||||
python scripts/draw_zones.py \
|
||||
--source_path "data/checkout/video.mp4" \
|
||||
--zone_configuration_path "data/checkout/config.json"
|
||||
--source_path "data/checkout/video.mp4" \
|
||||
--zone_configuration_path "data/checkout/config.json"
|
||||
```
|
||||
|
||||
```bash
|
||||
python scripts/draw_zones.py \
|
||||
--source_path "data/traffic/video.mp4" \
|
||||
--zone_configuration_path "data/traffic/config.json"
|
||||
--source_path "data/traffic/video.mp4" \
|
||||
--zone_configuration_path "data/traffic/config.json"
|
||||
```
|
||||
|
||||
https://github.com/roboflow/supervision/assets/26109316/9d514c9e-2a61-418b-ae49-6ac1ad6ae5ac
|
||||
@@ -114,33 +117,33 @@ https://github.com/roboflow/supervision/assets/26109316/9d514c9e-2a61-418b-ae49-
|
||||
|
||||
Script to run object detection on a video file using the Roboflow Inference model.
|
||||
|
||||
- `--zone_configuration_path`: Path to the zone configuration JSON file.
|
||||
- `--source_video_path`: Path to the source video file.
|
||||
- `--model_id`: Roboflow model ID.
|
||||
- `--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`.
|
||||
- `--zone_configuration_path`: Path to the zone configuration JSON file.
|
||||
- `--source_video_path`: Path to the source video file.
|
||||
- `--model_id`: Roboflow model ID.
|
||||
- `--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`.
|
||||
|
||||
```bash
|
||||
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 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
--zone_configuration_path "data/checkout/config.json" \
|
||||
--source_video_path "data/checkout/video.mp4" \
|
||||
--model_id "yolov8x-640" \
|
||||
--classes 0 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
```
|
||||
|
||||
https://github.com/roboflow/supervision/assets/26109316/d051cc8a-dd15-41d4-aa36-d38b86334c39
|
||||
|
||||
```bash
|
||||
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 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
--zone_configuration_path "data/traffic/config.json" \
|
||||
--source_video_path "data/traffic/video.mp4" \
|
||||
--model_id "yolov8x-640" \
|
||||
--classes 2 5 6 7 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
```
|
||||
|
||||
https://github.com/roboflow/supervision/assets/26109316/5ec896d7-4b39-4426-8979-11e71666878b
|
||||
@@ -149,31 +152,31 @@ https://github.com/roboflow/supervision/assets/26109316/5ec896d7-4b39-4426-8979-
|
||||
|
||||
Script to run object detection on a video stream using the Roboflow Inference model.
|
||||
|
||||
- `--zone_configuration_path`: Path to the zone configuration JSON file.
|
||||
- `--rtsp_url`: Complete RTSP URL for the video stream.
|
||||
- `--model_id`: Roboflow model ID.
|
||||
- `--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`.
|
||||
- `--zone_configuration_path`: Path to the zone configuration JSON file.
|
||||
- `--rtsp_url`: Complete RTSP URL for the video stream.
|
||||
- `--model_id`: Roboflow model ID.
|
||||
- `--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`.
|
||||
|
||||
```bash
|
||||
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 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
--zone_configuration_path "data/checkout/config.json" \
|
||||
--rtsp_url "rtsp://localhost:8554/live0.stream" \
|
||||
--model_id "yolov8x-640" \
|
||||
--classes 0 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
```
|
||||
|
||||
```bash
|
||||
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 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
--zone_configuration_path "data/traffic/config.json" \
|
||||
--rtsp_url "rtsp://localhost:8554/live0.stream" \
|
||||
--model_id "yolov8x-640" \
|
||||
--classes 2 5 6 7 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
```
|
||||
|
||||
<details>
|
||||
@@ -183,68 +186,68 @@ python inference_stream_example.py \
|
||||
|
||||
Script to run object detection on a video file using the Ultralytics YOLOv8 model.
|
||||
|
||||
- `--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. Default is `'yolov8s.pt'`.
|
||||
- `--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`.
|
||||
- `--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. Default is `'yolov8s.pt'`.
|
||||
- `--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`.
|
||||
|
||||
```bash
|
||||
python ultralytics_file_example.py \
|
||||
--zone_configuration_path "data/checkout/config.json" \
|
||||
--source_video_path "data/checkout/video.mp4" \
|
||||
--weights "yolov8x.pt" \
|
||||
--device "cpu" \
|
||||
--classes 0 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
--zone_configuration_path "data/checkout/config.json" \
|
||||
--source_video_path "data/checkout/video.mp4" \
|
||||
--weights "yolov8x.pt" \
|
||||
--device "cpu" \
|
||||
--classes 0 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
```
|
||||
|
||||
```bash
|
||||
python ultralytics_file_example.py \
|
||||
--zone_configuration_path "data/traffic/config.json" \
|
||||
--source_video_path "data/traffic/video.mp4" \
|
||||
--weights "yolov8x.pt" \
|
||||
--device "cpu" \
|
||||
--classes 2 5 6 7 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
--zone_configuration_path "data/traffic/config.json" \
|
||||
--source_video_path "data/traffic/video.mp4" \
|
||||
--weights "yolov8x.pt" \
|
||||
--device "cpu" \
|
||||
--classes 2 5 6 7 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
```
|
||||
|
||||
### `ultralytics_stream_example`
|
||||
|
||||
Script to run object detection on a video stream using the Ultralytics YOLOv8 model.
|
||||
|
||||
- `--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. Default is `'yolov8s.pt'`.
|
||||
- `--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`.
|
||||
- `--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. Default is `'yolov8s.pt'`.
|
||||
- `--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`.
|
||||
|
||||
```bash
|
||||
python ultralytics_stream_example.py \
|
||||
--zone_configuration_path "data/checkout/config.json" \
|
||||
--rtsp_url "rtsp://localhost:8554/live0.stream" \
|
||||
--weights "yolov8x.pt" \
|
||||
--device "cpu" \
|
||||
--classes 0 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
--zone_configuration_path "data/checkout/config.json" \
|
||||
--rtsp_url "rtsp://localhost:8554/live0.stream" \
|
||||
--weights "yolov8x.pt" \
|
||||
--device "cpu" \
|
||||
--classes 0 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
```
|
||||
|
||||
```bash
|
||||
python ultralytics_stream_example.py \
|
||||
--zone_configuration_path "data/traffic/config.json" \
|
||||
--rtsp_url "rtsp://localhost:8554/live0.stream" \
|
||||
--weights "yolov8x.pt" \
|
||||
--device "cpu" \
|
||||
--classes 2 5 6 7 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
--zone_configuration_path "data/traffic/config.json" \
|
||||
--rtsp_url "rtsp://localhost:8554/live0.stream" \
|
||||
--weights "yolov8x.pt" \
|
||||
--device "cpu" \
|
||||
--classes 2 5 6 7 \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.7
|
||||
```
|
||||
|
||||
</details>
|
||||
@@ -254,11 +257,11 @@ python ultralytics_stream_example.py \
|
||||
This demo integrates two main components, each with its own licensing:
|
||||
|
||||
- ultralytics: The object detection model used in this demo, YOLOv8, is distributed
|
||||
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
|
||||
You can find more details about this license here.
|
||||
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
|
||||
You can find more details about this license here.
|
||||
|
||||
- supervision: The analytics code that powers the zone-based analysis in this demo is
|
||||
based on the Supervision library, which is licensed under the
|
||||
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
|
||||
makes the Supervision part of the code fully open source and freely usable in your
|
||||
projects.
|
||||
based on the Supervision library, which is licensed under the
|
||||
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
|
||||
makes the Supervision part of the code fully open source and freely usable in your
|
||||
projects.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
opencv-python
|
||||
supervision>=0.20.0
|
||||
ultralytics
|
||||
ultralytics<=8.3.40
|
||||
inference==0.9.17
|
||||
pytube
|
||||
|
||||
@@ -22,7 +22,7 @@ class FPSBasedTimer:
|
||||
"""Initializes the FPSBasedTimer with the specified frames per second rate.
|
||||
|
||||
Args:
|
||||
fps (int, optional): The frame rate of the video stream. Defaults to 30.
|
||||
fps (int): The frame rate of the video stream. Defaults to 30.
|
||||
"""
|
||||
self.fps = fps
|
||||
self.frame_id = 0
|
||||
|
||||
+66
-59
@@ -9,93 +9,100 @@ detection and Supervision for tracking and annotation.
|
||||
|
||||
- clone repository and navigate to example directory
|
||||
|
||||
```bash
|
||||
git clone https://github.com/roboflow/supervision.git
|
||||
cd supervision/examples/tracking
|
||||
```
|
||||
```bash
|
||||
git clone --depth 1 -b develop https://github.com/roboflow/supervision.git
|
||||
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
|
||||
```
|
||||
```bash
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
```
|
||||
|
||||
- install required dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## 🛠️ script arguments
|
||||
|
||||
- ultralytics
|
||||
|
||||
- `--source_weights_path`: Required. Specifies the path to the YOLO model's weights
|
||||
file, which is essential for the object detection process. This file contains the data
|
||||
that the model uses to identify objects in the video.
|
||||
- `--source_weights_path`: Required. Specifies the path to the YOLO model's weights
|
||||
file, which is essential for the object detection process. This file contains the data
|
||||
that the model uses to identify objects in the video.
|
||||
|
||||
- `--source_video_path`: Required. The path to the source video file to be processed.
|
||||
This is the video on which object detection and annotation will be performed.
|
||||
- `--target_video_path`: Required. The path where the processed video, with annotations
|
||||
added, will be saved. This is your output video file.
|
||||
- `--confidence_threshold` (optional): Sets the confidence level at which the model
|
||||
identifies objects in the video. Default is `0.3`. A higher threshold makes the model
|
||||
more selective, while a lower threshold makes it more inclusive in identifying objects.
|
||||
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
|
||||
for the model, defaulting to `0.7`. This parameter helps in differentiating between
|
||||
distinct objects, especially in crowded scenes.
|
||||
- `--source_video_path`: Required. The path to the source video file to be processed.
|
||||
This is the video on which object detection and annotation will be performed.
|
||||
|
||||
- `--target_video_path`: Required. The path where the processed video, with annotations
|
||||
added, will be saved. This is your output video file.
|
||||
|
||||
- `--confidence_threshold` (optional): Sets the confidence level at which the model
|
||||
identifies objects in the video. Default is `0.3`. A higher threshold makes the model
|
||||
more selective, while a lower threshold makes it more inclusive in identifying objects.
|
||||
|
||||
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
|
||||
for the model, defaulting to `0.7`. This parameter helps in differentiating between
|
||||
distinct objects, especially in crowded scenes.
|
||||
|
||||
- inference
|
||||
|
||||
- `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided
|
||||
directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment
|
||||
variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key)
|
||||
to acquire your `API KEY`.
|
||||
- `--model_id` (optional): Designates the Roboflow model ID to be used. The default
|
||||
value is `"yolov8x-1280"`.
|
||||
- `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided
|
||||
directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment
|
||||
variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key)
|
||||
to acquire your `API KEY`.
|
||||
|
||||
- `--source_video_path`: Required. The path to the source video file to be processed.
|
||||
This is the video on which object detection and annotation will be performed.
|
||||
- `--target_video_path`: Required. The path where the processed video, with annotations
|
||||
added, will be saved. This is your output video file.
|
||||
- `--confidence_threshold` (optional): Sets the confidence level at which the model
|
||||
identifies objects in the video. Default is `0.3`. A higher threshold makes the model
|
||||
more selective, while a lower threshold makes it more inclusive in identifying objects.
|
||||
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
|
||||
for the model, defaulting to `0.7`. This parameter helps in differentiating between
|
||||
distinct objects, especially in crowded scenes.
|
||||
- `--model_id` (optional): Designates the Roboflow model ID to be used. The default
|
||||
value is `"yolov8x-1280"`.
|
||||
|
||||
- `--source_video_path`: Required. The path to the source video file to be processed.
|
||||
This is the video on which object detection and annotation will be performed.
|
||||
|
||||
- `--target_video_path`: Required. The path where the processed video, with annotations
|
||||
added, will be saved. This is your output video file.
|
||||
|
||||
- `--confidence_threshold` (optional): Sets the confidence level at which the model
|
||||
identifies objects in the video. Default is `0.3`. A higher threshold makes the model
|
||||
more selective, while a lower threshold makes it more inclusive in identifying objects.
|
||||
|
||||
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
|
||||
for the model, defaulting to `0.7`. This parameter helps in differentiating between
|
||||
distinct objects, especially in crowded scenes.
|
||||
|
||||
## ⚙️ run
|
||||
|
||||
- inference
|
||||
|
||||
```bash
|
||||
python inference_example.py \
|
||||
--roboflow_api_key <ROBOFLOW API KEY> \
|
||||
--source_video_path input.mp4 \
|
||||
--target_video_path tracking_result.mp4
|
||||
```
|
||||
```bash
|
||||
python inference_example.py \
|
||||
--roboflow_api_key <ROBOFLOW API KEY> \
|
||||
--source_video_path input.mp4 \
|
||||
--target_video_path tracking_result.mp4
|
||||
```
|
||||
|
||||
- ultralytics
|
||||
|
||||
```bash
|
||||
python ultralytics_example.py \
|
||||
--source_weights_path yolov8s.pt \
|
||||
--source_video_path input.mp4 \
|
||||
--target_video_path tracking_result.mp4
|
||||
```
|
||||
```bash
|
||||
python ultralytics_example.py \
|
||||
--source_weights_path yolov8s.pt \
|
||||
--source_video_path input.mp4 \
|
||||
--target_video_path tracking_result.mp4
|
||||
```
|
||||
|
||||
## © license
|
||||
|
||||
This demo integrates two main components, each with its own licensing:
|
||||
|
||||
- ultralytics: The object detection model used in this demo, YOLOv8, is distributed
|
||||
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
|
||||
You can find more details about this license here.
|
||||
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
|
||||
You can find more details about this license here.
|
||||
|
||||
- supervision: The analytics code that powers the zone-based analysis in this demo is
|
||||
based on the Supervision library, which is licensed under the
|
||||
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
|
||||
makes the Supervision part of the code fully open source and freely usable in your
|
||||
projects.
|
||||
based on the Supervision library, which is licensed under the
|
||||
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
|
||||
makes the Supervision part of the code fully open source and freely usable in your
|
||||
projects.
|
||||
|
||||
@@ -18,7 +18,7 @@ def process_video(
|
||||
model = get_roboflow_model(model_id=model_id, api_key=roboflow_api_key)
|
||||
|
||||
tracker = sv.ByteTrack()
|
||||
box_annotator = sv.BoundingBoxAnnotator()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
label_annotator = sv.LabelAnnotator()
|
||||
frame_generator = sv.get_video_frames_generator(source_path=source_video_path)
|
||||
video_info = sv.VideoInfo.from_video_path(video_path=source_video_path)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
inference==0.9.17
|
||||
supervision==0.19.0
|
||||
tqdm
|
||||
ultralytics
|
||||
ultralytics<=8.3.40
|
||||
|
||||
@@ -16,7 +16,7 @@ def process_video(
|
||||
model = YOLO(source_weights_path)
|
||||
|
||||
tracker = sv.ByteTrack()
|
||||
box_annotator = sv.BoundingBoxAnnotator()
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
label_annotator = sv.LabelAnnotator()
|
||||
frame_generator = sv.get_video_frames_generator(source_path=source_video_path)
|
||||
video_info = sv.VideoInfo.from_video_path(video_path=source_video_path)
|
||||
|
||||
@@ -12,105 +12,112 @@ https://github.com/roboflow/supervision/assets/26109316/c9436828-9fbf-4c25-ae8c-
|
||||
|
||||
- clone repository and navigate to example directory
|
||||
|
||||
```bash
|
||||
git clone https://github.com/roboflow/supervision.git
|
||||
cd supervision/examples/traffic_analysis
|
||||
```
|
||||
```bash
|
||||
git clone --depth 1 -b develop https://github.com/roboflow/supervision.git
|
||||
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
|
||||
```
|
||||
```bash
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
```
|
||||
|
||||
- install required dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
- download `traffic_analysis.pt` and `traffic_analysis.mov` files
|
||||
|
||||
```bash
|
||||
./setup.sh
|
||||
```
|
||||
```bash
|
||||
./setup.sh
|
||||
```
|
||||
|
||||
## 🛠️ script arguments
|
||||
|
||||
- ultralytics
|
||||
|
||||
- `--source_weights_path`: Required. Specifies the path to the YOLO model's weights
|
||||
file, which is essential for the object detection process. This file contains the
|
||||
data that the model uses to identify objects in the video.
|
||||
- `--source_weights_path`: Required. Specifies the path to the YOLO model's weights
|
||||
file, which is essential for the object detection process. This file contains the
|
||||
data that the model uses to identify objects in the video.
|
||||
|
||||
- `--source_video_path`: Required. The path to the source video file that will be
|
||||
analyzed. This is the input video on which traffic flow analysis will be performed.
|
||||
- `--target_video_path` (optional): The path to save the output video with
|
||||
annotations. If not specified, the processed video will be displayed in real-time
|
||||
without being saved.
|
||||
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO
|
||||
model to filter detections. Default is `0.3`. This determines how confident the
|
||||
model should be to recognize an object in the video.
|
||||
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
|
||||
for the model. Default is 0.7. This value is used to manage object detection
|
||||
accuracy, particularly in distinguishing between different objects.
|
||||
- `--source_video_path`: Required. The path to the source video file that will be
|
||||
analyzed. This is the input video on which traffic flow analysis will be performed.
|
||||
|
||||
- `--target_video_path` (optional): The path to save the output video with
|
||||
annotations. If not specified, the processed video will be displayed in real-time
|
||||
without being saved.
|
||||
|
||||
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO
|
||||
model to filter detections. Default is `0.3`. This determines how confident the
|
||||
model should be to recognize an object in the video.
|
||||
|
||||
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
|
||||
for the model. Default is 0.7. This value is used to manage object detection
|
||||
accuracy, particularly in distinguishing between different objects.
|
||||
|
||||
- inference
|
||||
|
||||
- `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided
|
||||
directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment
|
||||
variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key)
|
||||
to acquire your `API KEY`.
|
||||
- `--model_id` (optional): Designates the Roboflow model ID to be used. The default
|
||||
value is `"vehicle-count-in-drone-video/6"`.
|
||||
- `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided
|
||||
directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment
|
||||
variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key)
|
||||
to acquire your `API KEY`.
|
||||
|
||||
- `--source_video_path`: Required. The path to the source video file that will be
|
||||
analyzed. This is the input video on which traffic flow analysis will be performed.
|
||||
- `--target_video_path` (optional): The path to save the output video with
|
||||
annotations. If not specified, the processed video will be displayed in real-time
|
||||
without being saved.
|
||||
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO
|
||||
model to filter detections. Default is `0.3`. This determines how confident the
|
||||
model should be to recognize an object in the video.
|
||||
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
|
||||
for the model. Default is 0.7. This value is used to manage object detection
|
||||
accuracy, particularly in distinguishing between different objects.
|
||||
- `--model_id` (optional): Designates the Roboflow model ID to be used. The default
|
||||
value is `"vehicle-count-in-drone-video/6"`.
|
||||
|
||||
- `--source_video_path`: Required. The path to the source video file that will be
|
||||
analyzed. This is the input video on which traffic flow analysis will be performed.
|
||||
|
||||
- `--target_video_path` (optional): The path to save the output video with
|
||||
annotations. If not specified, the processed video will be displayed in real-time
|
||||
without being saved.
|
||||
|
||||
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO
|
||||
model to filter detections. Default is `0.3`. This determines how confident the
|
||||
model should be to recognize an object in the video.
|
||||
|
||||
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
|
||||
for the model. Default is 0.7. This value is used to manage object detection
|
||||
accuracy, particularly in distinguishing between different objects.
|
||||
|
||||
## ⚙️ run
|
||||
|
||||
- ultralytics
|
||||
|
||||
```bash
|
||||
python ultralytics_example.py \
|
||||
--source_weights_path data/traffic_analysis.pt \
|
||||
--source_video_path data/traffic_analysis.mov \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.5 \
|
||||
--target_video_path data/traffic_analysis_result.mov
|
||||
```
|
||||
```bash
|
||||
python ultralytics_example.py \
|
||||
--source_weights_path data/traffic_analysis.pt \
|
||||
--source_video_path data/traffic_analysis.mov \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.5 \
|
||||
--target_video_path data/traffic_analysis_result.mov
|
||||
```
|
||||
|
||||
- inference
|
||||
|
||||
```bash
|
||||
python inference_example.py \
|
||||
--roboflow_api_key <ROBOFLOW API KEY> \
|
||||
--source_video_path data/traffic_analysis.mov \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.5 \
|
||||
--target_video_path data/traffic_analysis_result.mov
|
||||
```
|
||||
```bash
|
||||
python inference_example.py \
|
||||
--roboflow_api_key <ROBOFLOW API KEY> \
|
||||
--source_video_path data/traffic_analysis.mov \
|
||||
--confidence_threshold 0.3 \
|
||||
--iou_threshold 0.5 \
|
||||
--target_video_path data/traffic_analysis_result.mov
|
||||
```
|
||||
|
||||
## © license
|
||||
|
||||
This demo integrates two main components, each with its own licensing:
|
||||
|
||||
- ultralytics: The object detection model used in this demo, YOLOv8, is distributed
|
||||
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
|
||||
You can find more details about this license here.
|
||||
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
|
||||
You can find more details about this license here.
|
||||
|
||||
- supervision: The analytics code that powers the zone-based analysis in this demo is
|
||||
based on the Supervision library, which is licensed under the
|
||||
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
|
||||
makes the Supervision part of the code fully open source and freely usable in your
|
||||
projects.
|
||||
based on the Supervision library, which is licensed under the
|
||||
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
|
||||
makes the Supervision part of the code fully open source and freely usable in your
|
||||
projects.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import argparse
|
||||
import os
|
||||
from typing import Dict, Iterable, List, Set
|
||||
from typing import Dict, Iterable, List, Optional, Set
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
@@ -77,7 +77,7 @@ class VideoProcessor:
|
||||
roboflow_api_key: str,
|
||||
model_id: str,
|
||||
source_video_path: str,
|
||||
target_video_path: str = None,
|
||||
target_video_path: Optional[str] = None,
|
||||
confidence_threshold: float = 0.3,
|
||||
iou_threshold: float = 0.7,
|
||||
) -> None:
|
||||
|
||||
@@ -2,4 +2,4 @@ gdown
|
||||
inference==0.9.17
|
||||
supervision>=0.20.0
|
||||
tqdm
|
||||
ultralytics
|
||||
ultralytics<=8.3.40
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import argparse
|
||||
from typing import Dict, Iterable, List, Set
|
||||
from typing import Dict, Iterable, List, Optional, Set
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
@@ -74,7 +74,7 @@ class VideoProcessor:
|
||||
self,
|
||||
source_weights_path: str,
|
||||
source_video_path: str,
|
||||
target_video_path: str = None,
|
||||
target_video_path: Optional[str] = None,
|
||||
confidence_threshold: float = 0.3,
|
||||
iou_threshold: float = 0.7,
|
||||
) -> None:
|
||||
|
||||
+51
-49
@@ -33,7 +33,6 @@ extra_css:
|
||||
- stylesheets/extra.css
|
||||
- stylesheets/cookbooks-card.css
|
||||
|
||||
|
||||
nav:
|
||||
- Supervision: index.md
|
||||
- Learn:
|
||||
@@ -42,52 +41,59 @@ nav:
|
||||
- Filter Detections: how_to/filter_detections.md
|
||||
- Detect Small Objects: how_to/detect_small_objects.md
|
||||
- Track Objects on Video: how_to/track_objects.md
|
||||
- Process Datasets: how_to/process_datasets.md
|
||||
- Benchmark a Model: how_to/benchmark_a_model.md
|
||||
|
||||
- Reference - Code API:
|
||||
- Detection and Segmentation:
|
||||
- Core: detection/core.md
|
||||
- Annotators: detection/annotators.md
|
||||
- Metrics: detection/metrics.md
|
||||
- Double Detection Filter: detection/double_detection_filter.md
|
||||
- Utils: detection/utils.md
|
||||
- Keypoint Detection:
|
||||
- Core: keypoint/core.md
|
||||
- Annotators: keypoint/annotators.md
|
||||
- Classification:
|
||||
- Core: classification/core.md
|
||||
- Tools:
|
||||
- Line Zone: detection/tools/line_zone.md
|
||||
- Polygon Zone: detection/tools/polygon_zone.md
|
||||
- Inference Slicer: detection/tools/inference_slicer.md
|
||||
- Detection Smoother: detection/tools/smoother.md
|
||||
- Save Detections: detection/tools/save_detections.md
|
||||
- Trackers: trackers.md
|
||||
- Datasets:
|
||||
- Core: datasets/core.md
|
||||
- Utils: datasets/utils.md
|
||||
- Utils:
|
||||
- Video: utils/video.md
|
||||
- Image: utils/image.md
|
||||
- Iterables: utils/iterables.md
|
||||
- Notebook: utils/notebook.md
|
||||
- File: utils/file.md
|
||||
- Draw: utils/draw.md
|
||||
- Geometry: utils/geometry.md
|
||||
- Assets: assets.md
|
||||
- Detection and Segmentation:
|
||||
- Core: detection/core.md
|
||||
- Annotators: detection/annotators.md
|
||||
- Double Detection Filter: detection/double_detection_filter.md
|
||||
- Utils: detection/utils.md
|
||||
- Keypoint Detection:
|
||||
- Core: keypoint/core.md
|
||||
- Annotators: keypoint/annotators.md
|
||||
- Classification:
|
||||
- Core: classification/core.md
|
||||
- Tools:
|
||||
- Line Zone: detection/tools/line_zone.md
|
||||
- Polygon Zone: detection/tools/polygon_zone.md
|
||||
- Inference Slicer: detection/tools/inference_slicer.md
|
||||
- Detection Smoother: detection/tools/smoother.md
|
||||
- Save Detections: detection/tools/save_detections.md
|
||||
- Trackers: trackers.md
|
||||
- Datasets:
|
||||
- Core: datasets/core.md
|
||||
- Utils: datasets/utils.md
|
||||
- Metrics:
|
||||
- mAP: metrics/mean_average_precision.md
|
||||
- mAR: metrics/mean_average_recall.md
|
||||
- Precision: metrics/precision.md
|
||||
- Recall: metrics/recall.md
|
||||
- F1 Score: metrics/f1_score.md
|
||||
- Common Values: metrics/common_values.md
|
||||
- Legacy Metrics: detection/metrics.md
|
||||
- Utils:
|
||||
- Video: utils/video.md
|
||||
- Image: utils/image.md
|
||||
- Iterables: utils/iterables.md
|
||||
- Notebook: utils/notebook.md
|
||||
- File: utils/file.md
|
||||
- Draw: utils/draw.md
|
||||
- Geometry: utils/geometry.md
|
||||
- Assets: assets.md
|
||||
- Cookbooks: cookbooks.md
|
||||
- Cheatsheet: https://roboflow.github.io/cheatsheet-supervision/
|
||||
- Contribute:
|
||||
- Contributing: contributing.md
|
||||
- Code of Conduct: code_of_conduct.md
|
||||
- License: license.md
|
||||
- Contributing: contributing.md
|
||||
- Code of Conduct: code_of_conduct.md
|
||||
- License: license.md
|
||||
- Release Notes:
|
||||
- Changelog: changelog.md
|
||||
- Deprecated: deprecated.md
|
||||
|
||||
|
||||
- Changelog: changelog.md
|
||||
- Deprecated: deprecated.md
|
||||
|
||||
theme:
|
||||
name: 'material'
|
||||
name: "material"
|
||||
icon:
|
||||
edit: material/pencil
|
||||
logo: assets/supervision-lenny.png
|
||||
@@ -104,19 +110,18 @@ theme:
|
||||
palette:
|
||||
# Palette for light mode
|
||||
- scheme: default
|
||||
primary: 'custom'
|
||||
primary: "custom"
|
||||
toggle:
|
||||
icon: material/brightness-7
|
||||
name: Switch to dark mode
|
||||
|
||||
# Palette toggle for dark mode
|
||||
- scheme: slate
|
||||
primary: 'custom'
|
||||
primary: "custom"
|
||||
toggle:
|
||||
icon: material/brightness-4
|
||||
name: Switch to light mode
|
||||
|
||||
|
||||
font:
|
||||
text: Roboto
|
||||
code: Roboto Mono
|
||||
@@ -127,10 +132,10 @@ theme:
|
||||
plugins:
|
||||
- search
|
||||
- mkdocs-jupyter:
|
||||
kernel_name: python3
|
||||
execute: false
|
||||
include_source: True
|
||||
include_requirejs: true
|
||||
kernel_name: python3
|
||||
execute: false
|
||||
include_source: True
|
||||
include_requirejs: true
|
||||
- mkdocstrings:
|
||||
default_handler: python
|
||||
handlers:
|
||||
@@ -154,8 +159,6 @@ plugins:
|
||||
- git-revision-date-localized:
|
||||
enable_creation_date: true
|
||||
|
||||
|
||||
|
||||
markdown_extensions:
|
||||
- admonition
|
||||
- pymdownx.details
|
||||
@@ -175,7 +178,6 @@ markdown_extensions:
|
||||
anchor_linenums: true
|
||||
|
||||
extra_javascript:
|
||||
- "https://widget.kapa.ai/kapa-widget.bundle.js"
|
||||
- "javascripts/init_kapa_widget.js"
|
||||
- "javascripts/cookbooks-card.js"
|
||||
- "https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.8/purify.min.js"
|
||||
|
||||
Generated
-4316
File diff suppressed because it is too large
Load Diff
+69
-83
@@ -1,15 +1,16 @@
|
||||
[tool.poetry]
|
||||
[project]
|
||||
name = "supervision"
|
||||
version = "0.22.0"
|
||||
description = "A set of easy-to-use utils that will come in handy in any Computer Vision project"
|
||||
authors = ["Piotr Skalski <piotr.skalski92@gmail.com>"]
|
||||
maintainers = ["Piotr Skalski <piotr.skalski92@gmail.com>"]
|
||||
license = { text = "MIT" }
|
||||
version = "0.26.0rc4"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
packages = [{ include = "supervision" }]
|
||||
homepage = "https://github.com/roboflow/supervision"
|
||||
repository = "https://github.com/roboflow/supervision"
|
||||
documentation = "https://github.com/roboflow/supervision/blob/main/README.md"
|
||||
requires-python = ">=3.8"
|
||||
authors = [
|
||||
{ name = "Piotr Skalski", email = "piotr.skalski92@gmail.com" }
|
||||
]
|
||||
maintainers = [
|
||||
{ name = "Piotr Skalski", email = "piotr.skalski92@gmail.com" },
|
||||
]
|
||||
keywords = [
|
||||
"machine-learning",
|
||||
"deep-learning",
|
||||
@@ -19,7 +20,6 @@ keywords = [
|
||||
"AI",
|
||||
"Roboflow",
|
||||
]
|
||||
|
||||
classifiers = [
|
||||
'Development Status :: 4 - Beta',
|
||||
'Intended Audience :: Developers',
|
||||
@@ -37,87 +37,59 @@ classifiers = [
|
||||
'Operating System :: POSIX :: Linux',
|
||||
'Operating System :: MacOS',
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.8"
|
||||
numpy = ">=1.21.2"
|
||||
scipy = [
|
||||
{ version = "1.10.0", python = "<3.9" },
|
||||
{ version = "^1.10.0", python = ">=3.9" },
|
||||
]
|
||||
matplotlib = ">=3.6.0"
|
||||
pyyaml = ">=5.3"
|
||||
defusedxml = "^0.7.1"
|
||||
opencv-python = { version = ">=4.5.5.64", optional = true }
|
||||
opencv-python-headless = ">=4.5.5.64"
|
||||
requests = { version = ">=2.26.0,<=2.32.3", optional = true }
|
||||
tqdm = { version = ">=4.62.3,<=4.66.4", optional = true }
|
||||
pillow = ">=9.4"
|
||||
|
||||
[tool.poetry.extras]
|
||||
desktop = ["opencv-python"]
|
||||
assets = ["requests", "tqdm"]
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
twine = "^5.1.1"
|
||||
pytest = ">=7.2.2,<9.0.0"
|
||||
wheel = ">=0.40,<0.44"
|
||||
build = ">=0.10,<1.3"
|
||||
ruff = ">=0.1.0"
|
||||
mypy = "^1.4.1"
|
||||
pre-commit = "^3.3.3"
|
||||
tox = "^4.11.4"
|
||||
notebook = ">=6.5.3,<8.0.0"
|
||||
ipywidgets = "^8.1.1"
|
||||
jupytext = "^1.16.1"
|
||||
nbconvert = "^7.14.2"
|
||||
docutils = [
|
||||
{ version = "^0.20.1", python = "<3.9" },
|
||||
{ version = "^0.21.1", python = ">=3.9" },
|
||||
dependencies = [
|
||||
"numpy>=1.21.2",
|
||||
"scipy>=1.10.0",
|
||||
"matplotlib>=3.6.0",
|
||||
"pyyaml>=5.3",
|
||||
"defusedxml>=0.7.1",
|
||||
"pillow>=9.4",
|
||||
"requests>=2.26.0",
|
||||
"tqdm>=4.62.3",
|
||||
"opencv-python>=4.5.5.64"
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/roboflow/supervision"
|
||||
Repository = "https://github.com/roboflow/supervision"
|
||||
Documentation = "https://supervision.roboflow.com/latest/"
|
||||
|
||||
[tool.poetry.group.docs.dependencies]
|
||||
mkdocs-material = { extras = ["imaging"], version = "^9.5.5" }
|
||||
mkdocstrings = { extras = ["python"], version = ">=0.20,<0.26" }
|
||||
mike = "^2.0.0"
|
||||
# For Documentation Development use Python 3.10 or above
|
||||
# Use Latest mkdocs-jupyter min 0.24.6 for Jupyter Notebook Theme support
|
||||
mkdocs-jupyter = "^0.24.3"
|
||||
mkdocs-git-committers-plugin-2 = "^2.2.3"
|
||||
mkdocs-git-revision-date-localized-plugin = "^1.2.4"
|
||||
|
||||
[tool.isort]
|
||||
line_length = 88
|
||||
profile = "black"
|
||||
[project.optional-dependencies]
|
||||
metrics = [
|
||||
"pandas>=2.0.0",
|
||||
]
|
||||
dev = [
|
||||
"pytest>=7.2.2,<9.0.0",
|
||||
"tox>=4.11.4",
|
||||
"notebook>=6.5.3,<8.0.0",
|
||||
"ipywidgets>=8.1.1",
|
||||
"jupytext>=1.16.1",
|
||||
"nbconvert>=7.14.2",
|
||||
"docutils!=0.21"
|
||||
]
|
||||
docs = [
|
||||
"mkdocs-material[imaging]>=9.5.5",
|
||||
"mkdocstrings>=0.25.2,<0.27.0",
|
||||
"mkdocstrings-python>=1.10.9",
|
||||
"mike>=2.0.0",
|
||||
"mkdocs-jupyter>=0.24.3",
|
||||
"mkdocs-git-committers-plugin-2>=2.4.1; python_version >= '3.8' and python_version < '4'",
|
||||
"mkdocs-git-revision-date-localized-plugin>=1.2.4"
|
||||
]
|
||||
build = [
|
||||
"twine>=5.1.1,<7.0.0",
|
||||
"wheel>=0.40,<0.46",
|
||||
"build>=0.10,<1.3"
|
||||
]
|
||||
|
||||
[tool.bandit]
|
||||
target = ["test", "supervision"]
|
||||
tests = ["B201", "B301", "B318", "B314", "B303", "B413", "B412", "B410"]
|
||||
tests = ["B201", "B301", "B318", "B314", "B303", "B413", "B412"]
|
||||
|
||||
[tool.autoflake]
|
||||
check = true
|
||||
imports = ["cv2", "supervision"]
|
||||
|
||||
[tool.black]
|
||||
target-version = ["py38"]
|
||||
line-length = 88
|
||||
include = '\.pyi?$'
|
||||
exclude = '''
|
||||
/(
|
||||
\.git
|
||||
| \.hg
|
||||
| \.mypy_cache
|
||||
| \.tox
|
||||
| \.venv
|
||||
| _build
|
||||
| buck-out
|
||||
| build
|
||||
| dist
|
||||
| docs
|
||||
)/
|
||||
'''
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py38"
|
||||
|
||||
@@ -154,7 +126,7 @@ indent-width = 4
|
||||
|
||||
[tool.ruff.lint]
|
||||
# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default.
|
||||
select = ["E", "F"]
|
||||
select = ["E", "F", "I", "A", "Q", "W", "RUF"]
|
||||
ignore = []
|
||||
# Allow autofix for all enabled rules (when `--fix`) is provided.
|
||||
fixable = [
|
||||
@@ -223,6 +195,10 @@ convention = "google"
|
||||
# Flag errors (`C901`) whenever the complexity level exceeds 5.
|
||||
max-complexity = 20
|
||||
|
||||
[tool.ruff.lint.isort]
|
||||
order-by-type = true
|
||||
no-sections = false
|
||||
|
||||
[tool.ruff.format]
|
||||
# Like Black, use double quotes for strings.
|
||||
quote-style = "double"
|
||||
@@ -236,12 +212,22 @@ skip-magic-trailing-comma = false
|
||||
# Like Black, automatically detect the appropriate line ending.
|
||||
line-ending = "auto"
|
||||
|
||||
[tool.codespell]
|
||||
skip = "*.ipynb"
|
||||
count = true
|
||||
quiet-level = 3
|
||||
ignore-words-list = "STrack,sTrack,strack"
|
||||
|
||||
[tool.setuptools]
|
||||
include-package-data = false
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
include = ["supervision*"]
|
||||
exclude = ["docs*", "test*", "examples*"]
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
supervision = ["py.typed"]
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
requires = ["setuptools >= 61.0"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
+11
-6
@@ -6,14 +6,19 @@ It assumes you already have the code changes, as well as a draft of the release
|
||||
|
||||
1. Make sure you have all required changes were merged into `develop`.
|
||||
2. Create and merge a PR, merging `develop` into `main`, containing:
|
||||
- A commit that updates the project version in `pyproject.toml`.
|
||||
- All changes made during the release.
|
||||
- A commit that updates the project version in `pyproject.toml`.
|
||||
- All changes made during the release.
|
||||
3. Tag the commit with the new supervision version.
|
||||
- Upon pushing the tag, the [PyPi](https://pypi.org/project/supervision/) should update to the new version. Check this!
|
||||
- make sure to pull from `main` !
|
||||
- Verify that the latest merge commits exists. `git log`.
|
||||
- Run `git tag x.y.z`, with your version
|
||||
- Check with `git log`.
|
||||
- Run `git push origin --tags`
|
||||
- Upon pushing the tag, the [PyPi](https://pypi.org/project/supervision/) should update to the new version. Check this!
|
||||
4. Open and merge a PR, merging `main` into `develop`.
|
||||
5. Update the docs by running the [Supervision Release Documentation Workflow 📚](https://github.com/roboflow/supervision/actions/workflows/publish-release-docs.yml) workflow from GitHub.
|
||||
- Select the `main` branch from the dropdown.
|
||||
6. Create a release on GitHub.
|
||||
- Go to releases
|
||||
- Assign the release notes to the tag created in step 3.
|
||||
- Publish the release.
|
||||
- Go to releases
|
||||
- Assign the release notes to the tag created in step 3.
|
||||
- Publish the release.
|
||||
|
||||
+120
-3
@@ -7,17 +7,19 @@ except importlib_metadata.PackageNotFoundError:
|
||||
__version__ = "development"
|
||||
|
||||
from supervision.annotators.core import (
|
||||
BackgroundOverlayAnnotator,
|
||||
BlurAnnotator,
|
||||
BoundingBoxAnnotator,
|
||||
BoxAnnotator,
|
||||
BoxCornerAnnotator,
|
||||
CircleAnnotator,
|
||||
ColorAnnotator,
|
||||
ComparisonAnnotator,
|
||||
CropAnnotator,
|
||||
DotAnnotator,
|
||||
EllipseAnnotator,
|
||||
HaloAnnotator,
|
||||
HeatMapAnnotator,
|
||||
IconAnnotator,
|
||||
LabelAnnotator,
|
||||
MaskAnnotator,
|
||||
OrientedBoxAnnotator,
|
||||
@@ -38,8 +40,11 @@ from supervision.dataset.core import (
|
||||
)
|
||||
from supervision.dataset.utils import mask_to_rle, rle_to_mask
|
||||
from supervision.detection.core import Detections
|
||||
from supervision.detection.line_zone import LineZone, LineZoneAnnotator
|
||||
from supervision.detection.lmm import LMM
|
||||
from supervision.detection.line_zone import (
|
||||
LineZone,
|
||||
LineZoneAnnotator,
|
||||
LineZoneAnnotatorMulticlass,
|
||||
)
|
||||
from supervision.detection.overlap_filter import (
|
||||
OverlapFilter,
|
||||
box_non_max_merge,
|
||||
@@ -63,17 +68,22 @@ from supervision.detection.utils import (
|
||||
mask_to_xyxy,
|
||||
move_boxes,
|
||||
move_masks,
|
||||
oriented_box_iou_batch,
|
||||
pad_boxes,
|
||||
polygon_to_mask,
|
||||
polygon_to_xyxy,
|
||||
scale_boxes,
|
||||
xcycwh_to_xyxy,
|
||||
xywh_to_xyxy,
|
||||
xyxy_to_polygons,
|
||||
xyxy_to_xywh,
|
||||
)
|
||||
from supervision.detection.vlm import LMM, VLM
|
||||
from supervision.draw.color import Color, ColorPalette
|
||||
from supervision.draw.utils import (
|
||||
calculate_optimal_line_thickness,
|
||||
calculate_optimal_text_scale,
|
||||
draw_filled_polygon,
|
||||
draw_filled_rectangle,
|
||||
draw_image,
|
||||
draw_line,
|
||||
@@ -110,3 +120,110 @@ from supervision.utils.video import (
|
||||
get_video_frames_generator,
|
||||
process_video,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"LMM",
|
||||
"BackgroundOverlayAnnotator",
|
||||
"BaseDataset",
|
||||
"BlurAnnotator",
|
||||
"BoxAnnotator",
|
||||
"BoxCornerAnnotator",
|
||||
"ByteTrack",
|
||||
"CSVSink",
|
||||
"CircleAnnotator",
|
||||
"ClassificationDataset",
|
||||
"Classifications",
|
||||
"Color",
|
||||
"ColorAnnotator",
|
||||
"ColorLookup",
|
||||
"ColorPalette",
|
||||
"ComparisonAnnotator",
|
||||
"ConfusionMatrix",
|
||||
"CropAnnotator",
|
||||
"DetectionDataset",
|
||||
"Detections",
|
||||
"DetectionsSmoother",
|
||||
"DotAnnotator",
|
||||
"EdgeAnnotator",
|
||||
"EllipseAnnotator",
|
||||
"FPSMonitor",
|
||||
"HaloAnnotator",
|
||||
"HeatMapAnnotator",
|
||||
"IconAnnotator",
|
||||
"ImageSink",
|
||||
"InferenceSlicer",
|
||||
"JSONSink",
|
||||
"KeyPoints",
|
||||
"LabelAnnotator",
|
||||
"LineZone",
|
||||
"LineZoneAnnotator",
|
||||
"LineZoneAnnotatorMulticlass",
|
||||
"MaskAnnotator",
|
||||
"MeanAveragePrecision",
|
||||
"OrientedBoxAnnotator",
|
||||
"OverlapFilter",
|
||||
"PercentageBarAnnotator",
|
||||
"PixelateAnnotator",
|
||||
"Point",
|
||||
"PolygonAnnotator",
|
||||
"PolygonZone",
|
||||
"PolygonZoneAnnotator",
|
||||
"Position",
|
||||
"Rect",
|
||||
"RichLabelAnnotator",
|
||||
"RoundBoxAnnotator",
|
||||
"TraceAnnotator",
|
||||
"TriangleAnnotator",
|
||||
"VertexAnnotator",
|
||||
"VertexLabelAnnotator",
|
||||
"VideoInfo",
|
||||
"VideoSink",
|
||||
"box_iou_batch",
|
||||
"box_non_max_merge",
|
||||
"box_non_max_suppression",
|
||||
"calculate_masks_centroids",
|
||||
"calculate_optimal_line_thickness",
|
||||
"calculate_optimal_text_scale",
|
||||
"clip_boxes",
|
||||
"contains_holes",
|
||||
"contains_multiple_segments",
|
||||
"create_tiles",
|
||||
"crop_image",
|
||||
"cv2_to_pillow",
|
||||
"draw_filled_polygon",
|
||||
"draw_filled_rectangle",
|
||||
"draw_image",
|
||||
"draw_line",
|
||||
"draw_polygon",
|
||||
"draw_rectangle",
|
||||
"draw_text",
|
||||
"filter_polygons_by_area",
|
||||
"get_polygon_center",
|
||||
"get_video_frames_generator",
|
||||
"letterbox_image",
|
||||
"list_files_with_extensions",
|
||||
"mask_iou_batch",
|
||||
"mask_non_max_suppression",
|
||||
"mask_to_polygons",
|
||||
"mask_to_rle",
|
||||
"mask_to_xyxy",
|
||||
"move_boxes",
|
||||
"move_masks",
|
||||
"oriented_box_iou_batch",
|
||||
"overlay_image",
|
||||
"pad_boxes",
|
||||
"pillow_to_cv2",
|
||||
"plot_image",
|
||||
"plot_images_grid",
|
||||
"polygon_to_mask",
|
||||
"polygon_to_xyxy",
|
||||
"process_video",
|
||||
"resize_image",
|
||||
"rle_to_mask",
|
||||
"scale_boxes",
|
||||
"scale_image",
|
||||
"xcycwh_to_xyxy",
|
||||
"xywh_to_xyxy",
|
||||
"xyxy_to_polygons",
|
||||
"xyxy_to_xywh",
|
||||
]
|
||||
|
||||
+838
-234
File diff suppressed because it is too large
Load Diff
@@ -51,14 +51,17 @@ def resolve_color_idx(
|
||||
if detections.class_id is None:
|
||||
raise ValueError(
|
||||
"Could not resolve color by class because "
|
||||
"Detections do not have class_id"
|
||||
"Detections do not have class_id. If using an annotator, "
|
||||
"try setting color_lookup to sv.ColorLookup.INDEX or "
|
||||
"sv.ColorLookup.TRACK."
|
||||
)
|
||||
return detections.class_id[detection_idx]
|
||||
elif color_lookup == ColorLookup.TRACK:
|
||||
if detections.tracker_id is None:
|
||||
raise ValueError(
|
||||
"Could not resolve color by track because "
|
||||
"Detections do not have tracker_id"
|
||||
"Detections do not have tracker_id. Did you call "
|
||||
"tracker.update_with_detections(...) before annotating?"
|
||||
)
|
||||
return detections.tracker_id[detection_idx]
|
||||
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
from supervision.assets.downloader import download_assets
|
||||
from supervision.assets.list import VideoAssets
|
||||
|
||||
__all__ = ["VideoAssets", "download_assets"]
|
||||
|
||||
@@ -4,19 +4,10 @@ from pathlib import Path
|
||||
from shutil import copyfileobj
|
||||
from typing import Union
|
||||
|
||||
from supervision.assets.list import VIDEO_ASSETS, VideoAssets
|
||||
from requests import get
|
||||
from tqdm.auto import tqdm
|
||||
|
||||
try:
|
||||
from requests import get
|
||||
from tqdm.auto import tqdm
|
||||
except ImportError:
|
||||
raise ValueError(
|
||||
"\n"
|
||||
"Please install requests and tqdm to download assets \n"
|
||||
"or install supervision with assets \n"
|
||||
"pip install supervision[assets] \n"
|
||||
"\n"
|
||||
)
|
||||
from supervision.assets.list import VIDEO_ASSETS, VideoAssets
|
||||
|
||||
|
||||
def is_md5_hash_matching(filename: str, original_md5_hash: str) -> bool:
|
||||
|
||||
@@ -20,6 +20,7 @@ class VideoAssets(Enum):
|
||||
| `PEOPLE_WALKING` | `people-walking.mp4` | [Link](https://media.roboflow.com/supervision/video-examples/people-walking.mp4) |
|
||||
| `BEACH` | `beach-1.mp4` | [Link](https://media.roboflow.com/supervision/video-examples/beach-1.mp4) |
|
||||
| `BASKETBALL` | `basketball-1.mp4` | [Link](https://media.roboflow.com/supervision/video-examples/basketball-1.mp4) |
|
||||
| `SKIING` | `skiing.mp4` | [Link](https://media.roboflow.com/supervision/video-examples/skiing.mp4) |
|
||||
""" # noqa: E501 // docs
|
||||
|
||||
VEHICLES = "vehicles.mp4"
|
||||
@@ -31,6 +32,7 @@ class VideoAssets(Enum):
|
||||
PEOPLE_WALKING = "people-walking.mp4"
|
||||
BEACH = "beach-1.mp4"
|
||||
BASKETBALL = "basketball-1.mp4"
|
||||
SKIING = "skiing.mp4"
|
||||
|
||||
@classmethod
|
||||
def list(cls):
|
||||
@@ -74,4 +76,8 @@ VIDEO_ASSETS: Dict[str, Tuple[str, str]] = {
|
||||
f"{BASE_VIDEO_URL}{VideoAssets.BASKETBALL.value}",
|
||||
"60d94a3c7c47d16f09d342b088012ecc",
|
||||
),
|
||||
VideoAssets.SKIING.value: (
|
||||
f"{BASE_VIDEO_URL}{VideoAssets.SKIING.value}",
|
||||
"d30987cbab1bbc5934199cdd1b293119",
|
||||
),
|
||||
}
|
||||
|
||||
+22
-35
@@ -43,7 +43,10 @@ class BaseDataset(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def split(
|
||||
self, split_ratio=0.8, random_state=None, shuffle: bool = True
|
||||
self,
|
||||
split_ratio: float = 0.8,
|
||||
random_state: Optional[int] = None,
|
||||
shuffle: bool = True,
|
||||
) -> Tuple[BaseDataset, BaseDataset]:
|
||||
pass
|
||||
|
||||
@@ -174,18 +177,21 @@ class DetectionDataset(BaseDataset):
|
||||
return True
|
||||
|
||||
def split(
|
||||
self, split_ratio=0.8, random_state=None, shuffle: bool = True
|
||||
self,
|
||||
split_ratio: float = 0.8,
|
||||
random_state: Optional[int] = None,
|
||||
shuffle: bool = True,
|
||||
) -> Tuple[DetectionDataset, DetectionDataset]:
|
||||
"""
|
||||
Splits the dataset into two parts (training and testing)
|
||||
using the provided split_ratio.
|
||||
|
||||
Args:
|
||||
split_ratio (float, optional): The ratio of the training
|
||||
split_ratio (float): The ratio of the training
|
||||
set to the entire dataset.
|
||||
random_state (int, optional): The seed for the random number generator.
|
||||
random_state (Optional[int]): The seed for the random number generator.
|
||||
This is used for reproducibility.
|
||||
shuffle (bool, optional): Whether to shuffle the data before splitting.
|
||||
shuffle (bool): Whether to shuffle the data before splitting.
|
||||
|
||||
Returns:
|
||||
Tuple[DetectionDataset, DetectionDataset]: A tuple containing
|
||||
@@ -396,7 +402,7 @@ class DetectionDataset(BaseDataset):
|
||||
images_directory_path (str): Path to the directory containing the images.
|
||||
annotations_directory_path (str): Path to the directory
|
||||
containing the PASCAL VOC XML annotations.
|
||||
force_masks (bool, optional): If True, forces masks to
|
||||
force_masks (bool): If True, forces masks to
|
||||
be loaded for all annotations, regardless of whether they are present.
|
||||
|
||||
Returns:
|
||||
@@ -455,10 +461,10 @@ class DetectionDataset(BaseDataset):
|
||||
containing the YOLO annotation files.
|
||||
data_yaml_path (str): The path to the data
|
||||
YAML file containing class information.
|
||||
force_masks (bool, optional): If True, forces
|
||||
force_masks (bool): If True, forces
|
||||
masks to be loaded for all annotations,
|
||||
regardless of whether they are present.
|
||||
is_obb (bool, optional): If True, loads the annotations in OBB format.
|
||||
is_obb (bool): If True, loads the annotations in OBB format.
|
||||
OBB annotations are defined as `[class_id, x, y, x, y, x, y, x, y]`,
|
||||
where pairs of [x, y] are box corners.
|
||||
|
||||
@@ -565,7 +571,7 @@ class DetectionDataset(BaseDataset):
|
||||
images_directory_path (str): The path to the
|
||||
directory containing the images.
|
||||
annotations_path (str): The path to the json annotation files.
|
||||
force_masks (bool, optional): If True,
|
||||
force_masks (bool): If True,
|
||||
forces masks to be loaded for all annotations,
|
||||
regardless of whether they are present.
|
||||
|
||||
@@ -699,28 +705,6 @@ class ClassificationDataset(BaseDataset):
|
||||
"a list of paths `List[str]` instead."
|
||||
)
|
||||
|
||||
@property
|
||||
@deprecated(
|
||||
"`DetectionDataset.images` property is deprecated and will be removed in "
|
||||
"`supervision-0.26.0`. Iterate with `for path, image, annotation in dataset:` "
|
||||
"instead."
|
||||
)
|
||||
def images(self) -> Dict[str, np.ndarray]:
|
||||
"""
|
||||
Load all images to memory and return them as a dictionary.
|
||||
|
||||
!!! warning
|
||||
|
||||
Only use this when you need all images at once.
|
||||
It is much more memory-efficient to initialize dataset with
|
||||
image paths and use `for path, image, annotation in dataset:`.
|
||||
"""
|
||||
if self._images_in_memory:
|
||||
return self._images_in_memory
|
||||
|
||||
images = {image_path: cv2.imread(image_path) for image_path in self.image_paths}
|
||||
return images
|
||||
|
||||
def _get_image(self, image_path: str) -> np.ndarray:
|
||||
"""Assumes that image is in dataset"""
|
||||
if self._images_in_memory:
|
||||
@@ -777,18 +761,21 @@ class ClassificationDataset(BaseDataset):
|
||||
return True
|
||||
|
||||
def split(
|
||||
self, split_ratio=0.8, random_state=None, shuffle: bool = True
|
||||
self,
|
||||
split_ratio: float = 0.8,
|
||||
random_state: Optional[int] = None,
|
||||
shuffle: bool = True,
|
||||
) -> Tuple[ClassificationDataset, ClassificationDataset]:
|
||||
"""
|
||||
Splits the dataset into two parts (training and testing)
|
||||
using the provided split_ratio.
|
||||
|
||||
Args:
|
||||
split_ratio (float, optional): The ratio of the training
|
||||
split_ratio (float): The ratio of the training
|
||||
set to the entire dataset.
|
||||
random_state (int, optional): The seed for the
|
||||
random_state (Optional[int]): The seed for the
|
||||
random number generator. This is used for reproducibility.
|
||||
shuffle (bool, optional): Whether to shuffle the data before splitting.
|
||||
shuffle (bool): Whether to shuffle the data before splitting.
|
||||
|
||||
Returns:
|
||||
Tuple[ClassificationDataset, ClassificationDataset]: A tuple containing
|
||||
|
||||
@@ -147,7 +147,7 @@ def load_pascal_voc_annotations(
|
||||
images_directory_path (str): The path to the directory containing the images.
|
||||
annotations_directory_path (str): The path to the directory containing the
|
||||
PASCAL VOC annotation files.
|
||||
force_masks (bool, optional): If True, forces masks to be loaded for all
|
||||
force_masks (bool): If True, forces masks to be loaded for all
|
||||
annotations, regardless of whether they are present.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -2,8 +2,8 @@ import os
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
from supervision.config import ORIENTED_BOX_COORDINATES
|
||||
from supervision.dataset.utils import approximate_mask_with_polygons
|
||||
@@ -138,9 +138,9 @@ def load_yolo_annotations(
|
||||
containing the YOLO annotation files.
|
||||
data_yaml_path (str): The path to the data
|
||||
YAML file containing class information.
|
||||
force_masks (bool, optional): If True, forces masks to be loaded
|
||||
force_masks (bool): If True, forces masks to be loaded
|
||||
for all annotations, regardless of whether they are present.
|
||||
is_obb (bool, optional): If True, loads the annotations in OBB format.
|
||||
is_obb (bool): If True, loads the annotations in OBB format.
|
||||
OBB annotations are defined as `[class_id, x, y, x, y, x, y, x, y]`,
|
||||
where pairs of [x, y] are box corners.
|
||||
|
||||
@@ -153,7 +153,18 @@ def load_yolo_annotations(
|
||||
image_paths = [
|
||||
str(path)
|
||||
for path in list_files_with_extensions(
|
||||
directory=images_directory_path, extensions=["jpg", "jpeg", "png"]
|
||||
directory=images_directory_path,
|
||||
extensions=[
|
||||
"bmp",
|
||||
"dng",
|
||||
"jpg",
|
||||
"jpeg",
|
||||
"mpo",
|
||||
"png",
|
||||
"tif",
|
||||
"tiff",
|
||||
"webp",
|
||||
],
|
||||
)
|
||||
]
|
||||
|
||||
@@ -167,10 +178,16 @@ def load_yolo_annotations(
|
||||
annotations[image_path] = Detections.empty()
|
||||
continue
|
||||
|
||||
image = cv2.imread(image_path)
|
||||
# PIL is much faster than cv2 for checking image shape and mode: https://github.com/roboflow/supervision/issues/1554
|
||||
image = Image.open(image_path)
|
||||
lines = read_txt_file(file_path=annotation_path, skip_empty=True)
|
||||
h, w, _ = image.shape
|
||||
w, h = image.size
|
||||
resolution_wh = (w, h)
|
||||
if image.mode not in ("RGB", "L"):
|
||||
raise ValueError(
|
||||
f"Images must be 'RGB' or 'grayscale', \
|
||||
but {image_path} mode is '{image.mode}'."
|
||||
)
|
||||
|
||||
with_masks = _with_mask(lines=lines)
|
||||
with_masks = force_masks if force_masks else with_masks
|
||||
|
||||
@@ -55,7 +55,7 @@ def merge_class_lists(class_lists: List[List[str]]) -> List[str]:
|
||||
|
||||
for class_list in class_lists:
|
||||
for class_name in class_list:
|
||||
unique_classes.add(class_name.lower())
|
||||
unique_classes.add(class_name)
|
||||
|
||||
return sorted(list(unique_classes))
|
||||
|
||||
|
||||
+243
-69
@@ -1,36 +1,48 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from contextlib import suppress
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
from supervision.config import CLASS_NAME_DATA_FIELD, ORIENTED_BOX_COORDINATES
|
||||
from supervision.detection.lmm import (
|
||||
LMM,
|
||||
from_florence_2,
|
||||
from_paligemma,
|
||||
validate_lmm_parameters,
|
||||
from supervision.config import (
|
||||
CLASS_NAME_DATA_FIELD,
|
||||
ORIENTED_BOX_COORDINATES,
|
||||
)
|
||||
from supervision.detection.overlap_filter import (
|
||||
box_non_max_merge,
|
||||
box_non_max_suppression,
|
||||
mask_non_max_suppression,
|
||||
)
|
||||
from supervision.detection.tools.transformers import (
|
||||
process_transformers_detection_result,
|
||||
process_transformers_v4_segmentation_result,
|
||||
process_transformers_v5_segmentation_result,
|
||||
)
|
||||
from supervision.detection.utils import (
|
||||
box_iou_batch,
|
||||
calculate_masks_centroids,
|
||||
extract_ultralytics_masks,
|
||||
get_data_item,
|
||||
is_data_equal,
|
||||
is_metadata_equal,
|
||||
mask_to_xyxy,
|
||||
merge_data,
|
||||
merge_metadata,
|
||||
process_roboflow_result,
|
||||
xywh_to_xyxy,
|
||||
)
|
||||
from supervision.detection.vlm import (
|
||||
LMM,
|
||||
VLM,
|
||||
from_florence_2,
|
||||
from_paligemma,
|
||||
from_qwen_2_5_vl,
|
||||
validate_vlm_parameters,
|
||||
)
|
||||
from supervision.geometry.core import Position
|
||||
from supervision.utils.internal import get_instance_variables
|
||||
from supervision.utils.internal import deprecated, get_instance_variables
|
||||
from supervision.validators import validate_detections_fields
|
||||
|
||||
|
||||
@@ -40,7 +52,7 @@ class Detections:
|
||||
The `sv.Detections` class in the Supervision library standardizes results from
|
||||
various object detection and segmentation models into a consistent format. This
|
||||
class simplifies data manipulation and filtering, providing a uniform API for
|
||||
integration with Supervision [trackers](/trackers/), [annotators](/detection/annotators/), and [tools](/detection/tools/line_zone/).
|
||||
integration with Supervision [trackers](/trackers/), [annotators](/latest/detection/annotators/), and [tools](/detection/tools/line_zone/).
|
||||
|
||||
=== "Inference"
|
||||
|
||||
@@ -107,7 +119,7 @@ class Detections:
|
||||
xyxy (np.ndarray): An array of shape `(n, 4)` containing
|
||||
the bounding boxes coordinates in format `[x1, y1, x2, y2]`
|
||||
mask: (Optional[np.ndarray]): An array of shape
|
||||
`(n, H, W)` containing the segmentation masks.
|
||||
`(n, H, W)` containing the segmentation masks (`bool` data type).
|
||||
confidence (Optional[np.ndarray]): An array of shape
|
||||
`(n,)` containing the confidence scores of the detections.
|
||||
class_id (Optional[np.ndarray]): An array of shape
|
||||
@@ -117,6 +129,9 @@ class Detections:
|
||||
data (Dict[str, Union[np.ndarray, List]]): A dictionary containing additional
|
||||
data where each key is a string representing the data type, and the value
|
||||
is either a NumPy array or a list of corresponding data.
|
||||
metadata (Dict[str, Any]): A dictionary containing collection-level metadata
|
||||
that applies to the entire set of detections. This may include information such
|
||||
as the video name, camera parameters, timestamp, or other global metadata.
|
||||
""" # noqa: E501 // docs
|
||||
|
||||
xyxy: np.ndarray
|
||||
@@ -125,6 +140,7 @@ class Detections:
|
||||
class_id: Optional[np.ndarray] = None
|
||||
tracker_id: Optional[np.ndarray] = None
|
||||
data: Dict[str, Union[np.ndarray, List]] = field(default_factory=dict)
|
||||
metadata: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def __post_init__(self):
|
||||
validate_detections_fields(
|
||||
@@ -177,6 +193,7 @@ class Detections:
|
||||
np.array_equal(self.confidence, other.confidence),
|
||||
np.array_equal(self.tracker_id, other.tracker_id),
|
||||
is_data_equal(self.data, other.data),
|
||||
is_metadata_equal(self.metadata, other.metadata),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -244,11 +261,7 @@ class Detections:
|
||||
results = model(image)[0]
|
||||
detections = sv.Detections.from_ultralytics(results)
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
||||
Class names values can be accessed using `detections["class_name"]`.
|
||||
""" # noqa: E501 // docs
|
||||
"""
|
||||
|
||||
if hasattr(ultralytics_results, "obb") and ultralytics_results.obb is not None:
|
||||
class_id = ultralytics_results.obb.cls.cpu().numpy().astype(int)
|
||||
@@ -267,6 +280,14 @@ class Detections:
|
||||
},
|
||||
)
|
||||
|
||||
if hasattr(ultralytics_results, "boxes") and ultralytics_results.boxes is None:
|
||||
masks = extract_ultralytics_masks(ultralytics_results)
|
||||
return cls(
|
||||
xyxy=mask_to_xyxy(masks),
|
||||
mask=masks,
|
||||
class_id=np.arange(len(ultralytics_results)),
|
||||
)
|
||||
|
||||
class_id = ultralytics_results.boxes.cls.cpu().numpy().astype(int)
|
||||
class_names = np.array([ultralytics_results.names[i] for i in class_id])
|
||||
return cls(
|
||||
@@ -347,7 +368,7 @@ class Detections:
|
||||
result = model(img)
|
||||
detections = sv.Detections.from_tensorflow(result)
|
||||
```
|
||||
""" # noqa: E501 // docs
|
||||
"""
|
||||
|
||||
boxes = tensorflow_results["detection_boxes"][0].numpy()
|
||||
boxes[:, [0, 2]] *= resolution_wh[0]
|
||||
@@ -422,7 +443,7 @@ class Detections:
|
||||
result = inference_detector(model, image)
|
||||
detections = sv.Detections.from_mmdetection(result)
|
||||
```
|
||||
""" # noqa: E501 // docs
|
||||
"""
|
||||
|
||||
return cls(
|
||||
xyxy=mmdet_results.pred_instances.bboxes.cpu().numpy(),
|
||||
@@ -438,15 +459,19 @@ class Detections:
|
||||
cls, transformers_results: dict, id2label: Optional[Dict[int, str]] = None
|
||||
) -> Detections:
|
||||
"""
|
||||
Creates a Detections instance from object detection or segmentation
|
||||
Creates a Detections instance from object detection or panoptic, semantic
|
||||
and instance segmentation
|
||||
[Transformer](https://github.com/huggingface/transformers) inference result.
|
||||
|
||||
Args:
|
||||
transformers_results (dict): The output of Transformers model inference. A
|
||||
dictionary containing the `scores`, `labels`, `boxes` and `masks` keys.
|
||||
transformers_results (Union[dict, torch.Tensor]): Inference results from
|
||||
your Transformers model. This can be either a dictionary containing
|
||||
valuable outputs like `scores`, `labels`, `boxes`, `masks`,
|
||||
`segments_info`, and `segmentation`, or a `torch.Tensor` holding a
|
||||
segmentation map where values represent class IDs.
|
||||
id2label (Optional[Dict[int, str]]): A dictionary mapping class IDs to
|
||||
class names. If provided, the resulting Detections object will contain
|
||||
`class_name` data field with the class names.
|
||||
labels, typically part of the `transformers` model configuration. If
|
||||
provided, the resulting dictionary will include class names.
|
||||
|
||||
Returns:
|
||||
Detections: A new Detections object.
|
||||
@@ -477,46 +502,45 @@ class Detections:
|
||||
id2label=model.config.id2label
|
||||
)
|
||||
```
|
||||
"""
|
||||
|
||||
!!! tip
|
||||
if (
|
||||
transformers_results.__class__.__name__ == "Tensor"
|
||||
or "segmentation" in transformers_results
|
||||
):
|
||||
return cls(
|
||||
**process_transformers_v5_segmentation_result(
|
||||
transformers_results, id2label
|
||||
)
|
||||
)
|
||||
|
||||
Class names values can be accessed using `detections["class_name"]`.
|
||||
""" # noqa: E501 // docs
|
||||
if "masks" in transformers_results or "png_string" in transformers_results:
|
||||
return cls(
|
||||
**process_transformers_v4_segmentation_result(
|
||||
transformers_results, id2label
|
||||
)
|
||||
)
|
||||
|
||||
class_ids = transformers_results["labels"].cpu().detach().numpy().astype(int)
|
||||
data = {}
|
||||
if id2label is not None:
|
||||
class_names = np.array([id2label[class_id] for class_id in class_ids])
|
||||
data[CLASS_NAME_DATA_FIELD] = class_names
|
||||
if "boxes" in transformers_results:
|
||||
return cls(
|
||||
xyxy=transformers_results["boxes"].cpu().detach().numpy(),
|
||||
confidence=transformers_results["scores"].cpu().detach().numpy(),
|
||||
class_id=class_ids,
|
||||
data=data,
|
||||
)
|
||||
elif "masks" in transformers_results:
|
||||
masks = transformers_results["masks"].cpu().detach().numpy().astype(bool)
|
||||
return cls(
|
||||
xyxy=mask_to_xyxy(masks),
|
||||
mask=masks,
|
||||
confidence=transformers_results["scores"].cpu().detach().numpy(),
|
||||
class_id=class_ids,
|
||||
data=data,
|
||||
**process_transformers_detection_result(transformers_results, id2label)
|
||||
)
|
||||
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
"Only object detection and semantic segmentation results are supported."
|
||||
raise ValueError(
|
||||
"The provided Transformers results do not contain any valid fields."
|
||||
" Expected fields are 'boxes', 'masks', 'segments_info' or"
|
||||
" 'segmentation'."
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_detectron2(cls, detectron2_results) -> Detections:
|
||||
def from_detectron2(cls, detectron2_results: Any) -> Detections:
|
||||
"""
|
||||
Create a Detections object from the
|
||||
[Detectron2](https://github.com/facebookresearch/detectron2) inference result.
|
||||
|
||||
Args:
|
||||
detectron2_results: The output of a
|
||||
detectron2_results (Any): The output of a
|
||||
Detectron2 model containing instances with prediction data.
|
||||
|
||||
Returns:
|
||||
@@ -583,13 +607,11 @@ class Detections:
|
||||
result = model.infer(image)[0]
|
||||
detections = sv.Detections.from_inference(result)
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
||||
Class names values can be accessed using `detections["class_name"]`.
|
||||
"""
|
||||
with suppress(AttributeError):
|
||||
if hasattr(roboflow_result, "dict"):
|
||||
roboflow_result = roboflow_result.dict(exclude_none=True, by_alias=True)
|
||||
elif hasattr(roboflow_result, "json"):
|
||||
roboflow_result = roboflow_result.json()
|
||||
xyxy, confidence, class_id, masks, trackers, data = process_roboflow_result(
|
||||
roboflow_result=roboflow_result
|
||||
)
|
||||
@@ -780,8 +802,12 @@ class Detections:
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@deprecated(
|
||||
"`Detections.from_lmm` property is deprecated and will be removed in "
|
||||
"`supervision-0.31.0`. Use Detections.from_vlm instead."
|
||||
)
|
||||
def from_lmm(
|
||||
cls, lmm: Union[LMM, str], result: Union[str, dict], **kwargs
|
||||
cls, lmm: Union[LMM, str], result: Union[str, dict], **kwargs: Any
|
||||
) -> Detections:
|
||||
"""
|
||||
Creates a Detections object from the given result string based on the specified
|
||||
@@ -790,7 +816,7 @@ class Detections:
|
||||
Args:
|
||||
lmm (Union[LMM, str]): The type of LMM (Large Multimodal Model) to use.
|
||||
result (str): The result string containing the detection data.
|
||||
**kwargs: Additional keyword arguments required by the specified LMM.
|
||||
**kwargs (Any): Additional keyword arguments required by the specified LMM.
|
||||
|
||||
Returns:
|
||||
Detections: A new Detections object.
|
||||
@@ -818,16 +844,51 @@ class Detections:
|
||||
# array([0])
|
||||
```
|
||||
"""
|
||||
lmm = validate_lmm_parameters(lmm, result, kwargs)
|
||||
# filler logic mapping old from_lmm to new from_vlm
|
||||
lmm_to_vlm = {
|
||||
LMM.PALIGEMMA: VLM.PALIGEMMA,
|
||||
LMM.FLORENCE_2: VLM.FLORENCE_2,
|
||||
LMM.QWEN_2_5_VL: VLM.QWEN_2_5_VL,
|
||||
}
|
||||
|
||||
if lmm == LMM.PALIGEMMA:
|
||||
assert isinstance(result, str)
|
||||
# (this works even if the LMM enum is wrapped by @deprecated)
|
||||
if isinstance(lmm, Enum) and lmm.__class__.__name__ == "LMM":
|
||||
vlm = lmm_to_vlm[lmm]
|
||||
|
||||
elif isinstance(lmm, str):
|
||||
try:
|
||||
lmm_enum = LMM(lmm.lower())
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
f"Invalid LMM string '{lmm}'. Must be one of "
|
||||
f"{[m.value for m in LMM]}"
|
||||
)
|
||||
vlm = lmm_to_vlm[lmm_enum]
|
||||
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Invalid type for 'lmm': {type(lmm)}. Must be LMM or str."
|
||||
)
|
||||
|
||||
return cls.from_vlm(vlm=vlm, result=result, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def from_vlm(
|
||||
cls, vlm: Union[VLM, str], result: Union[str, dict], **kwargs: Any
|
||||
) -> Detections:
|
||||
vlm = validate_vlm_parameters(vlm, result, kwargs)
|
||||
|
||||
if vlm == VLM.PALIGEMMA:
|
||||
xyxy, class_id, class_name = from_paligemma(result, **kwargs)
|
||||
data = {CLASS_NAME_DATA_FIELD: class_name}
|
||||
return cls(xyxy=xyxy, class_id=class_id, data=data)
|
||||
|
||||
if lmm == LMM.FLORENCE_2:
|
||||
assert isinstance(result, dict)
|
||||
if vlm == VLM.QWEN_2_5_VL:
|
||||
xyxy, class_id, class_name = from_qwen_2_5_vl(result, **kwargs)
|
||||
data = {CLASS_NAME_DATA_FIELD: class_name}
|
||||
return cls(xyxy=xyxy, class_id=class_id, data=data)
|
||||
|
||||
if vlm == VLM.FLORENCE_2:
|
||||
xyxy, labels, mask, xyxyxyxy = from_florence_2(result, **kwargs)
|
||||
if len(xyxy) == 0:
|
||||
return cls.empty()
|
||||
@@ -840,7 +901,109 @@ class Detections:
|
||||
|
||||
return cls(xyxy=xyxy, mask=mask, data=data)
|
||||
|
||||
raise ValueError(f"Unsupported LMM: {lmm}")
|
||||
@classmethod
|
||||
def from_easyocr(cls, easyocr_results: list) -> Detections:
|
||||
"""
|
||||
Create a Detections object from the
|
||||
[EasyOCR](https://github.com/JaidedAI/EasyOCR) result.
|
||||
|
||||
Results are placed in the `data` field with the key `"class_name"`.
|
||||
|
||||
Args:
|
||||
easyocr_results (List): The output Results instance from EasyOCR
|
||||
|
||||
Returns:
|
||||
Detections: A new Detections object.
|
||||
|
||||
Example:
|
||||
```python
|
||||
import supervision as sv
|
||||
import easyocr
|
||||
|
||||
reader = easyocr.Reader(['en'])
|
||||
results = reader.readtext(<SOURCE_IMAGE_PATH>)
|
||||
detections = sv.Detections.from_easyocr(results)
|
||||
detected_text = detections["class_name"]
|
||||
```
|
||||
"""
|
||||
if len(easyocr_results) == 0:
|
||||
return cls.empty()
|
||||
|
||||
bbox = np.array([result[0] for result in easyocr_results])
|
||||
xyxy = np.hstack((np.min(bbox, axis=1), np.max(bbox, axis=1)))
|
||||
confidence = np.array(
|
||||
[
|
||||
result[2] if len(result) > 2 and result[2] else 0
|
||||
for result in easyocr_results
|
||||
]
|
||||
)
|
||||
ocr_text = np.array([result[1] for result in easyocr_results])
|
||||
|
||||
return cls(
|
||||
xyxy=xyxy.astype(np.float32),
|
||||
confidence=confidence.astype(np.float32),
|
||||
data={
|
||||
CLASS_NAME_DATA_FIELD: ocr_text,
|
||||
},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_ncnn(cls, ncnn_results) -> Detections:
|
||||
"""
|
||||
Creates a Detections instance from the
|
||||
[ncnn](https://github.com/Tencent/ncnn) inference result.
|
||||
Supports object detection models.
|
||||
|
||||
Arguments:
|
||||
ncnn_results (dict): The output Results instance from ncnn.
|
||||
|
||||
Returns:
|
||||
Detections: A new Detections object.
|
||||
|
||||
Example:
|
||||
```python
|
||||
import cv2
|
||||
from ncnn.model_zoo import get_model
|
||||
import supervision as sv
|
||||
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
model = get_model(
|
||||
"yolov8s",
|
||||
target_size=640
|
||||
prob_threshold=0.5,
|
||||
nms_threshold=0.45,
|
||||
num_threads=4,
|
||||
use_gpu=True,
|
||||
)
|
||||
result = model(image)
|
||||
detections = sv.Detections.from_ncnn(result)
|
||||
```
|
||||
"""
|
||||
|
||||
xywh, confidences, class_ids = [], [], []
|
||||
|
||||
if len(ncnn_results) == 0:
|
||||
return cls.empty()
|
||||
|
||||
for ncnn_result in ncnn_results:
|
||||
rect = ncnn_result.rect
|
||||
xywh.append(
|
||||
[
|
||||
rect.x.astype(np.float32),
|
||||
rect.y.astype(np.float32),
|
||||
rect.w.astype(np.float32),
|
||||
rect.h.astype(np.float32),
|
||||
]
|
||||
)
|
||||
|
||||
confidences.append(ncnn_result.prob)
|
||||
class_ids.append(ncnn_result.label)
|
||||
|
||||
return cls(
|
||||
xyxy=xywh_to_xyxy(np.array(xywh, dtype=np.float32)),
|
||||
confidence=np.array(confidences, dtype=np.float32),
|
||||
class_id=np.array(class_ids, dtype=int),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def empty(cls) -> Detections:
|
||||
@@ -870,6 +1033,7 @@ class Detections:
|
||||
"""
|
||||
empty_detections = Detections.empty()
|
||||
empty_detections.data = self.data
|
||||
empty_detections.metadata = self.metadata
|
||||
return self == empty_detections
|
||||
|
||||
@classmethod
|
||||
@@ -903,16 +1067,16 @@ class Detections:
|
||||
detections_1 = sv.Detections(
|
||||
xyxy=np.array([[15, 15, 100, 100], [200, 200, 300, 300]]),
|
||||
class_id=np.array([1, 2]),
|
||||
data={'feature_vector': np.array([0.1, 0.2)])}
|
||||
)
|
||||
data={'feature_vector': np.array([0.1, 0.2])}
|
||||
)
|
||||
|
||||
detections_2 = sv.Detections(
|
||||
xyxy=np.array([[30, 30, 120, 120]]),
|
||||
class_id=np.array([1]),
|
||||
data={'feature_vector': [np.array([0.3])]}
|
||||
)
|
||||
data={'feature_vector': np.array([0.3])}
|
||||
)
|
||||
|
||||
merged_detections = Detections.merge([detections_1, detections_2])
|
||||
merged_detections = sv.Detections.merge([detections_1, detections_2])
|
||||
|
||||
merged_detections.xyxy
|
||||
array([[ 15, 15, 100, 100],
|
||||
@@ -963,6 +1127,9 @@ class Detections:
|
||||
|
||||
data = merge_data([d.data for d in detections_list])
|
||||
|
||||
metadata_list = [detections.metadata for detections in detections_list]
|
||||
metadata = merge_metadata(metadata_list)
|
||||
|
||||
return cls(
|
||||
xyxy=xyxy,
|
||||
mask=mask,
|
||||
@@ -970,6 +1137,7 @@ class Detections:
|
||||
class_id=class_id,
|
||||
tracker_id=tracker_id,
|
||||
data=data,
|
||||
metadata=metadata,
|
||||
)
|
||||
|
||||
def get_anchors_coordinates(self, anchor: Position) -> np.ndarray:
|
||||
@@ -1074,6 +1242,8 @@ class Detections:
|
||||
"""
|
||||
if isinstance(index, str):
|
||||
return self.data.get(index)
|
||||
if self.is_empty():
|
||||
return self
|
||||
if isinstance(index, int):
|
||||
index = [index]
|
||||
return Detections(
|
||||
@@ -1083,6 +1253,7 @@ class Detections:
|
||||
class_id=self.class_id[index] if self.class_id is not None else None,
|
||||
tracker_id=self.tracker_id[index] if self.tracker_id is not None else None,
|
||||
data=get_data_item(self.data, index),
|
||||
metadata=self.metadata,
|
||||
)
|
||||
|
||||
def __setitem__(self, key: str, value: Union[np.ndarray, List]):
|
||||
@@ -1157,10 +1328,10 @@ class Detections:
|
||||
from a segmentation model, the IoU mask is applied. Otherwise, box IoU is used.
|
||||
|
||||
Args:
|
||||
threshold (float, optional): The intersection-over-union threshold
|
||||
threshold (float): The intersection-over-union threshold
|
||||
to use for non-maximum suppression. I'm the lower the value the more
|
||||
restrictive the NMS becomes. Defaults to 0.5.
|
||||
class_agnostic (bool, optional): Whether to perform class-agnostic
|
||||
class_agnostic (bool): Whether to perform class-agnostic
|
||||
non-maximum suppression. If True, the class_id of each detection
|
||||
will be ignored. Defaults to False.
|
||||
|
||||
@@ -1212,9 +1383,9 @@ class Detections:
|
||||
Perform non-maximum merging on the current set of object detections.
|
||||
|
||||
Args:
|
||||
threshold (float, optional): The intersection-over-union threshold
|
||||
threshold (float): The intersection-over-union threshold
|
||||
to use for non-maximum merging. Defaults to 0.5.
|
||||
class_agnostic (bool, optional): Whether to perform class-agnostic
|
||||
class_agnostic (bool): Whether to perform class-agnostic
|
||||
non-maximum merging. If True, the class_id of each detection
|
||||
will be ignored. Defaults to False.
|
||||
|
||||
@@ -1344,6 +1515,8 @@ def merge_inner_detection_object_pair(
|
||||
else:
|
||||
winning_detection = detections_2
|
||||
|
||||
metadata = merge_metadata([detections_1.metadata, detections_2.metadata])
|
||||
|
||||
return Detections(
|
||||
xyxy=merged_xyxy,
|
||||
mask=merged_mask,
|
||||
@@ -1351,6 +1524,7 @@ def merge_inner_detection_object_pair(
|
||||
class_id=winning_detection.class_id,
|
||||
tracker_id=winning_detection.tracker_id,
|
||||
data=winning_detection.data,
|
||||
metadata=metadata,
|
||||
)
|
||||
|
||||
|
||||
|
||||
+659
-144
@@ -1,16 +1,24 @@
|
||||
import math
|
||||
import warnings
|
||||
from typing import Dict, Iterable, Optional, Tuple
|
||||
from collections import Counter, defaultdict, deque
|
||||
from functools import lru_cache
|
||||
from typing import Any, Deque, Dict, Iterable, List, Literal, Optional, Tuple
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
|
||||
from supervision.config import CLASS_NAME_DATA_FIELD
|
||||
from supervision.detection.core import Detections
|
||||
from supervision.detection.utils import cross_product
|
||||
from supervision.draw.color import Color
|
||||
from supervision.draw.utils import draw_text
|
||||
from supervision.geometry.core import Point, Position, Vector
|
||||
from supervision.draw.utils import draw_rectangle, draw_text
|
||||
from supervision.geometry.core import Point, Position, Rect, Vector
|
||||
from supervision.utils.image import overlay_image
|
||||
from supervision.utils.internal import SupervisionWarnings
|
||||
|
||||
TEXT_MARGIN = 10
|
||||
|
||||
|
||||
class LineZone:
|
||||
"""
|
||||
@@ -34,6 +42,10 @@ class LineZone:
|
||||
to inside.
|
||||
out_count (int): The number of objects that have crossed the line from inside
|
||||
to outside.
|
||||
in_count_per_class (Dict[int, int]): Number of objects of each class that have
|
||||
crossed the line from outside to inside.
|
||||
out_count_per_class (Dict[int, int]): Number of objects of each class that have
|
||||
crossed the line from inside to outside.
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -55,7 +67,7 @@ class LineZone:
|
||||
line_zone.in_count, line_zone.out_count
|
||||
# 7, 2
|
||||
```
|
||||
""" # noqa: E501 // docs
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -67,6 +79,7 @@ class LineZone:
|
||||
Position.BOTTOM_LEFT,
|
||||
Position.BOTTOM_RIGHT,
|
||||
),
|
||||
minimum_crossing_threshold: int = 1,
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
@@ -77,53 +90,40 @@ class LineZone:
|
||||
to consider when deciding on whether the detection
|
||||
has passed the line counter or not. By default, this
|
||||
contains the four corners of the detection's bounding box
|
||||
minimum_crossing_threshold (int): Detection needs to be seen
|
||||
on the other side of the line for this many frames to be
|
||||
considered as having crossed the line. This is useful when
|
||||
dealing with unstable bounding boxes or when detections
|
||||
may linger on the line.
|
||||
"""
|
||||
self.vector = Vector(start=start, end=end)
|
||||
self.limits = self.calculate_region_of_interest_limits(vector=self.vector)
|
||||
self.tracker_state: Dict[str, bool] = {}
|
||||
self.in_count: int = 0
|
||||
self.out_count: int = 0
|
||||
self.limits = self._calculate_region_of_interest_limits(vector=self.vector)
|
||||
self.crossing_history_length = max(2, minimum_crossing_threshold + 1)
|
||||
self.crossing_state_history: Dict[int, Deque[bool]] = defaultdict(
|
||||
lambda: deque(maxlen=self.crossing_history_length)
|
||||
)
|
||||
self._in_count_per_class: Counter = Counter()
|
||||
self._out_count_per_class: Counter = Counter()
|
||||
self.triggering_anchors = triggering_anchors
|
||||
if not list(self.triggering_anchors):
|
||||
raise ValueError("Triggering anchors cannot be empty.")
|
||||
self.class_id_to_name: Dict[int, str] = {}
|
||||
|
||||
@staticmethod
|
||||
def calculate_region_of_interest_limits(vector: Vector) -> Tuple[Vector, Vector]:
|
||||
magnitude = vector.magnitude
|
||||
@property
|
||||
def in_count(self) -> int:
|
||||
return sum(self._in_count_per_class.values())
|
||||
|
||||
if magnitude == 0:
|
||||
raise ValueError("The magnitude of the vector cannot be zero.")
|
||||
@property
|
||||
def out_count(self) -> int:
|
||||
return sum(self._out_count_per_class.values())
|
||||
|
||||
delta_x = vector.end.x - vector.start.x
|
||||
delta_y = vector.end.y - vector.start.y
|
||||
@property
|
||||
def in_count_per_class(self) -> Dict[int, int]:
|
||||
return dict(self._in_count_per_class)
|
||||
|
||||
unit_vector_x = delta_x / magnitude
|
||||
unit_vector_y = delta_y / magnitude
|
||||
|
||||
perpendicular_vector_x = -unit_vector_y
|
||||
perpendicular_vector_y = unit_vector_x
|
||||
|
||||
start_region_limit = Vector(
|
||||
start=vector.start,
|
||||
end=Point(
|
||||
x=vector.start.x + perpendicular_vector_x,
|
||||
y=vector.start.y + perpendicular_vector_y,
|
||||
),
|
||||
)
|
||||
end_region_limit = Vector(
|
||||
start=vector.end,
|
||||
end=Point(
|
||||
x=vector.end.x - perpendicular_vector_x,
|
||||
y=vector.end.y - perpendicular_vector_y,
|
||||
),
|
||||
)
|
||||
return start_region_limit, end_region_limit
|
||||
|
||||
@staticmethod
|
||||
def is_point_in_limits(point: Point, limits: Tuple[Vector, Vector]) -> bool:
|
||||
cross_product_1 = limits[0].cross_product(point)
|
||||
cross_product_2 = limits[1].cross_product(point)
|
||||
return (cross_product_1 > 0) == (cross_product_2 > 0)
|
||||
@property
|
||||
def out_count_per_class(self) -> Dict[int, int]:
|
||||
return dict(self._out_count_per_class)
|
||||
|
||||
def trigger(self, detections: Detections) -> Tuple[np.ndarray, np.ndarray]:
|
||||
"""
|
||||
@@ -154,6 +154,123 @@ class LineZone:
|
||||
)
|
||||
return crossed_in, crossed_out
|
||||
|
||||
self._update_class_id_to_name(detections)
|
||||
|
||||
in_limits, has_any_left_trigger, has_any_right_trigger = (
|
||||
self._compute_anchor_sides(detections)
|
||||
)
|
||||
|
||||
class_ids: List[Optional[int]] = (
|
||||
list(detections.class_id)
|
||||
if detections.class_id is not None
|
||||
else [None] * len(detections)
|
||||
)
|
||||
|
||||
for i, (class_id, tracker_id) in enumerate(
|
||||
zip(class_ids, detections.tracker_id)
|
||||
):
|
||||
if not in_limits[i]:
|
||||
continue
|
||||
|
||||
if has_any_left_trigger[i] and has_any_right_trigger[i]:
|
||||
continue
|
||||
|
||||
tracker_state: bool = has_any_left_trigger[i]
|
||||
crossing_history = self.crossing_state_history[tracker_id]
|
||||
crossing_history.append(tracker_state)
|
||||
|
||||
if len(crossing_history) < self.crossing_history_length:
|
||||
continue
|
||||
|
||||
# TODO: Account for incorrect class_id.
|
||||
# Most likely this would involve indexing self.crossing_state_history
|
||||
# with (tracker_id, class_id).
|
||||
|
||||
oldest_state = crossing_history[0]
|
||||
if crossing_history.count(oldest_state) > 1:
|
||||
continue
|
||||
|
||||
if tracker_state:
|
||||
self._in_count_per_class[class_id] += 1
|
||||
crossed_in[i] = True
|
||||
else:
|
||||
self._out_count_per_class[class_id] += 1
|
||||
crossed_out[i] = True
|
||||
|
||||
return crossed_in, crossed_out
|
||||
|
||||
@staticmethod
|
||||
def _calculate_region_of_interest_limits(vector: Vector) -> Tuple[Vector, Vector]:
|
||||
magnitude = vector.magnitude
|
||||
|
||||
if magnitude == 0:
|
||||
raise ValueError("The magnitude of the vector cannot be zero.")
|
||||
|
||||
delta_x = vector.end.x - vector.start.x
|
||||
delta_y = vector.end.y - vector.start.y
|
||||
|
||||
unit_vector_x = delta_x / magnitude
|
||||
unit_vector_y = delta_y / magnitude
|
||||
|
||||
perpendicular_vector_x = -unit_vector_y
|
||||
perpendicular_vector_y = unit_vector_x
|
||||
|
||||
start_region_limit = Vector(
|
||||
start=vector.start,
|
||||
end=Point(
|
||||
x=vector.start.x + perpendicular_vector_x,
|
||||
y=vector.start.y + perpendicular_vector_y,
|
||||
),
|
||||
)
|
||||
end_region_limit = Vector(
|
||||
start=vector.end,
|
||||
end=Point(
|
||||
x=vector.end.x - perpendicular_vector_x,
|
||||
y=vector.end.y - perpendicular_vector_y,
|
||||
),
|
||||
)
|
||||
return start_region_limit, end_region_limit
|
||||
|
||||
def _compute_anchor_sides(
|
||||
self, detections: Detections
|
||||
) -> Tuple[npt.NDArray[np.bool_], npt.NDArray[np.bool_], npt.NDArray[np.bool_]]:
|
||||
"""
|
||||
Find if detections' anchors are within the limit of the line
|
||||
zone and which anchors are on its left and right side.
|
||||
|
||||
Assumes:
|
||||
* At least 1 detection is provided
|
||||
* Detections have `tracker_id`
|
||||
|
||||
The limit is defined as the region between the two lines,
|
||||
perpendicular to the line zone, and passing through its start
|
||||
and end points, as shown below:
|
||||
|
||||
Limits:
|
||||
```
|
||||
| IN ↑
|
||||
| |
|
||||
OUT o---LINE---o OUT
|
||||
| |
|
||||
↓ IN |
|
||||
```
|
||||
|
||||
Args:
|
||||
detections (Detections): The detections to check.
|
||||
|
||||
Returns:
|
||||
result (Tuple[np.ndarray, np.ndarray, np.ndarray]):
|
||||
All 3 arrays are boolean arrays of shape (N, ) where N is the
|
||||
number of detections. The first array, `in_limits`, indicates
|
||||
if the detection's anchor is within the line zone limits.
|
||||
The second array, `has_any_left_trigger`, indicates if the
|
||||
detection's anchor is on the left side of the line zone.
|
||||
The third array, `has_any_right_trigger`, indicates if the
|
||||
detection's anchor is on the right side of the line zone.
|
||||
"""
|
||||
assert len(detections) > 0
|
||||
assert detections.tracker_id is not None
|
||||
|
||||
all_anchors = np.array(
|
||||
[
|
||||
detections.get_anchors_coordinates(anchor)
|
||||
@@ -163,45 +280,46 @@ class LineZone:
|
||||
|
||||
cross_products_1 = cross_product(all_anchors, self.limits[0])
|
||||
cross_products_2 = cross_product(all_anchors, self.limits[1])
|
||||
|
||||
# Works because limit vectors are pointing in opposite directions
|
||||
in_limits = (cross_products_1 > 0) == (cross_products_2 > 0)
|
||||
in_limits = np.all(in_limits, axis=0)
|
||||
|
||||
triggers = cross_product(all_anchors, self.vector) < 0
|
||||
has_any_left_trigger = np.any(triggers, axis=0)
|
||||
has_any_right_trigger = np.any(~triggers, axis=0)
|
||||
is_uniformly_triggered = ~(has_any_left_trigger & has_any_right_trigger)
|
||||
for i, tracker_id in enumerate(detections.tracker_id):
|
||||
if not in_limits[i]:
|
||||
continue
|
||||
|
||||
if not is_uniformly_triggered[i]:
|
||||
continue
|
||||
return in_limits, has_any_left_trigger, has_any_right_trigger
|
||||
|
||||
tracker_state = has_any_left_trigger[i]
|
||||
if tracker_id not in self.tracker_state:
|
||||
self.tracker_state[tracker_id] = tracker_state
|
||||
continue
|
||||
def _update_class_id_to_name(self, detections: Detections) -> None:
|
||||
"""
|
||||
Update the attribute keeping track of which class
|
||||
IDs correspond to which class names.
|
||||
|
||||
if self.tracker_state.get(tracker_id) == tracker_state:
|
||||
continue
|
||||
Assumes that class_names are only provided when class_ids are.
|
||||
"""
|
||||
class_names = detections.data.get(CLASS_NAME_DATA_FIELD)
|
||||
assert class_names is None or detections.class_id is not None
|
||||
|
||||
self.tracker_state[tracker_id] = tracker_state
|
||||
if tracker_state:
|
||||
self.in_count += 1
|
||||
crossed_in[i] = True
|
||||
else:
|
||||
self.out_count += 1
|
||||
crossed_out[i] = True
|
||||
if detections.class_id is None:
|
||||
return
|
||||
|
||||
return crossed_in, crossed_out
|
||||
if class_names is None:
|
||||
new_names = {class_id: str(class_id) for class_id in detections.class_id}
|
||||
else:
|
||||
new_names = {
|
||||
class_id: class_name
|
||||
for class_id, class_name in zip(detections.class_id, class_names)
|
||||
}
|
||||
self.class_id_to_name.update(new_names)
|
||||
|
||||
|
||||
class LineZoneAnnotator:
|
||||
def __init__(
|
||||
self,
|
||||
thickness: float = 2,
|
||||
thickness: int = 2,
|
||||
color: Color = Color.WHITE,
|
||||
text_thickness: float = 2,
|
||||
text_thickness: int = 2,
|
||||
text_color: Color = Color.BLACK,
|
||||
text_scale: float = 0.5,
|
||||
text_offset: float = 1.5,
|
||||
@@ -210,86 +328,67 @@ class LineZoneAnnotator:
|
||||
custom_out_text: Optional[str] = None,
|
||||
display_in_count: bool = True,
|
||||
display_out_count: bool = True,
|
||||
display_text_box: bool = True,
|
||||
text_orient_to_line: bool = False,
|
||||
text_centered: bool = True,
|
||||
):
|
||||
"""
|
||||
Initialize the LineCounterAnnotator object with default values.
|
||||
A class for drawing the `LineZone` and its detected object count
|
||||
on an image.
|
||||
|
||||
Attributes:
|
||||
thickness (float): The thickness of the line that will be drawn.
|
||||
color (Color): The color of the line that will be drawn.
|
||||
text_thickness (float): The thickness of the text that will be drawn.
|
||||
text_color (Color): The color of the text that will be drawn.
|
||||
text_scale (float): The scale of the text that will be drawn.
|
||||
text_offset (float): The offset of the text that will be drawn.
|
||||
text_padding (int): The padding of the text that will be drawn.
|
||||
display_in_count (bool): Whether to display the in count or not.
|
||||
display_out_count (bool): Whether to display the out count or not.
|
||||
thickness (int): Line thickness.
|
||||
color (Color): Line color.
|
||||
text_thickness (int): Text thickness.
|
||||
text_color (Color): Text color.
|
||||
text_scale (float): Text scale.
|
||||
text_offset (float): How far the text will be from the line.
|
||||
text_padding (int): The empty space in the text box, surrounding the text.
|
||||
custom_in_text (Optional[str]): Write something else instead of "in".
|
||||
custom_out_text (Optional[str]): Write something else instead of "out".
|
||||
display_in_count (bool): Pass `False` to hide the "in" count.
|
||||
display_out_count (bool): Pass `False` to hide the "out" count.
|
||||
display_text_box (bool): Pass `False` to hide the text background box.
|
||||
text_orient_to_line (bool): ⭐ Match text orientation to the line.
|
||||
Recommended to set to `True`.
|
||||
text_centered (bool): Pass `False` to disable text centering. Useful
|
||||
when the label overlaps something important.
|
||||
|
||||
"""
|
||||
self.thickness: float = thickness
|
||||
self.thickness: int = thickness
|
||||
self.color: Color = color
|
||||
self.text_thickness: float = text_thickness
|
||||
self.text_thickness: int = text_thickness
|
||||
self.text_color: Color = text_color
|
||||
self.text_scale: float = text_scale
|
||||
self.text_offset: float = text_offset
|
||||
self.text_padding: int = text_padding
|
||||
self.custom_in_text: str = custom_in_text
|
||||
self.custom_out_text: str = custom_out_text
|
||||
self.in_text: str = custom_in_text if custom_in_text else "in"
|
||||
self.out_text: str = custom_out_text if custom_out_text else "out"
|
||||
self.display_in_count: bool = display_in_count
|
||||
self.display_out_count: bool = display_out_count
|
||||
|
||||
def _annotate_count(
|
||||
self,
|
||||
frame: np.ndarray,
|
||||
center_text_anchor: Point,
|
||||
text: str,
|
||||
is_in_count: bool,
|
||||
) -> None:
|
||||
"""This method is drawing the text on the frame.
|
||||
|
||||
Args:
|
||||
frame (np.ndarray): The image on which the text will be drawn.
|
||||
center_text_anchor: The center point that the text will be drawn.
|
||||
text (str): The text that will be drawn.
|
||||
is_in_count (bool): Whether to display the in count or out count.
|
||||
"""
|
||||
_, text_height = cv2.getTextSize(
|
||||
text, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness
|
||||
)[0]
|
||||
|
||||
if is_in_count:
|
||||
center_text_anchor.y -= int(self.text_offset * text_height)
|
||||
else:
|
||||
center_text_anchor.y += int(self.text_offset * text_height)
|
||||
|
||||
draw_text(
|
||||
scene=frame,
|
||||
text=text,
|
||||
text_anchor=center_text_anchor,
|
||||
text_color=self.text_color,
|
||||
text_scale=self.text_scale,
|
||||
text_thickness=self.text_thickness,
|
||||
text_padding=self.text_padding,
|
||||
background_color=self.color,
|
||||
)
|
||||
self.display_text_box: bool = display_text_box
|
||||
self.text_orient_to_line: bool = text_orient_to_line
|
||||
self.text_centered: bool = text_centered
|
||||
|
||||
def annotate(self, frame: np.ndarray, line_counter: LineZone) -> np.ndarray:
|
||||
"""
|
||||
Draws the line on the frame using the line_counter provided.
|
||||
Draws the line on the frame using the line zone provided.
|
||||
|
||||
Attributes:
|
||||
frame (np.ndarray): The image on which the line will be drawn.
|
||||
line_counter (LineCounter): The line counter
|
||||
line_counter (LineZone): The line zone
|
||||
that will be used to draw the line.
|
||||
|
||||
Returns:
|
||||
np.ndarray: The image with the line drawn on it.
|
||||
(np.ndarray): The image with the line drawn on it.
|
||||
|
||||
"""
|
||||
line_start = line_counter.vector.start.as_xy_int_tuple()
|
||||
line_end = line_counter.vector.end.as_xy_int_tuple()
|
||||
cv2.line(
|
||||
frame,
|
||||
line_counter.vector.start.as_xy_int_tuple(),
|
||||
line_counter.vector.end.as_xy_int_tuple(),
|
||||
line_start,
|
||||
line_end,
|
||||
self.color.as_bgr(),
|
||||
self.thickness,
|
||||
lineType=cv2.LINE_AA,
|
||||
@@ -297,7 +396,7 @@ class LineZoneAnnotator:
|
||||
)
|
||||
cv2.circle(
|
||||
frame,
|
||||
line_counter.vector.start.as_xy_int_tuple(),
|
||||
line_start,
|
||||
radius=5,
|
||||
color=self.text_color.as_bgr(),
|
||||
thickness=-1,
|
||||
@@ -305,40 +404,456 @@ class LineZoneAnnotator:
|
||||
)
|
||||
cv2.circle(
|
||||
frame,
|
||||
line_counter.vector.end.as_xy_int_tuple(),
|
||||
line_end,
|
||||
radius=5,
|
||||
color=self.text_color.as_bgr(),
|
||||
thickness=-1,
|
||||
lineType=cv2.LINE_AA,
|
||||
)
|
||||
|
||||
text_anchor = Vector(
|
||||
start=line_counter.vector.start, end=line_counter.vector.end
|
||||
in_text = f"{self.in_text}: {line_counter.in_count}"
|
||||
out_text = f"{self.out_text}: {line_counter.out_count}"
|
||||
line_angle_degrees = self._get_line_angle(line_counter)
|
||||
|
||||
for text, is_shown, is_in_count in [
|
||||
(in_text, self.display_in_count, True),
|
||||
(out_text, self.display_out_count, False),
|
||||
]:
|
||||
if not is_shown:
|
||||
continue
|
||||
|
||||
if line_angle_degrees == 0 or not self.text_orient_to_line:
|
||||
self._draw_basic_label(
|
||||
frame=frame,
|
||||
line_center=line_counter.vector.center,
|
||||
text=text,
|
||||
is_in_count=is_in_count,
|
||||
)
|
||||
else:
|
||||
self._draw_oriented_label(
|
||||
frame=frame,
|
||||
line_zone=line_counter,
|
||||
text=text,
|
||||
is_in_count=is_in_count,
|
||||
)
|
||||
|
||||
return frame
|
||||
|
||||
def _get_line_angle(self, line_zone: LineZone) -> float:
|
||||
"""
|
||||
Calculate the line counter angle (in degrees).
|
||||
|
||||
Args:
|
||||
line_zone (LineZone): The line zone object.
|
||||
|
||||
Returns:
|
||||
(float): Line counter angle, in degrees.
|
||||
"""
|
||||
start_point = line_zone.vector.start.as_xy_int_tuple()
|
||||
end_point = line_zone.vector.end.as_xy_int_tuple()
|
||||
|
||||
delta_x = end_point[0] - start_point[0]
|
||||
delta_y = end_point[1] - start_point[1]
|
||||
|
||||
if delta_x == 0:
|
||||
line_angle = 90.0
|
||||
line_angle += 180 if delta_y < 0 else 0
|
||||
else:
|
||||
line_angle = math.degrees(math.atan(delta_y / delta_x))
|
||||
line_angle += 180 if delta_x < 0 else 0
|
||||
|
||||
return line_angle
|
||||
|
||||
def _calculate_anchor_in_frame(
|
||||
self,
|
||||
line_zone: LineZone,
|
||||
text_width: int,
|
||||
text_height: int,
|
||||
is_in_count: bool,
|
||||
label_dimension: int,
|
||||
) -> Tuple[int, int]:
|
||||
"""
|
||||
Calculate insertion anchor in frame to position the center of the count image.
|
||||
|
||||
Args:
|
||||
line_zone (LineZone): The line counter object used for counting.
|
||||
text_width (int): Text width.
|
||||
text_height (int): Text height.
|
||||
is_in_count (bool): Whether the count should be placed over or below line.
|
||||
label_dimension (int): Size of the label image. Assumes the
|
||||
label is rectangular.
|
||||
|
||||
Returns:
|
||||
(Tuple[int, int]): xy, point in an image where the label will be placed.
|
||||
"""
|
||||
line_angle = self._get_line_angle(line_zone)
|
||||
|
||||
if self.text_centered:
|
||||
mid_point = Vector(
|
||||
start=line_zone.vector.start, end=line_zone.vector.end
|
||||
).center.as_xy_int_tuple()
|
||||
anchor = list(mid_point)
|
||||
else:
|
||||
end_point = line_zone.vector.end.as_xy_int_tuple()
|
||||
anchor = list(end_point)
|
||||
|
||||
move_along_x = int(
|
||||
math.cos(math.radians(line_angle))
|
||||
* (text_width / 2 + self.text_padding)
|
||||
)
|
||||
move_along_y = int(
|
||||
math.sin(math.radians(line_angle))
|
||||
* (text_width / 2 + self.text_padding)
|
||||
)
|
||||
|
||||
anchor[0] -= move_along_x
|
||||
anchor[1] -= move_along_y
|
||||
|
||||
move_perpendicular_x = int(
|
||||
math.sin(math.radians(line_angle)) * (self.text_offset * text_height)
|
||||
)
|
||||
move_perpendicular_y = int(
|
||||
math.cos(math.radians(line_angle)) * (self.text_offset * text_height)
|
||||
)
|
||||
|
||||
if self.display_in_count:
|
||||
in_text = (
|
||||
f"{self.custom_in_text}: {line_counter.in_count}"
|
||||
if self.custom_in_text is not None
|
||||
else f"in: {line_counter.in_count}"
|
||||
)
|
||||
self._annotate_count(
|
||||
frame=frame,
|
||||
center_text_anchor=text_anchor.center,
|
||||
text=in_text,
|
||||
is_in_count=True,
|
||||
if is_in_count:
|
||||
anchor[0] += move_perpendicular_x
|
||||
anchor[1] -= move_perpendicular_y
|
||||
else:
|
||||
anchor[0] -= move_perpendicular_x
|
||||
anchor[1] += move_perpendicular_y
|
||||
|
||||
x1 = max(anchor[0] - label_dimension // 2, 0)
|
||||
y1 = max(anchor[1] - label_dimension // 2, 0)
|
||||
|
||||
return x1, y1
|
||||
|
||||
def _draw_basic_label(
|
||||
self,
|
||||
frame: np.ndarray,
|
||||
line_center: Point,
|
||||
text: str,
|
||||
is_in_count: bool,
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
Draw the count label on the frame. For example: "out: 7".
|
||||
The label contains horizontal text and is not rotated.
|
||||
|
||||
Args:
|
||||
frame (np.ndarray): The entire scene, on which the label will be placed.
|
||||
line_center (Point): The center of the line zone.
|
||||
text (str): The text that will be drawn.
|
||||
is_in_count (bool): Whether to display the in count (above line)
|
||||
or out count (below line).
|
||||
|
||||
Returns:
|
||||
(np.ndarray): The scene with the label drawn on it.
|
||||
"""
|
||||
_, text_height = cv2.getTextSize(
|
||||
text, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness
|
||||
)[0]
|
||||
|
||||
if is_in_count:
|
||||
line_center.y -= int(self.text_offset * text_height)
|
||||
else:
|
||||
line_center.y += int(self.text_offset * text_height)
|
||||
|
||||
draw_text(
|
||||
scene=frame,
|
||||
text=text,
|
||||
text_anchor=line_center,
|
||||
text_color=self.text_color,
|
||||
text_scale=self.text_scale,
|
||||
text_thickness=self.text_thickness,
|
||||
text_padding=self.text_padding,
|
||||
background_color=self.color if self.display_text_box else None,
|
||||
)
|
||||
|
||||
return frame
|
||||
|
||||
def _draw_oriented_label(
|
||||
self,
|
||||
frame: np.ndarray,
|
||||
line_zone: LineZone,
|
||||
text: str,
|
||||
is_in_count: bool,
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
Draw the count label on the frame. For example: "out: 7".
|
||||
The label is oriented to match the line angle.
|
||||
|
||||
Args:
|
||||
frame (np.ndarray): The entire scene, on which the label will be placed.
|
||||
line_zone (LineZone): The line zone responsible for counting
|
||||
objects crossing it.
|
||||
text (str): The text that will be drawn.
|
||||
is_in_count (bool): Whether to display the in count (above line)
|
||||
or out count (below line).
|
||||
|
||||
Returns:
|
||||
(np.ndarray): The scene with the label drawn on it.
|
||||
"""
|
||||
|
||||
line_angle_degrees = self._get_line_angle(line_zone)
|
||||
label_image = self._make_label_image(
|
||||
text,
|
||||
text_scale=self.text_scale,
|
||||
text_thickness=self.text_thickness,
|
||||
text_padding=self.text_padding,
|
||||
text_color=self.text_color,
|
||||
text_box_show=self.display_text_box,
|
||||
text_box_color=self.color,
|
||||
line_angle_degrees=line_angle_degrees,
|
||||
)
|
||||
assert label_image.shape[0] == label_image.shape[1]
|
||||
|
||||
text_width, text_height = cv2.getTextSize(
|
||||
text, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness
|
||||
)[0]
|
||||
|
||||
label_anchor = self._calculate_anchor_in_frame(
|
||||
line_zone=line_zone,
|
||||
text_width=text_width,
|
||||
text_height=text_height,
|
||||
is_in_count=is_in_count,
|
||||
label_dimension=label_image.shape[0],
|
||||
)
|
||||
|
||||
frame = overlay_image(frame, label_image, label_anchor)
|
||||
|
||||
return frame
|
||||
|
||||
@staticmethod
|
||||
@lru_cache(maxsize=32)
|
||||
def _make_label_image(
|
||||
text: str,
|
||||
*,
|
||||
text_scale: float,
|
||||
text_thickness: int,
|
||||
text_padding: int,
|
||||
text_color: Color,
|
||||
text_box_show: bool,
|
||||
text_box_color: Color,
|
||||
line_angle_degrees: float,
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
Create the small text box displaying line zone count. E.g. "out: 7".
|
||||
|
||||
Args:
|
||||
text (str): The text to display.
|
||||
text_scale (float): The scale of the text.
|
||||
text_thickness (int): The thickness of the text.
|
||||
text_padding (int): The padding around the text.
|
||||
text_color (Color): The color of the text.
|
||||
text_box_show (bool): Whether to display the text box.
|
||||
text_box_color (Color): The color of the text box.
|
||||
line_angle_degrees (float): The angle of the line in degrees.
|
||||
|
||||
Returns:
|
||||
(np.ndarray): The label of shape (H, W, 4), in BGRA format.
|
||||
"""
|
||||
text_width, text_height = cv2.getTextSize(
|
||||
text, cv2.FONT_HERSHEY_SIMPLEX, text_scale, text_thickness
|
||||
)[0]
|
||||
|
||||
annotation_dim = int((max(text_width, text_height) + text_padding * 2) * 1.5)
|
||||
annotation_shape = (annotation_dim, annotation_dim)
|
||||
annotation_center = Point(annotation_dim // 2, annotation_dim // 2)
|
||||
|
||||
annotation = np.zeros((*annotation_shape, 3), dtype=np.uint8)
|
||||
annotation_alpha = np.zeros((*annotation_shape, 1), dtype=np.uint8)
|
||||
|
||||
text_args: Dict[str, Any] = dict(
|
||||
text=text,
|
||||
text_anchor=annotation_center,
|
||||
text_scale=text_scale,
|
||||
text_thickness=text_thickness,
|
||||
text_padding=text_padding,
|
||||
)
|
||||
draw_text(
|
||||
scene=annotation,
|
||||
text_color=text_color,
|
||||
background_color=text_box_color if text_box_show else None,
|
||||
**text_args,
|
||||
)
|
||||
draw_text(
|
||||
scene=annotation_alpha,
|
||||
text_color=Color.WHITE,
|
||||
background_color=Color.WHITE if text_box_show else None,
|
||||
**text_args,
|
||||
)
|
||||
annotation = np.dstack((annotation, annotation_alpha))
|
||||
|
||||
# Make sure text is displayed upright
|
||||
if 90 < line_angle_degrees % 360 < 270:
|
||||
annotation = cv2.flip(annotation, flipCode=-1).astype(np.uint8)
|
||||
|
||||
rotation_angle = -line_angle_degrees
|
||||
rotation_matrix = cv2.getRotationMatrix2D(
|
||||
annotation_center.as_xy_float_tuple(), rotation_angle, scale=1
|
||||
)
|
||||
annotation = cv2.warpAffine(annotation, rotation_matrix, annotation_shape)
|
||||
|
||||
return annotation
|
||||
|
||||
|
||||
class LineZoneAnnotatorMulticlass:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
table_position: Literal[
|
||||
Position.TOP_LEFT,
|
||||
Position.TOP_RIGHT,
|
||||
Position.BOTTOM_LEFT,
|
||||
Position.BOTTOM_RIGHT,
|
||||
] = Position.TOP_RIGHT,
|
||||
table_color: Color = Color.WHITE,
|
||||
table_margin: int = 10,
|
||||
table_padding: int = 10,
|
||||
table_max_width: int = 400,
|
||||
text_color: Color = Color.BLACK,
|
||||
text_scale: float = 0.75,
|
||||
text_thickness: int = 1,
|
||||
force_draw_class_ids: bool = False,
|
||||
):
|
||||
"""
|
||||
Draw a table showing how many items of each class crossed each line.
|
||||
|
||||
Args:
|
||||
table_position (Position): The position of the table.
|
||||
table_color (Color): The color of the table.
|
||||
table_margin (int): The margin of the table from the image border.
|
||||
table_padding (int): The padding of the table.
|
||||
table_max_width (int): The maximum width of the table.
|
||||
text_color (Color): The color of the text.
|
||||
text_scale (float): The scale of the text.
|
||||
text_thickness (int): The thickness of the text.
|
||||
force_draw_class_ids (bool): Instead of writing the class names,
|
||||
on the table, write the class IDs. E.g. instead of `person: 6`,
|
||||
write `0: 6`.
|
||||
"""
|
||||
if table_position not in {
|
||||
Position.TOP_LEFT,
|
||||
Position.TOP_RIGHT,
|
||||
Position.BOTTOM_LEFT,
|
||||
Position.BOTTOM_RIGHT,
|
||||
}:
|
||||
raise ValueError(
|
||||
"Invalid table position. Supported values are:"
|
||||
" TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT."
|
||||
)
|
||||
|
||||
if self.display_out_count:
|
||||
out_text = (
|
||||
f"{self.custom_out_text}: {line_counter.out_count}"
|
||||
if self.custom_out_text is not None
|
||||
else f"out: {line_counter.out_count}"
|
||||
)
|
||||
self._annotate_count(
|
||||
frame=frame,
|
||||
center_text_anchor=text_anchor.center,
|
||||
text=out_text,
|
||||
is_in_count=False,
|
||||
self.table_position = table_position
|
||||
self.table_color = table_color
|
||||
self.table_margin = table_margin
|
||||
self.table_padding = table_padding
|
||||
self.table_max_width = table_max_width
|
||||
self.text_color = text_color
|
||||
self.text_scale = text_scale
|
||||
self.text_thickness = text_thickness
|
||||
self.force_draw_class_ids = force_draw_class_ids
|
||||
|
||||
def annotate(
|
||||
self,
|
||||
frame: np.ndarray,
|
||||
line_zones: List[LineZone],
|
||||
line_zone_labels: Optional[List[str]] = None,
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
Draws a table with the number of objects of each class that crossed each line.
|
||||
|
||||
Attributes:
|
||||
frame (np.ndarray): The image on which the table will be drawn.
|
||||
line_zones (List[LineZone]): The line zones to be annotated.
|
||||
line_zone_labels (Optional[List[str]]): The labels, one for each
|
||||
line zone. If not provided, the default labels will be used.
|
||||
|
||||
Returns:
|
||||
(np.ndarray): The image with the table drawn on it.
|
||||
|
||||
"""
|
||||
if line_zone_labels is None:
|
||||
line_zone_labels = [f"Line {i + 1}:" for i in range(len(line_zones))]
|
||||
if len(line_zones) != len(line_zone_labels):
|
||||
raise ValueError("The number of line zones and their labels must match.")
|
||||
|
||||
text_lines = ["Line Crossings:"]
|
||||
for line_zone, line_zone_label in zip(line_zones, line_zone_labels):
|
||||
text_lines.append(line_zone_label)
|
||||
class_id_to_name = line_zone.class_id_to_name
|
||||
|
||||
for direction, count_per_class in [
|
||||
("In", line_zone.in_count_per_class),
|
||||
("Out", line_zone.out_count_per_class),
|
||||
]:
|
||||
if not count_per_class:
|
||||
continue
|
||||
|
||||
text_lines.append(f" {direction}:")
|
||||
for class_id, count in count_per_class.items():
|
||||
class_name = (
|
||||
class_id_to_name.get(class_id, str(class_id))
|
||||
if not self.force_draw_class_ids
|
||||
else str(class_id)
|
||||
)
|
||||
text_lines.append(f" {class_name}: {count}")
|
||||
|
||||
table_width, table_height = 0, 0
|
||||
for line in text_lines:
|
||||
text_width, text_height = cv2.getTextSize(
|
||||
line, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness
|
||||
)[0]
|
||||
text_height += TEXT_MARGIN
|
||||
table_width = max(table_width, text_width)
|
||||
table_height += text_height
|
||||
|
||||
table_width += 2 * self.table_padding
|
||||
table_height += 2 * self.table_padding
|
||||
table_max_height = frame.shape[0] - 2 * self.table_margin
|
||||
table_height = min(table_height, table_max_height)
|
||||
table_width = min(table_width, self.table_max_width)
|
||||
|
||||
position_map = {
|
||||
Position.TOP_LEFT: (self.table_margin, self.table_margin),
|
||||
Position.TOP_RIGHT: (
|
||||
frame.shape[1] - table_width - self.table_margin,
|
||||
self.table_margin,
|
||||
),
|
||||
Position.BOTTOM_LEFT: (
|
||||
self.table_margin,
|
||||
frame.shape[0] - table_height - self.table_margin,
|
||||
),
|
||||
Position.BOTTOM_RIGHT: (
|
||||
frame.shape[1] - table_width - self.table_margin,
|
||||
frame.shape[0] - table_height - self.table_margin,
|
||||
),
|
||||
}
|
||||
table_x1, table_y1 = position_map[self.table_position]
|
||||
|
||||
table_rect = Rect(
|
||||
x=table_x1, y=table_y1, width=table_width, height=table_height
|
||||
)
|
||||
frame = draw_rectangle(
|
||||
scene=frame, rect=table_rect, color=self.table_color, thickness=-1
|
||||
)
|
||||
|
||||
for i, line in enumerate(text_lines):
|
||||
_, text_height = cv2.getTextSize(
|
||||
line, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness
|
||||
)[0]
|
||||
text_height += TEXT_MARGIN
|
||||
anchor_x = table_x1 + self.table_padding
|
||||
anchor_y = table_y1 + self.table_padding + (i + 1) * text_height
|
||||
|
||||
cv2.putText(
|
||||
img=frame,
|
||||
text=line,
|
||||
org=(anchor_x, anchor_y),
|
||||
fontFace=cv2.FONT_HERSHEY_SIMPLEX,
|
||||
fontScale=self.text_scale,
|
||||
color=self.text_color.as_bgr(),
|
||||
thickness=self.text_thickness,
|
||||
lineType=cv2.LINE_AA,
|
||||
)
|
||||
|
||||
return frame
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
from typing import List, Union
|
||||
|
||||
@@ -53,9 +55,9 @@ def mask_non_max_suppression(
|
||||
masks (np.ndarray): A 3D array of binary masks corresponding to the predictions.
|
||||
Shape: `(N, H, W)`, where N is the number of predictions, and H, W are the
|
||||
dimensions of each mask.
|
||||
iou_threshold (float, optional): The intersection-over-union threshold
|
||||
iou_threshold (float): The intersection-over-union threshold
|
||||
to use for non-maximum suppression.
|
||||
mask_dimension (int, optional): The dimension to which the masks should be
|
||||
mask_dimension (int): The dimension to which the masks should be
|
||||
resized before computing IOU values. Defaults to 640.
|
||||
|
||||
Returns:
|
||||
@@ -64,7 +66,7 @@ def mask_non_max_suppression(
|
||||
|
||||
Raises:
|
||||
AssertionError: If `iou_threshold` is not within the closed
|
||||
range from `0` to `1`.
|
||||
range from `0` to `1`.
|
||||
"""
|
||||
assert 0 <= iou_threshold <= 1, (
|
||||
"Value of `iou_threshold` must be in the closed range from 0 to 1, "
|
||||
@@ -101,7 +103,7 @@ def box_non_max_suppression(
|
||||
predictions (np.ndarray): An array of object detection predictions in
|
||||
the format of `(x_min, y_min, x_max, y_max, score)`
|
||||
or `(x_min, y_min, x_max, y_max, score, class)`.
|
||||
iou_threshold (float, optional): The intersection-over-union threshold
|
||||
iou_threshold (float): The intersection-over-union threshold
|
||||
to use for non-maximum suppression.
|
||||
|
||||
Returns:
|
||||
@@ -156,7 +158,7 @@ def group_overlapping_boxes(
|
||||
predictions (npt.NDArray[np.float64]): An array of shape `(n, 5)` containing
|
||||
the bounding boxes coordinates in format `[x1, y1, x2, y2]`
|
||||
and the confidence scores.
|
||||
iou_threshold (float, optional): The intersection-over-union threshold
|
||||
iou_threshold (float): The intersection-over-union threshold
|
||||
to use for non-maximum suppression. Defaults to 0.5.
|
||||
|
||||
Returns:
|
||||
@@ -181,7 +183,7 @@ def group_overlapping_boxes(
|
||||
ious = ious.flatten()
|
||||
|
||||
above_threshold = ious >= iou_threshold
|
||||
merge_group = [idx] + np.flip(order[above_threshold]).tolist()
|
||||
merge_group = [idx, *np.flip(order[above_threshold]).tolist()]
|
||||
merge_groups.append(merge_group)
|
||||
order = order[~above_threshold]
|
||||
return merge_groups
|
||||
@@ -200,7 +202,7 @@ def box_non_max_merge(
|
||||
containing the bounding boxes coordinates in format `[x1, y1, x2, y2]`,
|
||||
the confidence scores and class_ids. Omit class_id column to allow
|
||||
detections of different classes to be merged.
|
||||
iou_threshold (float, optional): The intersection-over-union threshold
|
||||
iou_threshold (float): The intersection-over-union threshold
|
||||
to use for non-maximum suppression. Defaults to 0.5.
|
||||
|
||||
Returns:
|
||||
@@ -248,16 +250,21 @@ class OverlapFilter(Enum):
|
||||
NON_MAX_SUPPRESSION = "non_max_suppression"
|
||||
NON_MAX_MERGE = "non_max_merge"
|
||||
|
||||
@classmethod
|
||||
def list(cls):
|
||||
return list(map(lambda c: c.value, cls))
|
||||
|
||||
def validate_overlap_filter(
|
||||
strategy: Union[OverlapFilter, str],
|
||||
) -> OverlapFilter:
|
||||
if isinstance(strategy, str):
|
||||
try:
|
||||
strategy = OverlapFilter(strategy.lower())
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
f"Invalid strategy value: {strategy}. Must be one of "
|
||||
f"{[e.value for e in OverlapFilter]}"
|
||||
)
|
||||
return strategy
|
||||
@classmethod
|
||||
def from_value(cls, value: Union[OverlapFilter, str]) -> OverlapFilter:
|
||||
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."
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user