diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/404.html b/404.html new file mode 100644 index 000000000..4cfde4cdb --- /dev/null +++ b/404.html @@ -0,0 +1,838 @@ + + + + + + + + + + + + + + + + + + + + + + Supervision + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/assets/_mkdocstrings.css b/assets/_mkdocstrings.css new file mode 100644 index 000000000..049a254b9 --- /dev/null +++ b/assets/_mkdocstrings.css @@ -0,0 +1,64 @@ + +/* Avoid breaking parameter names, etc. in table cells. */ +.doc-contents td code { + word-break: normal !important; +} + +/* No line break before first paragraph of descriptions. */ +.doc-md-description, +.doc-md-description>p:first-child { + display: inline; +} + +/* Max width for docstring sections tables. */ +.doc .md-typeset__table, +.doc .md-typeset__table table { + display: table !important; + width: 100%; +} + +.doc .md-typeset__table tr { + display: table-row; +} + +/* Defaults in Spacy table style. */ +.doc-param-default { + float: right; +} + +/* Keep headings consistent. */ +h1.doc-heading, +h2.doc-heading, +h3.doc-heading, +h4.doc-heading, +h5.doc-heading, +h6.doc-heading { + font-weight: 400; + line-height: 1.5; + color: inherit; + text-transform: none; +} + +h1.doc-heading { + font-size: 1.6rem; +} + +h2.doc-heading { + font-size: 1.2rem; +} + +h3.doc-heading { + font-size: 1.15rem; +} + +h4.doc-heading { + font-size: 1.10rem; +} + +h5.doc-heading { + font-size: 1.05rem; +} + +h6.doc-heading { + font-size: 1rem; +} \ No newline at end of file diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 000000000..1cf13b9f9 Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/javascripts/bundle.b425cdc4.min.js b/assets/javascripts/bundle.b425cdc4.min.js new file mode 100644 index 000000000..201e52356 --- /dev/null +++ b/assets/javascripts/bundle.b425cdc4.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var Ci=Object.create;var gr=Object.defineProperty;var Ri=Object.getOwnPropertyDescriptor;var ki=Object.getOwnPropertyNames,Ht=Object.getOwnPropertySymbols,Hi=Object.getPrototypeOf,yr=Object.prototype.hasOwnProperty,nn=Object.prototype.propertyIsEnumerable;var rn=(e,t,r)=>t in e?gr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))yr.call(t,r)&&rn(e,r,t[r]);if(Ht)for(var r of Ht(t))nn.call(t,r)&&rn(e,r,t[r]);return e};var on=(e,t)=>{var r={};for(var n in e)yr.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(e!=null&&Ht)for(var n of Ht(e))t.indexOf(n)<0&&nn.call(e,n)&&(r[n]=e[n]);return r};var Pt=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Pi=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of ki(t))!yr.call(e,o)&&o!==r&&gr(e,o,{get:()=>t[o],enumerable:!(n=Ri(t,o))||n.enumerable});return e};var yt=(e,t,r)=>(r=e!=null?Ci(Hi(e)):{},Pi(t||!e||!e.__esModule?gr(r,"default",{value:e,enumerable:!0}):r,e));var sn=Pt((xr,an)=>{(function(e,t){typeof xr=="object"&&typeof an!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(xr,function(){"use strict";function e(r){var n=!0,o=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(O){return!!(O&&O!==document&&O.nodeName!=="HTML"&&O.nodeName!=="BODY"&&"classList"in O&&"contains"in O.classList)}function f(O){var Qe=O.type,De=O.tagName;return!!(De==="INPUT"&&s[Qe]&&!O.readOnly||De==="TEXTAREA"&&!O.readOnly||O.isContentEditable)}function c(O){O.classList.contains("focus-visible")||(O.classList.add("focus-visible"),O.setAttribute("data-focus-visible-added",""))}function u(O){O.hasAttribute("data-focus-visible-added")&&(O.classList.remove("focus-visible"),O.removeAttribute("data-focus-visible-added"))}function p(O){O.metaKey||O.altKey||O.ctrlKey||(a(r.activeElement)&&c(r.activeElement),n=!0)}function m(O){n=!1}function d(O){a(O.target)&&(n||f(O.target))&&c(O.target)}function h(O){a(O.target)&&(O.target.classList.contains("focus-visible")||O.target.hasAttribute("data-focus-visible-added"))&&(o=!0,window.clearTimeout(i),i=window.setTimeout(function(){o=!1},100),u(O.target))}function v(O){document.visibilityState==="hidden"&&(o&&(n=!0),Y())}function Y(){document.addEventListener("mousemove",N),document.addEventListener("mousedown",N),document.addEventListener("mouseup",N),document.addEventListener("pointermove",N),document.addEventListener("pointerdown",N),document.addEventListener("pointerup",N),document.addEventListener("touchmove",N),document.addEventListener("touchstart",N),document.addEventListener("touchend",N)}function B(){document.removeEventListener("mousemove",N),document.removeEventListener("mousedown",N),document.removeEventListener("mouseup",N),document.removeEventListener("pointermove",N),document.removeEventListener("pointerdown",N),document.removeEventListener("pointerup",N),document.removeEventListener("touchmove",N),document.removeEventListener("touchstart",N),document.removeEventListener("touchend",N)}function N(O){O.target.nodeName&&O.target.nodeName.toLowerCase()==="html"||(n=!1,B())}document.addEventListener("keydown",p,!0),document.addEventListener("mousedown",m,!0),document.addEventListener("pointerdown",m,!0),document.addEventListener("touchstart",m,!0),document.addEventListener("visibilitychange",v,!0),Y(),r.addEventListener("focus",d,!0),r.addEventListener("blur",h,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var cn=Pt(Er=>{(function(e){var t=function(){try{return!!Symbol.iterator}catch(c){return!1}},r=t(),n=function(c){var u={next:function(){var p=c.shift();return{done:p===void 0,value:p}}};return r&&(u[Symbol.iterator]=function(){return u}),u},o=function(c){return encodeURIComponent(c).replace(/%20/g,"+")},i=function(c){return decodeURIComponent(String(c).replace(/\+/g," "))},s=function(){var c=function(p){Object.defineProperty(this,"_entries",{writable:!0,value:{}});var m=typeof p;if(m!=="undefined")if(m==="string")p!==""&&this._fromString(p);else if(p instanceof c){var d=this;p.forEach(function(B,N){d.append(N,B)})}else if(p!==null&&m==="object")if(Object.prototype.toString.call(p)==="[object Array]")for(var h=0;hd[0]?1:0}),c._entries&&(c._entries={});for(var p=0;p1?i(d[1]):"")}})})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Er);(function(e){var t=function(){try{var o=new e.URL("b","http://a");return o.pathname="c d",o.href==="http://a/c%20d"&&o.searchParams}catch(i){return!1}},r=function(){var o=e.URL,i=function(f,c){typeof f!="string"&&(f=String(f)),c&&typeof c!="string"&&(c=String(c));var u=document,p;if(c&&(e.location===void 0||c!==e.location.href)){c=c.toLowerCase(),u=document.implementation.createHTMLDocument(""),p=u.createElement("base"),p.href=c,u.head.appendChild(p);try{if(p.href.indexOf(c)!==0)throw new Error(p.href)}catch(O){throw new Error("URL unable to set base "+c+" due to "+O)}}var m=u.createElement("a");m.href=f,p&&(u.body.appendChild(m),m.href=m.href);var d=u.createElement("input");if(d.type="url",d.value=f,m.protocol===":"||!/:/.test(m.href)||!d.checkValidity()&&!c)throw new TypeError("Invalid URL");Object.defineProperty(this,"_anchorElement",{value:m});var h=new e.URLSearchParams(this.search),v=!0,Y=!0,B=this;["append","delete","set"].forEach(function(O){var Qe=h[O];h[O]=function(){Qe.apply(h,arguments),v&&(Y=!1,B.search=h.toString(),Y=!0)}}),Object.defineProperty(this,"searchParams",{value:h,enumerable:!0});var N=void 0;Object.defineProperty(this,"_updateSearchParams",{enumerable:!1,configurable:!1,writable:!1,value:function(){this.search!==N&&(N=this.search,Y&&(v=!1,this.searchParams._fromString(this.search),v=!0))}})},s=i.prototype,a=function(f){Object.defineProperty(s,f,{get:function(){return this._anchorElement[f]},set:function(c){this._anchorElement[f]=c},enumerable:!0})};["hash","host","hostname","port","protocol"].forEach(function(f){a(f)}),Object.defineProperty(s,"search",{get:function(){return this._anchorElement.search},set:function(f){this._anchorElement.search=f,this._updateSearchParams()},enumerable:!0}),Object.defineProperties(s,{toString:{get:function(){var f=this;return function(){return f.href}}},href:{get:function(){return this._anchorElement.href.replace(/\?$/,"")},set:function(f){this._anchorElement.href=f,this._updateSearchParams()},enumerable:!0},pathname:{get:function(){return this._anchorElement.pathname.replace(/(^\/?)/,"/")},set:function(f){this._anchorElement.pathname=f},enumerable:!0},origin:{get:function(){var f={"http:":80,"https:":443,"ftp:":21}[this._anchorElement.protocol],c=this._anchorElement.port!=f&&this._anchorElement.port!=="";return this._anchorElement.protocol+"//"+this._anchorElement.hostname+(c?":"+this._anchorElement.port:"")},enumerable:!0},password:{get:function(){return""},set:function(f){},enumerable:!0},username:{get:function(){return""},set:function(f){},enumerable:!0}}),i.createObjectURL=function(f){return o.createObjectURL.apply(o,arguments)},i.revokeObjectURL=function(f){return o.revokeObjectURL.apply(o,arguments)},e.URL=i};if(t()||r(),e.location!==void 0&&!("origin"in e.location)){var n=function(){return e.location.protocol+"//"+e.location.hostname+(e.location.port?":"+e.location.port:"")};try{Object.defineProperty(e.location,"origin",{get:n,enumerable:!0})}catch(o){setInterval(function(){e.location.origin=n()},100)}}})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Er)});var qr=Pt((Mt,Nr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Mt=="object"&&typeof Nr=="object"?Nr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Mt=="object"?Mt.ClipboardJS=r():t.ClipboardJS=r()})(Mt,function(){return function(){var e={686:function(n,o,i){"use strict";i.d(o,{default:function(){return Ai}});var s=i(279),a=i.n(s),f=i(370),c=i.n(f),u=i(817),p=i.n(u);function m(j){try{return document.execCommand(j)}catch(T){return!1}}var d=function(T){var E=p()(T);return m("cut"),E},h=d;function v(j){var T=document.documentElement.getAttribute("dir")==="rtl",E=document.createElement("textarea");E.style.fontSize="12pt",E.style.border="0",E.style.padding="0",E.style.margin="0",E.style.position="absolute",E.style[T?"right":"left"]="-9999px";var H=window.pageYOffset||document.documentElement.scrollTop;return E.style.top="".concat(H,"px"),E.setAttribute("readonly",""),E.value=j,E}var Y=function(T,E){var H=v(T);E.container.appendChild(H);var I=p()(H);return m("copy"),H.remove(),I},B=function(T){var E=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},H="";return typeof T=="string"?H=Y(T,E):T instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(T==null?void 0:T.type)?H=Y(T.value,E):(H=p()(T),m("copy")),H},N=B;function O(j){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?O=function(E){return typeof E}:O=function(E){return E&&typeof Symbol=="function"&&E.constructor===Symbol&&E!==Symbol.prototype?"symbol":typeof E},O(j)}var Qe=function(){var T=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},E=T.action,H=E===void 0?"copy":E,I=T.container,q=T.target,Me=T.text;if(H!=="copy"&&H!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(q!==void 0)if(q&&O(q)==="object"&&q.nodeType===1){if(H==="copy"&&q.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(H==="cut"&&(q.hasAttribute("readonly")||q.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Me)return N(Me,{container:I});if(q)return H==="cut"?h(q):N(q,{container:I})},De=Qe;function $e(j){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?$e=function(E){return typeof E}:$e=function(E){return E&&typeof Symbol=="function"&&E.constructor===Symbol&&E!==Symbol.prototype?"symbol":typeof E},$e(j)}function Ei(j,T){if(!(j instanceof T))throw new TypeError("Cannot call a class as a function")}function tn(j,T){for(var E=0;E0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof I.action=="function"?I.action:this.defaultAction,this.target=typeof I.target=="function"?I.target:this.defaultTarget,this.text=typeof I.text=="function"?I.text:this.defaultText,this.container=$e(I.container)==="object"?I.container:document.body}},{key:"listenClick",value:function(I){var q=this;this.listener=c()(I,"click",function(Me){return q.onClick(Me)})}},{key:"onClick",value:function(I){var q=I.delegateTarget||I.currentTarget,Me=this.action(q)||"copy",kt=De({action:Me,container:this.container,target:this.target(q),text:this.text(q)});this.emit(kt?"success":"error",{action:Me,text:kt,trigger:q,clearSelection:function(){q&&q.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(I){return vr("action",I)}},{key:"defaultTarget",value:function(I){var q=vr("target",I);if(q)return document.querySelector(q)}},{key:"defaultText",value:function(I){return vr("text",I)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(I){var q=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return N(I,q)}},{key:"cut",value:function(I){return h(I)}},{key:"isSupported",value:function(){var I=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],q=typeof I=="string"?[I]:I,Me=!!document.queryCommandSupported;return q.forEach(function(kt){Me=Me&&!!document.queryCommandSupported(kt)}),Me}}]),E}(a()),Ai=Li},828:function(n){var o=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,f){for(;a&&a.nodeType!==o;){if(typeof a.matches=="function"&&a.matches(f))return a;a=a.parentNode}}n.exports=s},438:function(n,o,i){var s=i(828);function a(u,p,m,d,h){var v=c.apply(this,arguments);return u.addEventListener(m,v,h),{destroy:function(){u.removeEventListener(m,v,h)}}}function f(u,p,m,d,h){return typeof u.addEventListener=="function"?a.apply(null,arguments):typeof m=="function"?a.bind(null,document).apply(null,arguments):(typeof u=="string"&&(u=document.querySelectorAll(u)),Array.prototype.map.call(u,function(v){return a(v,p,m,d,h)}))}function c(u,p,m,d){return function(h){h.delegateTarget=s(h.target,p),h.delegateTarget&&d.call(u,h)}}n.exports=f},879:function(n,o){o.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},o.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||o.node(i[0]))},o.string=function(i){return typeof i=="string"||i instanceof String},o.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}},370:function(n,o,i){var s=i(879),a=i(438);function f(m,d,h){if(!m&&!d&&!h)throw new Error("Missing required arguments");if(!s.string(d))throw new TypeError("Second argument must be a String");if(!s.fn(h))throw new TypeError("Third argument must be a Function");if(s.node(m))return c(m,d,h);if(s.nodeList(m))return u(m,d,h);if(s.string(m))return p(m,d,h);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(m,d,h){return m.addEventListener(d,h),{destroy:function(){m.removeEventListener(d,h)}}}function u(m,d,h){return Array.prototype.forEach.call(m,function(v){v.addEventListener(d,h)}),{destroy:function(){Array.prototype.forEach.call(m,function(v){v.removeEventListener(d,h)})}}}function p(m,d,h){return a(document.body,m,d,h)}n.exports=f},817:function(n){function o(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var f=window.getSelection(),c=document.createRange();c.selectNodeContents(i),f.removeAllRanges(),f.addRange(c),s=f.toString()}return s}n.exports=o},279:function(n){function o(){}o.prototype={on:function(i,s,a){var f=this.e||(this.e={});return(f[i]||(f[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var f=this;function c(){f.off(i,c),s.apply(a,arguments)}return c._=s,this.on(i,c,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),f=0,c=a.length;for(f;f{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var rs=/["'&<>]/;Yo.exports=ns;function ns(e){var t=""+e,r=rs.exec(t);if(!r)return t;var n,o="",i=0,s=0;for(i=r.index;i0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function W(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),o,i=[],s;try{for(;(t===void 0||t-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(a){s={error:a}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return i}function D(e,t,r){if(r||arguments.length===2)for(var n=0,o=t.length,i;n1||a(m,d)})})}function a(m,d){try{f(n[m](d))}catch(h){p(i[0][3],h)}}function f(m){m.value instanceof et?Promise.resolve(m.value.v).then(c,u):p(i[0][2],m)}function c(m){a("next",m)}function u(m){a("throw",m)}function p(m,d){m(d),i.shift(),i.length&&a(i[0][0],i[0][1])}}function pn(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof Ee=="function"?Ee(e):e[Symbol.iterator](),r={},n("next"),n("throw"),n("return"),r[Symbol.asyncIterator]=function(){return this},r);function n(i){r[i]=e[i]&&function(s){return new Promise(function(a,f){s=e[i](s),o(a,f,s.done,s.value)})}}function o(i,s,a,f){Promise.resolve(f).then(function(c){i({value:c,done:a})},s)}}function C(e){return typeof e=="function"}function at(e){var t=function(n){Error.call(n),n.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var It=at(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(n,o){return o+1+") "+n.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function Ve(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Ie=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,n,o,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=Ee(s),f=a.next();!f.done;f=a.next()){var c=f.value;c.remove(this)}}catch(v){t={error:v}}finally{try{f&&!f.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var u=this.initialTeardown;if(C(u))try{u()}catch(v){i=v instanceof It?v.errors:[v]}var p=this._finalizers;if(p){this._finalizers=null;try{for(var m=Ee(p),d=m.next();!d.done;d=m.next()){var h=d.value;try{ln(h)}catch(v){i=i!=null?i:[],v instanceof It?i=D(D([],W(i)),W(v.errors)):i.push(v)}}}catch(v){n={error:v}}finally{try{d&&!d.done&&(o=m.return)&&o.call(m)}finally{if(n)throw n.error}}}if(i)throw new It(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)ln(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ve(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ve(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Sr=Ie.EMPTY;function jt(e){return e instanceof Ie||e&&"closed"in e&&C(e.remove)&&C(e.add)&&C(e.unsubscribe)}function ln(e){C(e)?e():e.unsubscribe()}var Le={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var st={setTimeout:function(e,t){for(var r=[],n=2;n0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var n=this,o=this,i=o.hasError,s=o.isStopped,a=o.observers;return i||s?Sr:(this.currentObservers=null,a.push(r),new Ie(function(){n.currentObservers=null,Ve(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var n=this,o=n.hasError,i=n.thrownError,s=n.isStopped;o?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new F;return r.source=this,r},t.create=function(r,n){return new xn(r,n)},t}(F);var xn=function(e){ie(t,e);function t(r,n){var o=e.call(this)||this;return o.destination=r,o.source=n,o}return t.prototype.next=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.next)===null||o===void 0||o.call(n,r)},t.prototype.error=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.error)===null||o===void 0||o.call(n,r)},t.prototype.complete=function(){var r,n;(n=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||n===void 0||n.call(r)},t.prototype._subscribe=function(r){var n,o;return(o=(n=this.source)===null||n===void 0?void 0:n.subscribe(r))!==null&&o!==void 0?o:Sr},t}(x);var Et={now:function(){return(Et.delegate||Date).now()},delegate:void 0};var wt=function(e){ie(t,e);function t(r,n,o){r===void 0&&(r=1/0),n===void 0&&(n=1/0),o===void 0&&(o=Et);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=n,i._timestampProvider=o,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=n===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,n),i}return t.prototype.next=function(r){var n=this,o=n.isStopped,i=n._buffer,s=n._infiniteTimeWindow,a=n._timestampProvider,f=n._windowTime;o||(i.push(r),!s&&i.push(a.now()+f)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var n=this._innerSubscribe(r),o=this,i=o._infiniteTimeWindow,s=o._buffer,a=s.slice(),f=0;f0?e.prototype.requestAsyncId.call(this,r,n,o):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,n,o){var i;if(o===void 0&&(o=0),o!=null?o>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,n,o);var s=r.actions;n!=null&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==n&&(ut.cancelAnimationFrame(n),r._scheduled=void 0)},t}(Wt);var Sn=function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var n=this._scheduled;this._scheduled=void 0;var o=this.actions,i;r=r||o.shift();do if(i=r.execute(r.state,r.delay))break;while((r=o[0])&&r.id===n&&o.shift());if(this._active=!1,i){for(;(r=o[0])&&r.id===n&&o.shift();)r.unsubscribe();throw i}},t}(Dt);var Oe=new Sn(wn);var M=new F(function(e){return e.complete()});function Vt(e){return e&&C(e.schedule)}function Cr(e){return e[e.length-1]}function Ye(e){return C(Cr(e))?e.pop():void 0}function Te(e){return Vt(Cr(e))?e.pop():void 0}function zt(e,t){return typeof Cr(e)=="number"?e.pop():t}var pt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Nt(e){return C(e==null?void 0:e.then)}function qt(e){return C(e[ft])}function Kt(e){return Symbol.asyncIterator&&C(e==null?void 0:e[Symbol.asyncIterator])}function Qt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function zi(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Yt=zi();function Gt(e){return C(e==null?void 0:e[Yt])}function Bt(e){return un(this,arguments,function(){var r,n,o,i;return $t(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,et(r.read())];case 3:return n=s.sent(),o=n.value,i=n.done,i?[4,et(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,et(o)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function Jt(e){return C(e==null?void 0:e.getReader)}function U(e){if(e instanceof F)return e;if(e!=null){if(qt(e))return Ni(e);if(pt(e))return qi(e);if(Nt(e))return Ki(e);if(Kt(e))return On(e);if(Gt(e))return Qi(e);if(Jt(e))return Yi(e)}throw Qt(e)}function Ni(e){return new F(function(t){var r=e[ft]();if(C(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function qi(e){return new F(function(t){for(var r=0;r=2;return function(n){return n.pipe(e?A(function(o,i){return e(o,i,n)}):de,ge(1),r?He(t):Dn(function(){return new Zt}))}}function Vn(){for(var e=[],t=0;t=2,!0))}function pe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new x}:t,n=e.resetOnError,o=n===void 0?!0:n,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,f=a===void 0?!0:a;return function(c){var u,p,m,d=0,h=!1,v=!1,Y=function(){p==null||p.unsubscribe(),p=void 0},B=function(){Y(),u=m=void 0,h=v=!1},N=function(){var O=u;B(),O==null||O.unsubscribe()};return y(function(O,Qe){d++,!v&&!h&&Y();var De=m=m!=null?m:r();Qe.add(function(){d--,d===0&&!v&&!h&&(p=$r(N,f))}),De.subscribe(Qe),!u&&d>0&&(u=new rt({next:function($e){return De.next($e)},error:function($e){v=!0,Y(),p=$r(B,o,$e),De.error($e)},complete:function(){h=!0,Y(),p=$r(B,s),De.complete()}}),U(O).subscribe(u))})(c)}}function $r(e,t){for(var r=[],n=2;ne.next(document)),e}function K(e,t=document){return Array.from(t.querySelectorAll(e))}function z(e,t=document){let r=ce(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function ce(e,t=document){return t.querySelector(e)||void 0}function _e(){return document.activeElement instanceof HTMLElement&&document.activeElement||void 0}function tr(e){return L(b(document.body,"focusin"),b(document.body,"focusout")).pipe(ke(1),l(()=>{let t=_e();return typeof t!="undefined"?e.contains(t):!1}),V(e===_e()),J())}function Xe(e){return{x:e.offsetLeft,y:e.offsetTop}}function Kn(e){return L(b(window,"load"),b(window,"resize")).pipe(Ce(0,Oe),l(()=>Xe(e)),V(Xe(e)))}function rr(e){return{x:e.scrollLeft,y:e.scrollTop}}function dt(e){return L(b(e,"scroll"),b(window,"resize")).pipe(Ce(0,Oe),l(()=>rr(e)),V(rr(e)))}var Yn=function(){if(typeof Map!="undefined")return Map;function e(t,r){var n=-1;return t.some(function(o,i){return o[0]===r?(n=i,!0):!1}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(r){var n=e(this.__entries__,r),o=this.__entries__[n];return o&&o[1]},t.prototype.set=function(r,n){var o=e(this.__entries__,r);~o?this.__entries__[o][1]=n:this.__entries__.push([r,n])},t.prototype.delete=function(r){var n=this.__entries__,o=e(n,r);~o&&n.splice(o,1)},t.prototype.has=function(r){return!!~e(this.__entries__,r)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(r,n){n===void 0&&(n=null);for(var o=0,i=this.__entries__;o0},e.prototype.connect_=function(){!Wr||this.connected_||(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),va?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){!Wr||!this.connected_||(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(t){var r=t.propertyName,n=r===void 0?"":r,o=ba.some(function(i){return!!~n.indexOf(i)});o&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),Gn=function(e,t){for(var r=0,n=Object.keys(t);r0},e}(),Jn=typeof WeakMap!="undefined"?new WeakMap:new Yn,Xn=function(){function e(t){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var r=ga.getInstance(),n=new La(t,r,this);Jn.set(this,n)}return e}();["observe","unobserve","disconnect"].forEach(function(e){Xn.prototype[e]=function(){var t;return(t=Jn.get(this))[e].apply(t,arguments)}});var Aa=function(){return typeof nr.ResizeObserver!="undefined"?nr.ResizeObserver:Xn}(),Zn=Aa;var eo=new x,Ca=$(()=>k(new Zn(e=>{for(let t of e)eo.next(t)}))).pipe(g(e=>L(ze,k(e)).pipe(R(()=>e.disconnect()))),X(1));function he(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ye(e){return Ca.pipe(S(t=>t.observe(e)),g(t=>eo.pipe(A(({target:r})=>r===e),R(()=>t.unobserve(e)),l(()=>he(e)))),V(he(e)))}function bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function ar(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var to=new x,Ra=$(()=>k(new IntersectionObserver(e=>{for(let t of e)to.next(t)},{threshold:0}))).pipe(g(e=>L(ze,k(e)).pipe(R(()=>e.disconnect()))),X(1));function sr(e){return Ra.pipe(S(t=>t.observe(e)),g(t=>to.pipe(A(({target:r})=>r===e),R(()=>t.unobserve(e)),l(({isIntersecting:r})=>r))))}function ro(e,t=16){return dt(e).pipe(l(({y:r})=>{let n=he(e),o=bt(e);return r>=o.height-n.height-t}),J())}var cr={drawer:z("[data-md-toggle=drawer]"),search:z("[data-md-toggle=search]")};function no(e){return cr[e].checked}function Ke(e,t){cr[e].checked!==t&&cr[e].click()}function Ue(e){let t=cr[e];return b(t,"change").pipe(l(()=>t.checked),V(t.checked))}function ka(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ha(){return L(b(window,"compositionstart").pipe(l(()=>!0)),b(window,"compositionend").pipe(l(()=>!1))).pipe(V(!1))}function oo(){let e=b(window,"keydown").pipe(A(t=>!(t.metaKey||t.ctrlKey)),l(t=>({mode:no("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),A(({mode:t,type:r})=>{if(t==="global"){let n=_e();if(typeof n!="undefined")return!ka(n,r)}return!0}),pe());return Ha().pipe(g(t=>t?M:e))}function le(){return new URL(location.href)}function ot(e){location.href=e.href}function io(){return new x}function ao(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)ao(e,r)}function _(e,t,...r){let n=document.createElement(e);if(t)for(let o of Object.keys(t))typeof t[o]!="undefined"&&(typeof t[o]!="boolean"?n.setAttribute(o,t[o]):n.setAttribute(o,""));for(let o of r)ao(n,o);return n}function fr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function so(){return location.hash.substring(1)}function Dr(e){let t=_("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Pa(e){return L(b(window,"hashchange"),e).pipe(l(so),V(so()),A(t=>t.length>0),X(1))}function co(e){return Pa(e).pipe(l(t=>ce(`[id="${t}"]`)),A(t=>typeof t!="undefined"))}function Vr(e){let t=matchMedia(e);return er(r=>t.addListener(()=>r(t.matches))).pipe(V(t.matches))}function fo(){let e=matchMedia("print");return L(b(window,"beforeprint").pipe(l(()=>!0)),b(window,"afterprint").pipe(l(()=>!1))).pipe(V(e.matches))}function zr(e,t){return e.pipe(g(r=>r?t():M))}function ur(e,t={credentials:"same-origin"}){return ue(fetch(`${e}`,t)).pipe(fe(()=>M),g(r=>r.status!==200?Ot(()=>new Error(r.statusText)):k(r)))}function We(e,t){return ur(e,t).pipe(g(r=>r.json()),X(1))}function uo(e,t){let r=new DOMParser;return ur(e,t).pipe(g(n=>n.text()),l(n=>r.parseFromString(n,"text/xml")),X(1))}function pr(e){let t=_("script",{src:e});return $(()=>(document.head.appendChild(t),L(b(t,"load"),b(t,"error").pipe(g(()=>Ot(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(l(()=>{}),R(()=>document.head.removeChild(t)),ge(1))))}function po(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function lo(){return L(b(window,"scroll",{passive:!0}),b(window,"resize",{passive:!0})).pipe(l(po),V(po()))}function mo(){return{width:innerWidth,height:innerHeight}}function ho(){return b(window,"resize",{passive:!0}).pipe(l(mo),V(mo()))}function bo(){return G([lo(),ho()]).pipe(l(([e,t])=>({offset:e,size:t})),X(1))}function lr(e,{viewport$:t,header$:r}){let n=t.pipe(ee("size")),o=G([n,r]).pipe(l(()=>Xe(e)));return G([r,t,o]).pipe(l(([{height:i},{offset:s,size:a},{x:f,y:c}])=>({offset:{x:s.x-f,y:s.y-c+i},size:a})))}(()=>{function e(n,o){parent.postMessage(n,o||"*")}function t(...n){return n.reduce((o,i)=>o.then(()=>new Promise(s=>{let a=document.createElement("script");a.src=i,a.onload=s,document.body.appendChild(a)})),Promise.resolve())}var r=class extends EventTarget{constructor(n){super(),this.url=n,this.m=i=>{i.source===this.w&&(this.dispatchEvent(new MessageEvent("message",{data:i.data})),this.onmessage&&this.onmessage(i))},this.e=(i,s,a,f,c)=>{if(s===`${this.url}`){let u=new ErrorEvent("error",{message:i,filename:s,lineno:a,colno:f,error:c});this.dispatchEvent(u),this.onerror&&this.onerror(u)}};let o=document.createElement("iframe");o.hidden=!0,document.body.appendChild(this.iframe=o),this.w.document.open(),this.w.document.write(` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Changelog

+ +

0.10.0 June 14, 2023

+ +
>>> import supervision as sv
+
+>>> cs = sv.ClassificationDataset.from_folder_structure(
+...     root_directory_path='...'
+... )
+
+>>> cs.as_folder_structure(
+...     root_directory_path='...'
+... )
+
+
    +
  • +

    Added [#125]: support for sv.ClassificationDataset.split allowing to divide sv.ClassificationDataset into two parts.

    +
  • +
  • +

    Added [#110]: ability to extract masks from Roboflow API results using sv.Detections.from_roboflow.

    +
  • +
  • +

    Added [commit hash]: Supervision Quickstart notebook where you can learn more about Detection, Dataset and Video APIs.

    +
  • +
  • +

    Changed [#135]: sv.get_video_frames_generator documentation to better describe actual behavior.

    +
  • +
+

0.9.0 June 7, 2023

+
    +
  • Added [#118]: ability to select sv.Detections by index, list of indexes or slice. Here is an example illustrating the new selection methods.
  • +
+
>>> import supervision as sv
+
+>>> detections = sv.Detections(...)
+>>> len(detections[0])
+1
+>>> len(detections[[0, 1]])
+2
+>>> len(detections[0:2])
+2
+
+
    +
  • +

    Added [#101]: ability to extract masks from YOLOv8 result using sv.Detections.from_yolov8. Here is an example illustrating how to extract boolean masks from the result of the YOLOv8 model inference.

    +
  • +
  • +

    Added [#122]: ability to crop image using sv.crop. Here is an example showing how to get a separate crop for each detection in sv.Detections.

    +
  • +
  • +

    Added [#120]: ability to conveniently save multiple images into directory using sv.ImageSink. Here is an example showing how to save every tenth video frame as a separate image.

    +
  • +
+
>>> import supervision as sv
+
+>>> with sv.ImageSink(target_dir_path='target/directory/path') as sink:
+...     for image in sv.get_video_frames_generator(source_path='source_video.mp4', stride=10):
+...         sink.save_image(image=image)
+
+
    +
  • Fixed [#106]: inconvenient handling of sv.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 May 17, 2023

+
    +
  • Added [#100]: support for dataset inheritance. The current Dataset got renamed to DetectionDataset. Now DetectionDataset inherits from BaseDataset. This change was made to enforce the future consistency of APIs of different types of computer vision datasets.
  • +
  • Added [#100]: ability to save datasets in YOLO format using DetectionDataset.as_yolo.
  • +
+
>>> import roboflow
+>>> from roboflow import Roboflow
+>>> import supervision as sv
+
+>>> roboflow.login()
+
+>>> rf = Roboflow()
+
+>>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
+>>> dataset = project.version(PROJECT_VERSION).download("yolov5")
+
+>>> ds = 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.classes
+['dog', 'person']
+
+ +
>>> import supervision as sv
+
+>>> ds = sv.DetectionDataset(...)
+>>> train_ds, test_ds = ds.split(split_ratio=0.7, random_state=42, shuffle=True)
+
+>>> len(train_ds), len(test_ds)
+(700, 300)
+
+
    +
  • Changed [#100]: default value of approximation_percentage parameter from 0.75 to 0.0 in DetectionDataset.as_yolo and DetectionDataset.as_pascal_voc.
  • +
+

0.7.0 May 11, 2023

+
    +
  • Added [#91]: Detections.from_yolo_nas to enable seamless integration with YOLO-NAS model.
  • +
  • Added [#86]: ability to load datasets in YOLO format using Dataset.from_yolo.
  • +
  • Added [#84]: Detections.merge to merge multiple Detections objects together.
  • +
  • Fixed [#81]: LineZoneAnnotator.annotate does not return annotated frame.
  • +
  • Changed [#44]: LineZoneAnnotator.annotate to allow for custom text for the in and out tags.
  • +
+

0.6.0 April 19, 2023

+
    +
  • Added [#71]: initial Dataset support and ability to save Detections in Pascal VOC XML format.
  • +
  • Added [#71]: new mask_to_polygons, filter_polygons_by_area, polygon_to_xyxy and approximate_polygon utilities.
  • +
  • Added [#72]: ability to load Pascal VOC XML object detections dataset as Dataset.
  • +
  • Changed [#70]: order of Detections attributes to make it consistent with order of objects in __iter__ tuple.
  • +
  • Changed [#71]: generate_2d_mask to polygon_to_mask.
  • +
+

0.5.2 April 13, 2023

+
    +
  • Fixed [#63]: LineZone.trigger function expects 4 values instead of 5.
  • +
+

0.5.1 April 12, 2023

+
    +
  • Fixed Detections.__getitem__ method did not return mask for selected item.
  • +
  • Fixed Detections.area crashed for mask detections.
  • +
+

0.5.0 April 10, 2023

+
    +
  • Added [#58]: Detections.mask to enable segmentation support.
  • +
  • Added [#58]: MaskAnnotator to allow easy Detections.mask annotation.
  • +
  • Added [#58]: Detections.from_sam to enable native Segment Anything Model (SAM) support.
  • +
  • Changed [#58]: Detections.area behaviour to work not only with boxes but also with masks.
  • +
+

0.4.0 April 5, 2023

+
    +
  • Added [#46]: Detections.empty to allow easy creation of empty Detections objects.
  • +
  • Added [#56]: Detections.from_roboflow to allow easy creation of Detections objects from Roboflow API inference results.
  • +
  • Added [#56]: plot_images_grid to allow easy plotting of multiple images on single plot.
  • +
  • Added [#56]: initial support for Pascal VOC XML format with detections_to_voc_xml method.
  • +
  • Changed [#56]: show_frame_in_notebook refactored and renamed to plot_image.
  • +
+

0.3.2 March 23, 2023

+
    +
  • Changed [#50]: Allow Detections.class_id to be None.
  • +
+

0.3.1 March 6, 2023

+
    +
  • Fixed [#41]: PolygonZone throws an exception when the object touches the bottom edge of the image.
  • +
  • Fixed [#42]: Detections.wth_nms method throws an exception when Detections is empty.
  • +
  • Changed [#36]: Detections.wth_nms support class agnostic and non-class agnostic case.
  • +
+

0.3.0 March 6, 2023

+
    +
  • Changed: Allow Detections.confidence to be None.
  • +
  • Added: Detections.from_transformers and Detections.from_detectron2 to enable seamless integration with Transformers and Detectron2 models.
  • +
  • Added: Detections.area to dynamically calculate bounding box area.
  • +
  • Added: Detections.wth_nms to filter out double detections with NMS. Initial - only class agnostic - implementation.
  • +
+

0.2.0 February 2, 2023

+
    +
  • Added: Advanced Detections filtering with pandas-like API.
  • +
  • Added: Detections.from_yolov5 and Detections.from_yolov8 to enable seamless integration with YOLOv5 and YOLOv8 models.
  • +
+

0.1.0 January 19, 2023

+

Say hello to Supervision 👋

+ + + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/classification/core/index.html b/classification/core/index.html new file mode 100644 index 000000000..2ffbc55f5 --- /dev/null +++ b/classification/core/index.html @@ -0,0 +1,1465 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Core - Supervision + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Core

+ +

Classifications

+ + +
+ + + +
+ + + +
+ Source code in supervision/classification/core.py +
28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
@dataclass
+class Classifications:
+    class_id: np.ndarray
+    confidence: Optional[np.ndarray] = None
+
+    def __post_init__(self) -> None:
+        """
+        Validate the classification inputs.
+        """
+        n = len(self.class_id)
+
+        _validate_class_ids(self.class_id, n)
+        _validate_confidence(self.confidence, n)
+
+    @classmethod
+    def from_yolov8(cls, yolov8_results) -> Classifications:
+        """
+        Creates a Classifications instance from a [YOLOv8](https://github.com/ultralytics/ultralytics) inference result.
+
+        Args:
+            yolov8_results (ultralytics.yolo.engine.results.Results): The output Results instance from YOLOv8
+
+        Returns:
+            Detections: A new Classifications object.
+
+        Example:
+            ```python
+            >>> import cv2
+            >>> from ultralytics import YOLO
+            >>> import supervision as sv
+
+            >>> image = cv2.imread(SOURCE_IMAGE_PATH)
+            >>> model = YOLO('yolov8s-cls.pt')
+            >>> result = model(image)[0]
+            >>> classifications = sv.Classifications.from_yolov8(result)
+            ```
+        """
+        confidence = yolov8_results.probs.data.cpu().numpy()
+        return cls(class_id=np.arange(confidence.shape[0]), confidence=confidence)
+
+    def get_top_k(self, k: int) -> Tuple[np.ndarray, np.ndarray]:
+        """
+        Retrieve the top k class IDs and confidences, ordered in descending order by confidence.
+
+        Args:
+            k (int): The number of top class IDs and confidences to retrieve.
+
+        Returns:
+            Tuple[np.ndarray, np.ndarray]: A tuple containing the top k class IDs and confidences.
+
+        Example:
+            ```python
+            >>> import supervision as sv
+
+            >>> classifications = sv.Classifications(...)
+
+            >>> classifications.get_top_k(1)
+
+            (array([1]), array([0.9]))
+            ```
+        """
+        if self.confidence is None:
+            raise ValueError("top_k could not be calculated, confidence is None")
+
+        order = np.argsort(self.confidence)[::-1]
+        top_k_order = order[:k]
+        top_k_class_id = self.class_id[top_k_order]
+        top_k_confidence = self.confidence[top_k_order]
+
+        return top_k_class_id, top_k_confidence
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+__post_init__() + +

+ + +
+ +

Validate the classification inputs.

+ +
+ Source code in supervision/classification/core.py +
33
+34
+35
+36
+37
+38
+39
+40
def __post_init__(self) -> None:
+    """
+    Validate the classification inputs.
+    """
+    n = len(self.class_id)
+
+    _validate_class_ids(self.class_id, n)
+    _validate_confidence(self.confidence, n)
+
+
+
+ +
+ +
+ + + +

+from_yolov8(yolov8_results) + + + classmethod + + +

+ + +
+ +

Creates a Classifications instance from a YOLOv8 inference result.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
yolov8_results + ultralytics.yolo.engine.results.Results + +
+

The output Results instance from YOLOv8

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Detections + Classifications + +
+

A new Classifications object.

+
+
+ +
+ Example +
>>> import cv2
+>>> from ultralytics import YOLO
+>>> import supervision as sv
+
+>>> image = cv2.imread(SOURCE_IMAGE_PATH)
+>>> model = YOLO('yolov8s-cls.pt')
+>>> result = model(image)[0]
+>>> classifications = sv.Classifications.from_yolov8(result)
+
+
+
+ Source code in supervision/classification/core.py +
42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
@classmethod
+def from_yolov8(cls, yolov8_results) -> Classifications:
+    """
+    Creates a Classifications instance from a [YOLOv8](https://github.com/ultralytics/ultralytics) inference result.
+
+    Args:
+        yolov8_results (ultralytics.yolo.engine.results.Results): The output Results instance from YOLOv8
+
+    Returns:
+        Detections: A new Classifications object.
+
+    Example:
+        ```python
+        >>> import cv2
+        >>> from ultralytics import YOLO
+        >>> import supervision as sv
+
+        >>> image = cv2.imread(SOURCE_IMAGE_PATH)
+        >>> model = YOLO('yolov8s-cls.pt')
+        >>> result = model(image)[0]
+        >>> classifications = sv.Classifications.from_yolov8(result)
+        ```
+    """
+    confidence = yolov8_results.probs.data.cpu().numpy()
+    return cls(class_id=np.arange(confidence.shape[0]), confidence=confidence)
+
+
+
+ +
+ +
+ + + +

+get_top_k(k) + +

+ + +
+ +

Retrieve the top k class IDs and confidences, ordered in descending order by confidence.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
k + int + +
+

The number of top class IDs and confidences to retrieve.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Tuple[np.ndarray, np.ndarray] + +
+

Tuple[np.ndarray, np.ndarray]: A tuple containing the top k class IDs and confidences.

+
+
+ +
+ Example +
>>> import supervision as sv
+
+>>> classifications = sv.Classifications(...)
+
+>>> classifications.get_top_k(1)
+
+(array([1]), array([0.9]))
+
+
+
+ Source code in supervision/classification/core.py +
68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
def get_top_k(self, k: int) -> Tuple[np.ndarray, np.ndarray]:
+    """
+    Retrieve the top k class IDs and confidences, ordered in descending order by confidence.
+
+    Args:
+        k (int): The number of top class IDs and confidences to retrieve.
+
+    Returns:
+        Tuple[np.ndarray, np.ndarray]: A tuple containing the top k class IDs and confidences.
+
+    Example:
+        ```python
+        >>> import supervision as sv
+
+        >>> classifications = sv.Classifications(...)
+
+        >>> classifications.get_top_k(1)
+
+        (array([1]), array([0.9]))
+        ```
+    """
+    if self.confidence is None:
+        raise ValueError("top_k could not be calculated, confidence is None")
+
+    order = np.argsort(self.confidence)[::-1]
+    top_k_order = order[:k]
+    top_k_class_id = self.class_id[top_k_order]
+    top_k_confidence = self.confidence[top_k_order]
+
+    return top_k_class_id, top_k_confidence
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/dataset/core/index.html b/dataset/core/index.html new file mode 100644 index 000000000..3f1acc104 --- /dev/null +++ b/dataset/core/index.html @@ -0,0 +1,4522 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Core - Supervision + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Core

+ +
+

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.

+
+

DetectionDataset

+ + +
+ + + +
+

+ Bases: BaseDataset

+ + +

Dataclass containing information about object detection dataset.

+ +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
classes + List[str] + +
+

List containing dataset class names.

+
+
images + Dict[str, np.ndarray] + +
+

Dictionary mapping image name to image.

+
+
annotations + Dict[str, Detections] + +
+

Dictionary mapping image name to annotations.

+
+
+ + +
+ Source code in supervision/dataset/core.py +
 44
+ 45
+ 46
+ 47
+ 48
+ 49
+ 50
+ 51
+ 52
+ 53
+ 54
+ 55
+ 56
+ 57
+ 58
+ 59
+ 60
+ 61
+ 62
+ 63
+ 64
+ 65
+ 66
+ 67
+ 68
+ 69
+ 70
+ 71
+ 72
+ 73
+ 74
+ 75
+ 76
+ 77
+ 78
+ 79
+ 80
+ 81
+ 82
+ 83
+ 84
+ 85
+ 86
+ 87
+ 88
+ 89
+ 90
+ 91
+ 92
+ 93
+ 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
@dataclass
+class DetectionDataset(BaseDataset):
+    """
+    Dataclass containing information about object detection dataset.
+
+    Attributes:
+        classes (List[str]): List containing dataset class names.
+        images (Dict[str, np.ndarray]): Dictionary mapping image name to image.
+        annotations (Dict[str, Detections]): Dictionary mapping image name to annotations.
+    """
+
+    classes: List[str]
+    images: Dict[str, np.ndarray]
+    annotations: Dict[str, Detections]
+
+    def __len__(self) -> int:
+        """
+        Return the number of images in the dataset.
+
+        Returns:
+            int: The number of images.
+        """
+        return len(self.images)
+
+    def __iter__(self) -> Iterator[Tuple[str, np.ndarray, Detections]]:
+        """
+        Iterate over the images and annotations in the dataset.
+
+        Yields:
+            Iterator[Tuple[str, np.ndarray, Detections]]: An iterator that yields tuples containing the image name,
+                                                          the image data, and its corresponding annotation.
+        """
+        for image_name, image in self.images.items():
+            yield image_name, image, self.annotations.get(image_name, None)
+
+    def split(
+        self, split_ratio=0.8, random_state=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 set to the entire dataset.
+            random_state (int, optional): The seed for the random number generator. This is used for reproducibility.
+            shuffle (bool, optional): Whether to shuffle the data before splitting.
+
+        Returns:
+            Tuple[DetectionDataset, DetectionDataset]: A tuple containing the training and testing datasets.
+
+        Example:
+            ```python
+            >>> import supervision as sv
+
+            >>> ds = sv.DetectionDataset(...)
+            >>> train_ds, test_ds = ds.split(split_ratio=0.7, random_state=42, shuffle=True)
+            >>> len(train_ds), len(test_ds)
+            (700, 300)
+            ```
+        """
+
+        image_names = list(self.images.keys())
+        train_names, test_names = train_test_split(
+            data=image_names,
+            train_ratio=split_ratio,
+            random_state=random_state,
+            shuffle=shuffle,
+        )
+
+        train_dataset = DetectionDataset(
+            classes=self.classes,
+            images={name: self.images[name] for name in train_names},
+            annotations={name: self.annotations[name] for name in train_names},
+        )
+        test_dataset = DetectionDataset(
+            classes=self.classes,
+            images={name: self.images[name] for name in test_names},
+            annotations={name: self.annotations[name] for name in test_names},
+        )
+        return train_dataset, test_dataset
+
+    def as_pascal_voc(
+        self,
+        images_directory_path: Optional[str] = None,
+        annotations_directory_path: Optional[str] = None,
+        min_image_area_percentage: float = 0.0,
+        max_image_area_percentage: float = 1.0,
+        approximation_percentage: float = 0.0,
+    ) -> None:
+        """
+        Exports the dataset to PASCAL VOC format. This method saves the images and their corresponding annotations in
+        PASCAL VOC format, which consists of XML files. The method allows filtering the detections based on their area
+        percentage.
+
+        Args:
+            images_directory_path (Optional[str]): The path to the directory where the images should be saved.
+                If not provided, images will not be saved.
+            annotations_directory_path (Optional[str]): The path to the directory where the annotations in
+                PASCAL VOC format should be saved. If not provided, annotations will not be saved.
+            min_image_area_percentage (float): The minimum percentage of detection area relative to
+                the image area for a detection to be included.
+            max_image_area_percentage (float): The maximum percentage of detection area relative to
+                the image area for a detection to be included.
+            approximation_percentage (float): The percentage of polygon points to be removed from the input polygon, in the range [0, 1).
+        """
+        if images_directory_path:
+            images_path = Path(images_directory_path)
+            images_path.mkdir(parents=True, exist_ok=True)
+
+        if annotations_directory_path:
+            annotations_path = Path(annotations_directory_path)
+            annotations_path.mkdir(parents=True, exist_ok=True)
+
+        for image_name, image in self.images.items():
+            detections = self.annotations[image_name]
+
+            if images_directory_path:
+                cv2.imwrite(str(images_path / image_name), image)
+
+            if annotations_directory_path:
+                annotation_name = Path(image_name).stem
+                pascal_voc_xml = detections_to_pascal_voc(
+                    detections=detections,
+                    classes=self.classes,
+                    filename=image_name,
+                    image_shape=image.shape,
+                    min_image_area_percentage=min_image_area_percentage,
+                    max_image_area_percentage=max_image_area_percentage,
+                    approximation_percentage=approximation_percentage,
+                )
+
+                with open(annotations_path / f"{annotation_name}.xml", "w") as f:
+                    f.write(pascal_voc_xml)
+
+    @classmethod
+    def from_pascal_voc(
+        cls, images_directory_path: str, annotations_directory_path: str
+    ) -> DetectionDataset:
+        """
+        Creates a Dataset instance from PASCAL VOC formatted data.
+
+        Args:
+            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 XML annotations.
+
+        Returns:
+            DetectionDataset: A DetectionDataset instance containing the loaded images and annotations.
+
+        Example:
+            ```python
+            >>> import roboflow
+            >>> from roboflow import Roboflow
+            >>> import supervision as sv
+
+            >>> roboflow.login()
+
+            >>> rf = Roboflow()
+
+            >>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
+            >>> dataset = project.version(PROJECT_VERSION).download("voc")
+
+            >>> ds = sv.DetectionDataset.from_yolo(
+            ...     images_directory_path=f"{dataset.location}/train/images",
+            ...     annotations_directory_path=f"{dataset.location}/train/labels"
+            ... )
+
+            >>> ds.classes
+            ['dog', 'person']
+            ```
+        """
+        image_paths = list_files_with_extensions(
+            directory=images_directory_path, extensions=["jpg", "jpeg", "png"]
+        )
+        annotation_paths = list_files_with_extensions(
+            directory=annotations_directory_path, extensions=["xml"]
+        )
+
+        raw_annotations: List[Tuple[str, Detections, List[str]]] = [
+            load_pascal_voc_annotations(annotation_path=str(annotation_path))
+            for annotation_path in annotation_paths
+        ]
+
+        classes = []
+        for annotation in raw_annotations:
+            classes.extend(annotation[2])
+        classes = list(set(classes))
+
+        for annotation in raw_annotations:
+            class_id = [classes.index(class_name) for class_name in annotation[2]]
+            annotation[1].class_id = np.array(class_id)
+
+        images = {
+            image_path.name: cv2.imread(str(image_path)) for image_path in image_paths
+        }
+
+        annotations = {
+            image_name: detections for image_name, detections, _ in raw_annotations
+        }
+        return DetectionDataset(classes=classes, images=images, annotations=annotations)
+
+    @classmethod
+    def from_yolo(
+        cls,
+        images_directory_path: str,
+        annotations_directory_path: str,
+        data_yaml_path: str,
+        force_masks: bool = False,
+    ) -> DetectionDataset:
+        """
+        Creates a Dataset instance from YOLO formatted data.
+
+        Args:
+            images_directory_path (str): The path to the directory containing the images.
+            annotations_directory_path (str): The path to the directory 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 for all annotations, regardless of whether they are present.
+
+        Returns:
+            DetectionDataset: A DetectionDataset instance containing the loaded images and annotations.
+
+        Example:
+            ```python
+            >>> import roboflow
+            >>> from roboflow import Roboflow
+            >>> import supervision as sv
+
+            >>> roboflow.login()
+
+            >>> rf = Roboflow()
+
+            >>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
+            >>> dataset = project.version(PROJECT_VERSION).download("yolov5")
+
+            >>> ds = 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.classes
+            ['dog', 'person']
+            ```
+        """
+        classes, images, annotations = load_yolo_annotations(
+            images_directory_path=images_directory_path,
+            annotations_directory_path=annotations_directory_path,
+            data_yaml_path=data_yaml_path,
+            force_masks=force_masks,
+        )
+        return DetectionDataset(classes=classes, images=images, annotations=annotations)
+
+    def as_yolo(
+        self,
+        images_directory_path: Optional[str] = None,
+        annotations_directory_path: Optional[str] = None,
+        data_yaml_path: Optional[str] = None,
+        min_image_area_percentage: float = 0.0,
+        max_image_area_percentage: float = 1.0,
+        approximation_percentage: float = 0.0,
+    ) -> None:
+        """
+        Exports the dataset to YOLO format. This method saves the images and their corresponding
+        annotations in YOLO format, which is a simple text file that describes an object in the image. It also allows
+        for the optional saving of a data.yaml file, used in YOLOv5, that contains metadata about the dataset.
+
+        The method allows filtering the detections based on their area percentage and offers an option for polygon approximation.
+
+        Args:
+            images_directory_path (Optional[str]): The path to the directory where the images should be saved.
+                If not provided, images will not be saved.
+            annotations_directory_path (Optional[str]): The path to the directory where the annotations in
+                YOLO format should be saved. If not provided, annotations will not be saved.
+            data_yaml_path (Optional[str]): The path where the data.yaml file should be saved.
+                If not provided, the file will not be saved.
+            min_image_area_percentage (float): The minimum percentage of detection area relative to
+                the image area for a detection to be included.
+            max_image_area_percentage (float): The maximum percentage of detection area relative to
+                the image area for a detection to be included.
+            approximation_percentage (float): The percentage of polygon points to be removed from the input polygon,
+                in the range [0, 1). This is useful for simplifying the annotations.
+        """
+        if images_directory_path is not None:
+            save_dataset_images(
+                images_directory_path=images_directory_path, images=self.images
+            )
+        if annotations_directory_path is not None:
+            save_yolo_annotations(
+                annotations_directory_path=annotations_directory_path,
+                images=self.images,
+                annotations=self.annotations,
+                min_image_area_percentage=min_image_area_percentage,
+                max_image_area_percentage=max_image_area_percentage,
+                approximation_percentage=approximation_percentage,
+            )
+        if data_yaml_path is not None:
+            save_data_yaml(data_yaml_path=data_yaml_path, classes=self.classes)
+
+    @classmethod
+    def from_coco(
+        cls,
+        images_directory_path: str,
+        annotations_path: str,
+        force_masks: bool = False,
+    ) -> DetectionDataset:
+        """
+        Creates a Dataset instance from YOLO formatted data.
+
+        Args:
+            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, forces masks to be loaded for all annotations, regardless of whether they are present.
+
+        Returns:
+            DetectionDataset: A DetectionDataset instance containing the loaded images and annotations.
+
+        Example:
+            ```python
+            >>> import roboflow
+            >>> from roboflow import Roboflow
+            >>> import supervision as sv
+
+            >>> roboflow.login()
+
+            >>> rf = Roboflow()
+
+            >>> project = rf.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",
+            ... )
+
+            >>> ds.classes
+            ['dog', 'person']
+            ```
+        """
+        classes, images, annotations = load_coco_annotations(
+            images_directory_path=images_directory_path,
+            annotations_path=annotations_path,
+            force_masks=force_masks,
+        )
+        return DetectionDataset(classes=classes, images=images, annotations=annotations)
+
+    def as_coco(
+        self,
+        images_directory_path: Optional[str] = None,
+        annotations_path: Optional[str] = None,
+        min_image_area_percentage: float = 0.0,
+        max_image_area_percentage: float = 1.0,
+        approximation_percentage: float = 0.0,
+        licenses: Optional[list] = None,
+        info: Optional[dict] = None,
+    ) -> None:
+        """
+        Exports the dataset to COCO format. This method saves the images and their corresponding
+        annotations in COCO format, which is a simple json file that describes an object in the image.
+        Annotation json file also include category maps.
+
+        The method allows filtering the detections based on their area percentage and offers an option for polygon approximation.
+
+        Args:
+            images_directory_path (Optional[str]): The path to the directory where the images should be saved.
+                If not provided, images will not be saved.
+            annotations_directory_path (Optional[str]): The path to the directory where the annotations in
+                YOLO format should be saved. If not provided, annotations will not be saved.
+            min_image_area_percentage (float): The minimum percentage of detection area relative to
+                the image area for a detection to be included.
+            max_image_area_percentage (float): The maximum percentage of detection area relative to
+                the image area for a detection to be included.
+            approximation_percentage (float): The percentage of polygon points to be removed from the input polygon,
+                in the range [0, 1). This is useful for simplifying the annotations.
+            licenses (Optional[str]): List of licenses for images
+            info (Optional[dict]): Information of Dataset as dictionary
+        """
+        if images_directory_path is not None:
+            save_dataset_images(
+                images_directory_path=images_directory_path, images=self.images
+            )
+        if annotations_path is not None:
+            save_coco_annotations(
+                annotation_path=annotations_path,
+                images=self.images,
+                annotations=self.annotations,
+                classes=self.classes,
+                min_image_area_percentage=min_image_area_percentage,
+                max_image_area_percentage=max_image_area_percentage,
+                approximation_percentage=approximation_percentage,
+                licenses=licenses,
+                info=info,
+            )
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+__iter__() + +

+ + +
+ +

Iterate over the images and annotations in the dataset.

+ +

Yields:

+ + + + + + + + + + + + + +
TypeDescription
+ str + +
+

Iterator[Tuple[str, np.ndarray, Detections]]: An iterator that yields tuples containing the image name, + the image data, and its corresponding annotation.

+
+
+ +
+ Source code in supervision/dataset/core.py +
68
+69
+70
+71
+72
+73
+74
+75
+76
+77
def __iter__(self) -> Iterator[Tuple[str, np.ndarray, Detections]]:
+    """
+    Iterate over the images and annotations in the dataset.
+
+    Yields:
+        Iterator[Tuple[str, np.ndarray, Detections]]: An iterator that yields tuples containing the image name,
+                                                      the image data, and its corresponding annotation.
+    """
+    for image_name, image in self.images.items():
+        yield image_name, image, self.annotations.get(image_name, None)
+
+
+
+ +
+ +
+ + + +

+__len__() + +

+ + +
+ +

Return the number of images in the dataset.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
int + int + +
+

The number of images.

+
+
+ +
+ Source code in supervision/dataset/core.py +
59
+60
+61
+62
+63
+64
+65
+66
def __len__(self) -> int:
+    """
+    Return the number of images in the dataset.
+
+    Returns:
+        int: The number of images.
+    """
+    return len(self.images)
+
+
+
+ +
+ +
+ + + +

+as_coco(images_directory_path=None, annotations_path=None, min_image_area_percentage=0.0, max_image_area_percentage=1.0, approximation_percentage=0.0, licenses=None, info=None) + +

+ + +
+ +

Exports the dataset to COCO format. This method saves the images and their corresponding +annotations in COCO format, which is a simple json file that describes an object in the image. +Annotation json file also include category maps.

+

The method allows filtering the detections based on their area percentage and offers an option for polygon approximation.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
images_directory_path + Optional[str] + +
+

The path to the directory where the images should be saved. +If not provided, images will not be saved.

+
+
+ None +
annotations_directory_path + Optional[str] + +
+

The path to the directory where the annotations in +YOLO format should be saved. If not provided, annotations will not be saved.

+
+
+ required +
min_image_area_percentage + float + +
+

The minimum percentage of detection area relative to +the image area for a detection to be included.

+
+
+ 0.0 +
max_image_area_percentage + float + +
+

The maximum percentage of detection area relative to +the image area for a detection to be included.

+
+
+ 1.0 +
approximation_percentage + float + +
+

The percentage of polygon points to be removed from the input polygon, +in the range [0, 1). This is useful for simplifying the annotations.

+
+
+ 0.0 +
licenses + Optional[str] + +
+

List of licenses for images

+
+
+ None +
info + Optional[dict] + +
+

Information of Dataset as dictionary

+
+
+ None +
+ +
+ Source code in supervision/dataset/core.py +
387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
def as_coco(
+    self,
+    images_directory_path: Optional[str] = None,
+    annotations_path: Optional[str] = None,
+    min_image_area_percentage: float = 0.0,
+    max_image_area_percentage: float = 1.0,
+    approximation_percentage: float = 0.0,
+    licenses: Optional[list] = None,
+    info: Optional[dict] = None,
+) -> None:
+    """
+    Exports the dataset to COCO format. This method saves the images and their corresponding
+    annotations in COCO format, which is a simple json file that describes an object in the image.
+    Annotation json file also include category maps.
+
+    The method allows filtering the detections based on their area percentage and offers an option for polygon approximation.
+
+    Args:
+        images_directory_path (Optional[str]): The path to the directory where the images should be saved.
+            If not provided, images will not be saved.
+        annotations_directory_path (Optional[str]): The path to the directory where the annotations in
+            YOLO format should be saved. If not provided, annotations will not be saved.
+        min_image_area_percentage (float): The minimum percentage of detection area relative to
+            the image area for a detection to be included.
+        max_image_area_percentage (float): The maximum percentage of detection area relative to
+            the image area for a detection to be included.
+        approximation_percentage (float): The percentage of polygon points to be removed from the input polygon,
+            in the range [0, 1). This is useful for simplifying the annotations.
+        licenses (Optional[str]): List of licenses for images
+        info (Optional[dict]): Information of Dataset as dictionary
+    """
+    if images_directory_path is not None:
+        save_dataset_images(
+            images_directory_path=images_directory_path, images=self.images
+        )
+    if annotations_path is not None:
+        save_coco_annotations(
+            annotation_path=annotations_path,
+            images=self.images,
+            annotations=self.annotations,
+            classes=self.classes,
+            min_image_area_percentage=min_image_area_percentage,
+            max_image_area_percentage=max_image_area_percentage,
+            approximation_percentage=approximation_percentage,
+            licenses=licenses,
+            info=info,
+        )
+
+
+
+ +
+ +
+ + + +

+as_pascal_voc(images_directory_path=None, annotations_directory_path=None, min_image_area_percentage=0.0, max_image_area_percentage=1.0, approximation_percentage=0.0) + +

+ + +
+ +

Exports the dataset to PASCAL VOC format. This method saves the images and their corresponding annotations in +PASCAL VOC format, which consists of XML files. The method allows filtering the detections based on their area +percentage.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
images_directory_path + Optional[str] + +
+

The path to the directory where the images should be saved. +If not provided, images will not be saved.

+
+
+ None +
annotations_directory_path + Optional[str] + +
+

The path to the directory where the annotations in +PASCAL VOC format should be saved. If not provided, annotations will not be saved.

+
+
+ None +
min_image_area_percentage + float + +
+

The minimum percentage of detection area relative to +the image area for a detection to be included.

+
+
+ 0.0 +
max_image_area_percentage + float + +
+

The maximum percentage of detection area relative to +the image area for a detection to be included.

+
+
+ 1.0 +
approximation_percentage + float + +
+

The percentage of polygon points to be removed from the input polygon, in the range [0, 1).

+
+
+ 0.0 +
+ +
+ Source code in supervision/dataset/core.py +
124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
def as_pascal_voc(
+    self,
+    images_directory_path: Optional[str] = None,
+    annotations_directory_path: Optional[str] = None,
+    min_image_area_percentage: float = 0.0,
+    max_image_area_percentage: float = 1.0,
+    approximation_percentage: float = 0.0,
+) -> None:
+    """
+    Exports the dataset to PASCAL VOC format. This method saves the images and their corresponding annotations in
+    PASCAL VOC format, which consists of XML files. The method allows filtering the detections based on their area
+    percentage.
+
+    Args:
+        images_directory_path (Optional[str]): The path to the directory where the images should be saved.
+            If not provided, images will not be saved.
+        annotations_directory_path (Optional[str]): The path to the directory where the annotations in
+            PASCAL VOC format should be saved. If not provided, annotations will not be saved.
+        min_image_area_percentage (float): The minimum percentage of detection area relative to
+            the image area for a detection to be included.
+        max_image_area_percentage (float): The maximum percentage of detection area relative to
+            the image area for a detection to be included.
+        approximation_percentage (float): The percentage of polygon points to be removed from the input polygon, in the range [0, 1).
+    """
+    if images_directory_path:
+        images_path = Path(images_directory_path)
+        images_path.mkdir(parents=True, exist_ok=True)
+
+    if annotations_directory_path:
+        annotations_path = Path(annotations_directory_path)
+        annotations_path.mkdir(parents=True, exist_ok=True)
+
+    for image_name, image in self.images.items():
+        detections = self.annotations[image_name]
+
+        if images_directory_path:
+            cv2.imwrite(str(images_path / image_name), image)
+
+        if annotations_directory_path:
+            annotation_name = Path(image_name).stem
+            pascal_voc_xml = detections_to_pascal_voc(
+                detections=detections,
+                classes=self.classes,
+                filename=image_name,
+                image_shape=image.shape,
+                min_image_area_percentage=min_image_area_percentage,
+                max_image_area_percentage=max_image_area_percentage,
+                approximation_percentage=approximation_percentage,
+            )
+
+            with open(annotations_path / f"{annotation_name}.xml", "w") as f:
+                f.write(pascal_voc_xml)
+
+
+
+ +
+ +
+ + + +

+as_yolo(images_directory_path=None, annotations_directory_path=None, data_yaml_path=None, min_image_area_percentage=0.0, max_image_area_percentage=1.0, approximation_percentage=0.0) + +

+ + +
+ +

Exports the dataset to YOLO format. This method saves the images and their corresponding +annotations in YOLO format, which is a simple text file that describes an object in the image. It also allows +for the optional saving of a data.yaml file, used in YOLOv5, that contains metadata about the dataset.

+

The method allows filtering the detections based on their area percentage and offers an option for polygon approximation.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
images_directory_path + Optional[str] + +
+

The path to the directory where the images should be saved. +If not provided, images will not be saved.

+
+
+ None +
annotations_directory_path + Optional[str] + +
+

The path to the directory where the annotations in +YOLO format should be saved. If not provided, annotations will not be saved.

+
+
+ None +
data_yaml_path + Optional[str] + +
+

The path where the data.yaml file should be saved. +If not provided, the file will not be saved.

+
+
+ None +
min_image_area_percentage + float + +
+

The minimum percentage of detection area relative to +the image area for a detection to be included.

+
+
+ 0.0 +
max_image_area_percentage + float + +
+

The maximum percentage of detection area relative to +the image area for a detection to be included.

+
+
+ 1.0 +
approximation_percentage + float + +
+

The percentage of polygon points to be removed from the input polygon, +in the range [0, 1). This is useful for simplifying the annotations.

+
+
+ 0.0 +
+ +
+ Source code in supervision/dataset/core.py +
294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
def as_yolo(
+    self,
+    images_directory_path: Optional[str] = None,
+    annotations_directory_path: Optional[str] = None,
+    data_yaml_path: Optional[str] = None,
+    min_image_area_percentage: float = 0.0,
+    max_image_area_percentage: float = 1.0,
+    approximation_percentage: float = 0.0,
+) -> None:
+    """
+    Exports the dataset to YOLO format. This method saves the images and their corresponding
+    annotations in YOLO format, which is a simple text file that describes an object in the image. It also allows
+    for the optional saving of a data.yaml file, used in YOLOv5, that contains metadata about the dataset.
+
+    The method allows filtering the detections based on their area percentage and offers an option for polygon approximation.
+
+    Args:
+        images_directory_path (Optional[str]): The path to the directory where the images should be saved.
+            If not provided, images will not be saved.
+        annotations_directory_path (Optional[str]): The path to the directory where the annotations in
+            YOLO format should be saved. If not provided, annotations will not be saved.
+        data_yaml_path (Optional[str]): The path where the data.yaml file should be saved.
+            If not provided, the file will not be saved.
+        min_image_area_percentage (float): The minimum percentage of detection area relative to
+            the image area for a detection to be included.
+        max_image_area_percentage (float): The maximum percentage of detection area relative to
+            the image area for a detection to be included.
+        approximation_percentage (float): The percentage of polygon points to be removed from the input polygon,
+            in the range [0, 1). This is useful for simplifying the annotations.
+    """
+    if images_directory_path is not None:
+        save_dataset_images(
+            images_directory_path=images_directory_path, images=self.images
+        )
+    if annotations_directory_path is not None:
+        save_yolo_annotations(
+            annotations_directory_path=annotations_directory_path,
+            images=self.images,
+            annotations=self.annotations,
+            min_image_area_percentage=min_image_area_percentage,
+            max_image_area_percentage=max_image_area_percentage,
+            approximation_percentage=approximation_percentage,
+        )
+    if data_yaml_path is not None:
+        save_data_yaml(data_yaml_path=data_yaml_path, classes=self.classes)
+
+
+
+ +
+ +
+ + + +

+from_coco(images_directory_path, annotations_path, force_masks=False) + + + classmethod + + +

+ + +
+ +

Creates a Dataset instance from YOLO formatted data.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
images_directory_path + str + +
+

The path to the directory containing the images.

+
+
+ required +
annotations_path + str + +
+

The path to the json annotation files.

+
+
+ required +
force_masks + bool + +
+

If True, forces masks to be loaded for all annotations, regardless of whether they are present.

+
+
+ False +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
DetectionDataset + DetectionDataset + +
+

A DetectionDataset instance containing the loaded images and annotations.

+
+
+ +
+ Example +
>>> import roboflow
+>>> from roboflow import Roboflow
+>>> import supervision as sv
+
+>>> roboflow.login()
+
+>>> rf = Roboflow()
+
+>>> project = rf.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",
+... )
+
+>>> ds.classes
+['dog', 'person']
+
+
+
+ Source code in supervision/dataset/core.py +
340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
@classmethod
+def from_coco(
+    cls,
+    images_directory_path: str,
+    annotations_path: str,
+    force_masks: bool = False,
+) -> DetectionDataset:
+    """
+    Creates a Dataset instance from YOLO formatted data.
+
+    Args:
+        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, forces masks to be loaded for all annotations, regardless of whether they are present.
+
+    Returns:
+        DetectionDataset: A DetectionDataset instance containing the loaded images and annotations.
+
+    Example:
+        ```python
+        >>> import roboflow
+        >>> from roboflow import Roboflow
+        >>> import supervision as sv
+
+        >>> roboflow.login()
+
+        >>> rf = Roboflow()
+
+        >>> project = rf.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",
+        ... )
+
+        >>> ds.classes
+        ['dog', 'person']
+        ```
+    """
+    classes, images, annotations = load_coco_annotations(
+        images_directory_path=images_directory_path,
+        annotations_path=annotations_path,
+        force_masks=force_masks,
+    )
+    return DetectionDataset(classes=classes, images=images, annotations=annotations)
+
+
+
+ +
+ +
+ + + +

+from_pascal_voc(images_directory_path, annotations_directory_path) + + + classmethod + + +

+ + +
+ +

Creates a Dataset instance from PASCAL VOC formatted data.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
images_directory_path + str + +
+

The path to the directory containing the images.

+
+
+ required +
annotations_directory_path + str + +
+

The path to the directory containing the PASCAL VOC XML annotations.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
DetectionDataset + DetectionDataset + +
+

A DetectionDataset instance containing the loaded images and annotations.

+
+
+ +
+ Example +
>>> import roboflow
+>>> from roboflow import Roboflow
+>>> import supervision as sv
+
+>>> roboflow.login()
+
+>>> rf = Roboflow()
+
+>>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
+>>> dataset = project.version(PROJECT_VERSION).download("voc")
+
+>>> ds = sv.DetectionDataset.from_yolo(
+...     images_directory_path=f"{dataset.location}/train/images",
+...     annotations_directory_path=f"{dataset.location}/train/labels"
+... )
+
+>>> ds.classes
+['dog', 'person']
+
+
+
+ Source code in supervision/dataset/core.py +
177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
@classmethod
+def from_pascal_voc(
+    cls, images_directory_path: str, annotations_directory_path: str
+) -> DetectionDataset:
+    """
+    Creates a Dataset instance from PASCAL VOC formatted data.
+
+    Args:
+        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 XML annotations.
+
+    Returns:
+        DetectionDataset: A DetectionDataset instance containing the loaded images and annotations.
+
+    Example:
+        ```python
+        >>> import roboflow
+        >>> from roboflow import Roboflow
+        >>> import supervision as sv
+
+        >>> roboflow.login()
+
+        >>> rf = Roboflow()
+
+        >>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
+        >>> dataset = project.version(PROJECT_VERSION).download("voc")
+
+        >>> ds = sv.DetectionDataset.from_yolo(
+        ...     images_directory_path=f"{dataset.location}/train/images",
+        ...     annotations_directory_path=f"{dataset.location}/train/labels"
+        ... )
+
+        >>> ds.classes
+        ['dog', 'person']
+        ```
+    """
+    image_paths = list_files_with_extensions(
+        directory=images_directory_path, extensions=["jpg", "jpeg", "png"]
+    )
+    annotation_paths = list_files_with_extensions(
+        directory=annotations_directory_path, extensions=["xml"]
+    )
+
+    raw_annotations: List[Tuple[str, Detections, List[str]]] = [
+        load_pascal_voc_annotations(annotation_path=str(annotation_path))
+        for annotation_path in annotation_paths
+    ]
+
+    classes = []
+    for annotation in raw_annotations:
+        classes.extend(annotation[2])
+    classes = list(set(classes))
+
+    for annotation in raw_annotations:
+        class_id = [classes.index(class_name) for class_name in annotation[2]]
+        annotation[1].class_id = np.array(class_id)
+
+    images = {
+        image_path.name: cv2.imread(str(image_path)) for image_path in image_paths
+    }
+
+    annotations = {
+        image_name: detections for image_name, detections, _ in raw_annotations
+    }
+    return DetectionDataset(classes=classes, images=images, annotations=annotations)
+
+
+
+ +
+ +
+ + + +

+from_yolo(images_directory_path, annotations_directory_path, data_yaml_path, force_masks=False) + + + classmethod + + +

+ + +
+ +

Creates a Dataset instance from YOLO formatted data.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
images_directory_path + str + +
+

The path to the directory containing the images.

+
+
+ required +
annotations_directory_path + str + +
+

The path to the directory containing the YOLO annotation files.

+
+
+ required +
data_yaml_path + str + +
+

The path to the data YAML file containing class information.

+
+
+ required +
force_masks + bool + +
+

If True, forces masks to be loaded for all annotations, regardless of whether they are present.

+
+
+ False +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
DetectionDataset + DetectionDataset + +
+

A DetectionDataset instance containing the loaded images and annotations.

+
+
+ +
+ Example +
>>> import roboflow
+>>> from roboflow import Roboflow
+>>> import supervision as sv
+
+>>> roboflow.login()
+
+>>> rf = Roboflow()
+
+>>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
+>>> dataset = project.version(PROJECT_VERSION).download("yolov5")
+
+>>> ds = 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.classes
+['dog', 'person']
+
+
+
+ Source code in supervision/dataset/core.py +
243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
@classmethod
+def from_yolo(
+    cls,
+    images_directory_path: str,
+    annotations_directory_path: str,
+    data_yaml_path: str,
+    force_masks: bool = False,
+) -> DetectionDataset:
+    """
+    Creates a Dataset instance from YOLO formatted data.
+
+    Args:
+        images_directory_path (str): The path to the directory containing the images.
+        annotations_directory_path (str): The path to the directory 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 for all annotations, regardless of whether they are present.
+
+    Returns:
+        DetectionDataset: A DetectionDataset instance containing the loaded images and annotations.
+
+    Example:
+        ```python
+        >>> import roboflow
+        >>> from roboflow import Roboflow
+        >>> import supervision as sv
+
+        >>> roboflow.login()
+
+        >>> rf = Roboflow()
+
+        >>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
+        >>> dataset = project.version(PROJECT_VERSION).download("yolov5")
+
+        >>> ds = 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.classes
+        ['dog', 'person']
+        ```
+    """
+    classes, images, annotations = load_yolo_annotations(
+        images_directory_path=images_directory_path,
+        annotations_directory_path=annotations_directory_path,
+        data_yaml_path=data_yaml_path,
+        force_masks=force_masks,
+    )
+    return DetectionDataset(classes=classes, images=images, annotations=annotations)
+
+
+
+ +
+ +
+ + + +

+split(split_ratio=0.8, random_state=None, shuffle=True) + +

+ + +
+ +

Splits the dataset into two parts (training and testing) using the provided split_ratio.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
split_ratio + float + +
+

The ratio of the training set to the entire dataset.

+
+
+ 0.8 +
random_state + int + +
+

The seed for the random number generator. This is used for reproducibility.

+
+
+ None +
shuffle + bool + +
+

Whether to shuffle the data before splitting.

+
+
+ True +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Tuple[DetectionDataset, DetectionDataset] + +
+

Tuple[DetectionDataset, DetectionDataset]: A tuple containing the training and testing datasets.

+
+
+ +
+ Example +
>>> import supervision as sv
+
+>>> ds = sv.DetectionDataset(...)
+>>> train_ds, test_ds = ds.split(split_ratio=0.7, random_state=42, shuffle=True)
+>>> len(train_ds), len(test_ds)
+(700, 300)
+
+
+
+ Source code in supervision/dataset/core.py +
 79
+ 80
+ 81
+ 82
+ 83
+ 84
+ 85
+ 86
+ 87
+ 88
+ 89
+ 90
+ 91
+ 92
+ 93
+ 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
def split(
+    self, split_ratio=0.8, random_state=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 set to the entire dataset.
+        random_state (int, optional): The seed for the random number generator. This is used for reproducibility.
+        shuffle (bool, optional): Whether to shuffle the data before splitting.
+
+    Returns:
+        Tuple[DetectionDataset, DetectionDataset]: A tuple containing the training and testing datasets.
+
+    Example:
+        ```python
+        >>> import supervision as sv
+
+        >>> ds = sv.DetectionDataset(...)
+        >>> train_ds, test_ds = ds.split(split_ratio=0.7, random_state=42, shuffle=True)
+        >>> len(train_ds), len(test_ds)
+        (700, 300)
+        ```
+    """
+
+    image_names = list(self.images.keys())
+    train_names, test_names = train_test_split(
+        data=image_names,
+        train_ratio=split_ratio,
+        random_state=random_state,
+        shuffle=shuffle,
+    )
+
+    train_dataset = DetectionDataset(
+        classes=self.classes,
+        images={name: self.images[name] for name in train_names},
+        annotations={name: self.annotations[name] for name in train_names},
+    )
+    test_dataset = DetectionDataset(
+        classes=self.classes,
+        images={name: self.images[name] for name in test_names},
+        annotations={name: self.annotations[name] for name in test_names},
+    )
+    return train_dataset, test_dataset
+
+
+
+ +
+ + + +
+ +
+ +

ClassificationDataset

+ + +
+ + + +
+

+ Bases: BaseDataset

+ + +

Dataclass containing information about a classification dataset.

+ +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
classes + List[str] + +
+

List containing dataset class names.

+
+
images + Dict[str, np.ndarray] + +
+

Dictionary mapping image name to image.

+
+
annotations + Dict[str, Detections] + +
+

Dictionary mapping image name to annotations.

+
+
+ + +
+ Source code in supervision/dataset/core.py +
436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
@dataclass
+class ClassificationDataset(BaseDataset):
+    """
+    Dataclass containing information about a classification dataset.
+
+    Attributes:
+        classes (List[str]): List containing dataset class names.
+        images (Dict[str, np.ndarray]): Dictionary mapping image name to image.
+        annotations (Dict[str, Detections]): Dictionary mapping image name to annotations.
+    """
+
+    classes: List[str]
+    images: Dict[str, np.ndarray]
+    annotations: Dict[str, Classifications]
+
+    def __len__(self) -> int:
+        return len(self.images)
+
+    def split(
+        self, split_ratio=0.8, random_state=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 set to the entire dataset.
+            random_state (int, optional): The seed for the random number generator. This is used for reproducibility.
+            shuffle (bool, optional): Whether to shuffle the data before splitting.
+
+        Returns:
+            Tuple[ClassificationDataset, ClassificationDataset]: A tuple containing the training and testing datasets.
+
+        Example:
+            ```python
+            >>> import supervision as sv
+
+            >>> cd = sv.ClassificationDataset(...)
+            >>> train_cd, test_cd = cd.split(split_ratio=0.7, random_state=42, shuffle=True)
+            >>> len(train_cd), len(test_cd)
+            (700, 300)
+            ```
+        """
+        image_names = list(self.images.keys())
+        train_names, test_names = train_test_split(
+            data=image_names,
+            train_ratio=split_ratio,
+            random_state=random_state,
+            shuffle=shuffle,
+        )
+
+        train_dataset = ClassificationDataset(
+            classes=self.classes,
+            images={name: self.images[name] for name in train_names},
+            annotations={name: self.annotations[name] for name in train_names},
+        )
+        test_dataset = ClassificationDataset(
+            classes=self.classes,
+            images={name: self.images[name] for name in test_names},
+            annotations={name: self.annotations[name] for name in test_names},
+        )
+        return train_dataset, test_dataset
+
+    def as_folder_structure(self, root_directory_path: str) -> None:
+        """
+        Saves the dataset as a multi-class folder structure.
+
+        Args:
+            root_directory_path (str): The path to the directory where the dataset will be saved.
+        """
+        os.makedirs(root_directory_path, exist_ok=True)
+
+        for class_name in self.classes:
+            os.makedirs(os.path.join(root_directory_path, class_name), exist_ok=True)
+
+        for image_name in self.images:
+            classification = self.annotations[image_name]
+            image = self.images[image_name]
+            class_id = (
+                classification.class_id[0]
+                if classification.confidence is None
+                else classification.get_top_k(1)[0]
+            )
+            class_name = self.classes[class_id]
+            image_path = os.path.join(root_directory_path, class_name, image_name)
+            cv2.imwrite(image_path, image)
+
+    @classmethod
+    def from_folder_structure(cls, root_directory_path: str) -> ClassificationDataset:
+        """
+        Load data from a multiclass folder structure into a ClassificationDataset.
+
+        Args:
+            root_directory_path (str): The path to the dataset directory.
+
+        Returns:
+            ClassificationDataset: The dataset.
+
+        Example:
+            ```python
+            >>> import roboflow
+            >>> from roboflow import Roboflow
+            >>> import supervision as sv
+
+            >>> roboflow.login()
+
+            >>> rf = Roboflow()
+
+            >>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
+            >>> dataset = project.version(PROJECT_VERSION).download("folder")
+
+            >>> cd = sv.ClassificationDataset.from_folder_structure(
+            ...     root_directory_path=f"{dataset.location}/train"
+            ... )
+            ```
+        """
+        classes = os.listdir(root_directory_path)
+        classes = sorted(set(classes))
+
+        images = {}
+        annotations = {}
+
+        for class_name in classes:
+            class_id = classes.index(class_name)
+
+            for image in os.listdir(os.path.join(root_directory_path, class_name)):
+                image_dir = os.path.join(root_directory_path, class_name, image)
+                images[image] = cv2.imread(image_dir)
+                annotations[image] = Classifications(
+                    class_id=np.array([class_id]),
+                )
+
+        return cls(
+            classes=classes,
+            images=images,
+            annotations=annotations,
+        )
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+as_folder_structure(root_directory_path) + +

+ + +
+ +

Saves the dataset as a multi-class folder structure.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
root_directory_path + str + +
+

The path to the directory where the dataset will be saved.

+
+
+ required +
+ +
+ Source code in supervision/dataset/core.py +
498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
def as_folder_structure(self, root_directory_path: str) -> None:
+    """
+    Saves the dataset as a multi-class folder structure.
+
+    Args:
+        root_directory_path (str): The path to the directory where the dataset will be saved.
+    """
+    os.makedirs(root_directory_path, exist_ok=True)
+
+    for class_name in self.classes:
+        os.makedirs(os.path.join(root_directory_path, class_name), exist_ok=True)
+
+    for image_name in self.images:
+        classification = self.annotations[image_name]
+        image = self.images[image_name]
+        class_id = (
+            classification.class_id[0]
+            if classification.confidence is None
+            else classification.get_top_k(1)[0]
+        )
+        class_name = self.classes[class_id]
+        image_path = os.path.join(root_directory_path, class_name, image_name)
+        cv2.imwrite(image_path, image)
+
+
+
+ +
+ +
+ + + +

+from_folder_structure(root_directory_path) + + + classmethod + + +

+ + +
+ +

Load data from a multiclass folder structure into a ClassificationDataset.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
root_directory_path + str + +
+

The path to the dataset directory.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
ClassificationDataset + ClassificationDataset + +
+

The dataset.

+
+
+ +
+ Example +
>>> import roboflow
+>>> from roboflow import Roboflow
+>>> import supervision as sv
+
+>>> roboflow.login()
+
+>>> rf = Roboflow()
+
+>>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
+>>> dataset = project.version(PROJECT_VERSION).download("folder")
+
+>>> cd = sv.ClassificationDataset.from_folder_structure(
+...     root_directory_path=f"{dataset.location}/train"
+... )
+
+
+
+ Source code in supervision/dataset/core.py +
522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
@classmethod
+def from_folder_structure(cls, root_directory_path: str) -> ClassificationDataset:
+    """
+    Load data from a multiclass folder structure into a ClassificationDataset.
+
+    Args:
+        root_directory_path (str): The path to the dataset directory.
+
+    Returns:
+        ClassificationDataset: The dataset.
+
+    Example:
+        ```python
+        >>> import roboflow
+        >>> from roboflow import Roboflow
+        >>> import supervision as sv
+
+        >>> roboflow.login()
+
+        >>> rf = Roboflow()
+
+        >>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
+        >>> dataset = project.version(PROJECT_VERSION).download("folder")
+
+        >>> cd = sv.ClassificationDataset.from_folder_structure(
+        ...     root_directory_path=f"{dataset.location}/train"
+        ... )
+        ```
+    """
+    classes = os.listdir(root_directory_path)
+    classes = sorted(set(classes))
+
+    images = {}
+    annotations = {}
+
+    for class_name in classes:
+        class_id = classes.index(class_name)
+
+        for image in os.listdir(os.path.join(root_directory_path, class_name)):
+            image_dir = os.path.join(root_directory_path, class_name, image)
+            images[image] = cv2.imread(image_dir)
+            annotations[image] = Classifications(
+                class_id=np.array([class_id]),
+            )
+
+    return cls(
+        classes=classes,
+        images=images,
+        annotations=annotations,
+    )
+
+
+
+ +
+ +
+ + + +

+split(split_ratio=0.8, random_state=None, shuffle=True) + +

+ + +
+ +

Splits the dataset into two parts (training and testing) using the provided split_ratio.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
split_ratio + float + +
+

The ratio of the training set to the entire dataset.

+
+
+ 0.8 +
random_state + int + +
+

The seed for the random number generator. This is used for reproducibility.

+
+
+ None +
shuffle + bool + +
+

Whether to shuffle the data before splitting.

+
+
+ True +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Tuple[ClassificationDataset, ClassificationDataset] + +
+

Tuple[ClassificationDataset, ClassificationDataset]: A tuple containing the training and testing datasets.

+
+
+ +
+ Example +
>>> import supervision as sv
+
+>>> cd = sv.ClassificationDataset(...)
+>>> train_cd, test_cd = cd.split(split_ratio=0.7, random_state=42, shuffle=True)
+>>> len(train_cd), len(test_cd)
+(700, 300)
+
+
+
+ Source code in supervision/dataset/core.py +
454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
def split(
+    self, split_ratio=0.8, random_state=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 set to the entire dataset.
+        random_state (int, optional): The seed for the random number generator. This is used for reproducibility.
+        shuffle (bool, optional): Whether to shuffle the data before splitting.
+
+    Returns:
+        Tuple[ClassificationDataset, ClassificationDataset]: A tuple containing the training and testing datasets.
+
+    Example:
+        ```python
+        >>> import supervision as sv
+
+        >>> cd = sv.ClassificationDataset(...)
+        >>> train_cd, test_cd = cd.split(split_ratio=0.7, random_state=42, shuffle=True)
+        >>> len(train_cd), len(test_cd)
+        (700, 300)
+        ```
+    """
+    image_names = list(self.images.keys())
+    train_names, test_names = train_test_split(
+        data=image_names,
+        train_ratio=split_ratio,
+        random_state=random_state,
+        shuffle=shuffle,
+    )
+
+    train_dataset = ClassificationDataset(
+        classes=self.classes,
+        images={name: self.images[name] for name in train_names},
+        annotations={name: self.annotations[name] for name in train_names},
+    )
+    test_dataset = ClassificationDataset(
+        classes=self.classes,
+        images={name: self.images[name] for name in test_names},
+        annotations={name: self.annotations[name] for name in test_names},
+    )
+    return train_dataset, test_dataset
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/detection/annotate/index.html b/detection/annotate/index.html new file mode 100644 index 000000000..fc0cb3c7a --- /dev/null +++ b/detection/annotate/index.html @@ -0,0 +1,2044 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Annotate - Supervision + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+ +
+ + + +
+
+ + + + + + + +

Annotate

+ +

BoxAnnotator

+ + +
+ + + +
+ + +

A class for drawing bounding boxes on an image using detections provided.

+ +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
color + Union[Color, ColorPalette] + +
+

The color to draw the bounding box, can be a single color or a color palette

+
+
thickness + int + +
+

The thickness of the bounding box lines, default is 2

+
+
text_color + Color + +
+

The color of the text on the bounding box, default is white

+
+
text_scale + float + +
+

The scale of the text on the bounding box, default is 0.5

+
+
text_thickness + int + +
+

The thickness of the text on the bounding box, default is 1

+
+
text_padding + int + +
+

The padding around the text on the bounding box, default is 5

+
+
+ + +
+ Source code in supervision/detection/annotate.py +
 10
+ 11
+ 12
+ 13
+ 14
+ 15
+ 16
+ 17
+ 18
+ 19
+ 20
+ 21
+ 22
+ 23
+ 24
+ 25
+ 26
+ 27
+ 28
+ 29
+ 30
+ 31
+ 32
+ 33
+ 34
+ 35
+ 36
+ 37
+ 38
+ 39
+ 40
+ 41
+ 42
+ 43
+ 44
+ 45
+ 46
+ 47
+ 48
+ 49
+ 50
+ 51
+ 52
+ 53
+ 54
+ 55
+ 56
+ 57
+ 58
+ 59
+ 60
+ 61
+ 62
+ 63
+ 64
+ 65
+ 66
+ 67
+ 68
+ 69
+ 70
+ 71
+ 72
+ 73
+ 74
+ 75
+ 76
+ 77
+ 78
+ 79
+ 80
+ 81
+ 82
+ 83
+ 84
+ 85
+ 86
+ 87
+ 88
+ 89
+ 90
+ 91
+ 92
+ 93
+ 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
class BoxAnnotator:
+    """
+    A class for drawing bounding boxes on an image using detections provided.
+
+    Attributes:
+        color (Union[Color, ColorPalette]): The color to draw the bounding box, can be a single color or a color palette
+        thickness (int): The thickness of the bounding box lines, default is 2
+        text_color (Color): The color of the text on the bounding box, default is white
+        text_scale (float): The scale of the text on the bounding box, default is 0.5
+        text_thickness (int): The thickness of the text on the bounding box, default is 1
+        text_padding (int): The padding around the text on the bounding box, default is 5
+
+    """
+
+    def __init__(
+        self,
+        color: Union[Color, ColorPalette] = ColorPalette.default(),
+        thickness: int = 2,
+        text_color: Color = Color.black(),
+        text_scale: float = 0.5,
+        text_thickness: int = 1,
+        text_padding: int = 10,
+    ):
+        self.color: Union[Color, ColorPalette] = color
+        self.thickness: int = thickness
+        self.text_color: Color = text_color
+        self.text_scale: float = text_scale
+        self.text_thickness: int = text_thickness
+        self.text_padding: int = text_padding
+
+    def annotate(
+        self,
+        scene: np.ndarray,
+        detections: Detections,
+        labels: Optional[List[str]] = None,
+        skip_label: bool = False,
+    ) -> np.ndarray:
+        """
+        Draws bounding boxes on the frame using the detections provided.
+
+        Args:
+            scene (np.ndarray): The image on which the bounding boxes will be drawn
+            detections (Detections): The detections for which the bounding boxes will be drawn
+            labels (Optional[List[str]]): An optional list of labels corresponding to each detection. If `labels` are not provided, corresponding `class_id` will be used as label.
+            skip_label (bool): Is set to `True`, skips bounding box label annotation.
+        Returns:
+            np.ndarray: The image with the bounding boxes drawn on it
+
+        Example:
+            ```python
+            >>> import supervision as sv
+
+            >>> classes = ['person', ...]
+            >>> image = ...
+            >>> detections = sv.Detections(...)
+
+            >>> box_annotator = sv.BoxAnnotator()
+            >>> labels = [
+            ...     f"{classes[class_id]} {confidence:0.2f}"
+            ...     for _, _, confidence, class_id, _
+            ...     in detections
+            ... ]
+            >>> annotated_frame = box_annotator.annotate(
+            ...     scene=image.copy(),
+            ...     detections=detections,
+            ...     labels=labels
+            ... )
+            ```
+        """
+        font = cv2.FONT_HERSHEY_SIMPLEX
+        for i in range(len(detections)):
+            x1, y1, x2, y2 = detections.xyxy[i].astype(int)
+            class_id = (
+                detections.class_id[i] if detections.class_id is not None else None
+            )
+            idx = class_id if class_id is not None else i
+            color = (
+                self.color.by_idx(idx)
+                if isinstance(self.color, ColorPalette)
+                else self.color
+            )
+            cv2.rectangle(
+                img=scene,
+                pt1=(x1, y1),
+                pt2=(x2, y2),
+                color=color.as_bgr(),
+                thickness=self.thickness,
+            )
+            if skip_label:
+                continue
+
+            text = (
+                f"{class_id}"
+                if (labels is None or len(detections) != len(labels))
+                else labels[i]
+            )
+
+            text_width, text_height = cv2.getTextSize(
+                text=text,
+                fontFace=font,
+                fontScale=self.text_scale,
+                thickness=self.text_thickness,
+            )[0]
+
+            text_x = x1 + self.text_padding
+            text_y = y1 - self.text_padding
+
+            text_background_x1 = x1
+            text_background_y1 = y1 - 2 * self.text_padding - text_height
+
+            text_background_x2 = x1 + 2 * self.text_padding + text_width
+            text_background_y2 = y1
+
+            cv2.rectangle(
+                img=scene,
+                pt1=(text_background_x1, text_background_y1),
+                pt2=(text_background_x2, text_background_y2),
+                color=color.as_bgr(),
+                thickness=cv2.FILLED,
+            )
+            cv2.putText(
+                img=scene,
+                text=text,
+                org=(text_x, text_y),
+                fontFace=font,
+                fontScale=self.text_scale,
+                color=self.text_color.as_rgb(),
+                thickness=self.text_thickness,
+                lineType=cv2.LINE_AA,
+            )
+        return scene
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+annotate(scene, detections, labels=None, skip_label=False) + +

+ + +
+ +

Draws bounding boxes on the frame using the detections provided.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
scene + np.ndarray + +
+

The image on which the bounding boxes will be drawn

+
+
+ required +
detections + Detections + +
+

The detections for which the bounding boxes will be drawn

+
+
+ required +
labels + Optional[List[str]] + +
+

An optional list of labels corresponding to each detection. If labels are not provided, corresponding class_id will be used as label.

+
+
+ None +
skip_label + bool + +
+

Is set to True, skips bounding box label annotation.

+
+
+ False +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ np.ndarray + +
+

np.ndarray: The image with the bounding boxes drawn on it

+
+
+ +
+ Example +
>>> import supervision as sv
+
+>>> classes = ['person', ...]
+>>> image = ...
+>>> detections = sv.Detections(...)
+
+>>> box_annotator = sv.BoxAnnotator()
+>>> labels = [
+...     f"{classes[class_id]} {confidence:0.2f}"
+...     for _, _, confidence, class_id, _
+...     in detections
+... ]
+>>> annotated_frame = box_annotator.annotate(
+...     scene=image.copy(),
+...     detections=detections,
+...     labels=labels
+... )
+
+
+
+ Source code in supervision/detection/annotate.py +
 40
+ 41
+ 42
+ 43
+ 44
+ 45
+ 46
+ 47
+ 48
+ 49
+ 50
+ 51
+ 52
+ 53
+ 54
+ 55
+ 56
+ 57
+ 58
+ 59
+ 60
+ 61
+ 62
+ 63
+ 64
+ 65
+ 66
+ 67
+ 68
+ 69
+ 70
+ 71
+ 72
+ 73
+ 74
+ 75
+ 76
+ 77
+ 78
+ 79
+ 80
+ 81
+ 82
+ 83
+ 84
+ 85
+ 86
+ 87
+ 88
+ 89
+ 90
+ 91
+ 92
+ 93
+ 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
def annotate(
+    self,
+    scene: np.ndarray,
+    detections: Detections,
+    labels: Optional[List[str]] = None,
+    skip_label: bool = False,
+) -> np.ndarray:
+    """
+    Draws bounding boxes on the frame using the detections provided.
+
+    Args:
+        scene (np.ndarray): The image on which the bounding boxes will be drawn
+        detections (Detections): The detections for which the bounding boxes will be drawn
+        labels (Optional[List[str]]): An optional list of labels corresponding to each detection. If `labels` are not provided, corresponding `class_id` will be used as label.
+        skip_label (bool): Is set to `True`, skips bounding box label annotation.
+    Returns:
+        np.ndarray: The image with the bounding boxes drawn on it
+
+    Example:
+        ```python
+        >>> import supervision as sv
+
+        >>> classes = ['person', ...]
+        >>> image = ...
+        >>> detections = sv.Detections(...)
+
+        >>> box_annotator = sv.BoxAnnotator()
+        >>> labels = [
+        ...     f"{classes[class_id]} {confidence:0.2f}"
+        ...     for _, _, confidence, class_id, _
+        ...     in detections
+        ... ]
+        >>> annotated_frame = box_annotator.annotate(
+        ...     scene=image.copy(),
+        ...     detections=detections,
+        ...     labels=labels
+        ... )
+        ```
+    """
+    font = cv2.FONT_HERSHEY_SIMPLEX
+    for i in range(len(detections)):
+        x1, y1, x2, y2 = detections.xyxy[i].astype(int)
+        class_id = (
+            detections.class_id[i] if detections.class_id is not None else None
+        )
+        idx = class_id if class_id is not None else i
+        color = (
+            self.color.by_idx(idx)
+            if isinstance(self.color, ColorPalette)
+            else self.color
+        )
+        cv2.rectangle(
+            img=scene,
+            pt1=(x1, y1),
+            pt2=(x2, y2),
+            color=color.as_bgr(),
+            thickness=self.thickness,
+        )
+        if skip_label:
+            continue
+
+        text = (
+            f"{class_id}"
+            if (labels is None or len(detections) != len(labels))
+            else labels[i]
+        )
+
+        text_width, text_height = cv2.getTextSize(
+            text=text,
+            fontFace=font,
+            fontScale=self.text_scale,
+            thickness=self.text_thickness,
+        )[0]
+
+        text_x = x1 + self.text_padding
+        text_y = y1 - self.text_padding
+
+        text_background_x1 = x1
+        text_background_y1 = y1 - 2 * self.text_padding - text_height
+
+        text_background_x2 = x1 + 2 * self.text_padding + text_width
+        text_background_y2 = y1
+
+        cv2.rectangle(
+            img=scene,
+            pt1=(text_background_x1, text_background_y1),
+            pt2=(text_background_x2, text_background_y2),
+            color=color.as_bgr(),
+            thickness=cv2.FILLED,
+        )
+        cv2.putText(
+            img=scene,
+            text=text,
+            org=(text_x, text_y),
+            fontFace=font,
+            fontScale=self.text_scale,
+            color=self.text_color.as_rgb(),
+            thickness=self.text_thickness,
+            lineType=cv2.LINE_AA,
+        )
+    return scene
+
+
+
+ +
+ + + +
+ +
+ +

MaskAnnotator

+ + +
+ + + +
+ + +

A class for overlaying masks on an image using detections provided.

+ +

Attributes:

+ + + + + + + + + + + + + + + +
NameTypeDescription
color + Union[Color, ColorPalette] + +
+

The color to fill the mask, can be a single color or a color palette

+
+
+ + +
+ Source code in supervision/detection/annotate.py +
143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
class MaskAnnotator:
+    """
+    A class for overlaying masks on an image using detections provided.
+
+    Attributes:
+        color (Union[Color, ColorPalette]): The color to fill the mask, can be a single color or a color palette
+    """
+
+    def __init__(
+        self,
+        color: Union[Color, ColorPalette] = ColorPalette.default(),
+    ):
+        self.color: Union[Color, ColorPalette] = color
+
+    def annotate(
+        self, scene: np.ndarray, detections: Detections, opacity: float = 0.5
+    ) -> np.ndarray:
+        """
+        Overlays the masks on the given image based on the provided detections, with a specified opacity.
+
+        Args:
+            scene (np.ndarray): The image on which the masks will be overlaid
+            detections (Detections): The detections for which the masks will be overlaid
+            opacity (float): The opacity of the masks, between 0 and 1, default is 0.5
+
+        Returns:
+            np.ndarray: The image with the masks overlaid
+        """
+        if detections.mask is None:
+            return scene
+
+        for i in np.flip(np.argsort(detections.area)):
+            class_id = (
+                detections.class_id[i] if detections.class_id is not None else None
+            )
+            idx = class_id if class_id is not None else i
+            color = (
+                self.color.by_idx(idx)
+                if isinstance(self.color, ColorPalette)
+                else self.color
+            )
+
+            mask = detections.mask[i]
+            colored_mask = np.zeros_like(scene, dtype=np.uint8)
+            colored_mask[:] = color.as_bgr()
+
+            scene = np.where(
+                np.expand_dims(mask, axis=-1),
+                np.uint8(opacity * colored_mask + (1 - opacity) * scene),
+                scene,
+            )
+
+        return scene
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+annotate(scene, detections, opacity=0.5) + +

+ + +
+ +

Overlays the masks on the given image based on the provided detections, with a specified opacity.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
scene + np.ndarray + +
+

The image on which the masks will be overlaid

+
+
+ required +
detections + Detections + +
+

The detections for which the masks will be overlaid

+
+
+ required +
opacity + float + +
+

The opacity of the masks, between 0 and 1, default is 0.5

+
+
+ 0.5 +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ np.ndarray + +
+

np.ndarray: The image with the masks overlaid

+
+
+ +
+ Source code in supervision/detection/annotate.py +
157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
def annotate(
+    self, scene: np.ndarray, detections: Detections, opacity: float = 0.5
+) -> np.ndarray:
+    """
+    Overlays the masks on the given image based on the provided detections, with a specified opacity.
+
+    Args:
+        scene (np.ndarray): The image on which the masks will be overlaid
+        detections (Detections): The detections for which the masks will be overlaid
+        opacity (float): The opacity of the masks, between 0 and 1, default is 0.5
+
+    Returns:
+        np.ndarray: The image with the masks overlaid
+    """
+    if detections.mask is None:
+        return scene
+
+    for i in np.flip(np.argsort(detections.area)):
+        class_id = (
+            detections.class_id[i] if detections.class_id is not None else None
+        )
+        idx = class_id if class_id is not None else i
+        color = (
+            self.color.by_idx(idx)
+            if isinstance(self.color, ColorPalette)
+            else self.color
+        )
+
+        mask = detections.mask[i]
+        colored_mask = np.zeros_like(scene, dtype=np.uint8)
+        colored_mask[:] = color.as_bgr()
+
+        scene = np.where(
+            np.expand_dims(mask, axis=-1),
+            np.uint8(opacity * colored_mask + (1 - opacity) * scene),
+            scene,
+        )
+
+    return scene
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/detection/core/index.html b/detection/core/index.html new file mode 100644 index 000000000..ecca08c21 --- /dev/null +++ b/detection/core/index.html @@ -0,0 +1,4198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Core - Supervision + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Core

+ +

Detections

+ + +
+ + + +
+ + +

Data class containing information about the detections in a video frame.

+ +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
xyxy + np.ndarray + +
+

An array of shape (n, 4) containing the bounding boxes coordinates in format [x1, y1, x2, y2]

+
+
mask + np.Optional[np.ndarray] + +
+

(Optional[np.ndarray]): An array of shape (n, W, H) containing the segmentation masks.

+
+
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 (n,) containing the class ids of the detections.

+
+
tracker_id + Optional[np.ndarray] + +
+

An array of shape (n,) containing the tracker ids of the detections.

+
+
+ + +
+ Source code in supervision/detection/core.py +
 57
+ 58
+ 59
+ 60
+ 61
+ 62
+ 63
+ 64
+ 65
+ 66
+ 67
+ 68
+ 69
+ 70
+ 71
+ 72
+ 73
+ 74
+ 75
+ 76
+ 77
+ 78
+ 79
+ 80
+ 81
+ 82
+ 83
+ 84
+ 85
+ 86
+ 87
+ 88
+ 89
+ 90
+ 91
+ 92
+ 93
+ 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
@dataclass
+class Detections:
+    """
+    Data class containing information about the detections in a video frame.
+    Attributes:
+        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, W, H)` containing the segmentation masks.
+        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 `(n,)` containing the class ids of the detections.
+        tracker_id (Optional[np.ndarray]): An array of shape `(n,)` containing the tracker ids of the detections.
+    """
+
+    xyxy: np.ndarray
+    mask: np.Optional[np.ndarray] = None
+    confidence: Optional[np.ndarray] = None
+    class_id: Optional[np.ndarray] = None
+    tracker_id: Optional[np.ndarray] = None
+
+    def __post_init__(self):
+        n = len(self.xyxy)
+        _validate_xyxy(xyxy=self.xyxy, n=n)
+        _validate_mask(mask=self.mask, n=n)
+        _validate_class_id(class_id=self.class_id, n=n)
+        _validate_confidence(confidence=self.confidence, n=n)
+        _validate_tracker_id(tracker_id=self.tracker_id, n=n)
+
+    def __len__(self):
+        """
+        Returns the number of detections in the Detections object.
+        """
+        return len(self.xyxy)
+
+    def __iter__(
+        self,
+    ) -> Iterator[
+        Tuple[
+            np.ndarray,
+            Optional[np.ndarray],
+            Optional[float],
+            Optional[int],
+            Optional[int],
+        ]
+    ]:
+        """
+        Iterates over the Detections object and yield a tuple of `(xyxy, mask, confidence, class_id, tracker_id)` for each detection.
+        """
+        for i in range(len(self.xyxy)):
+            yield (
+                self.xyxy[i],
+                self.mask[i] if self.mask is not None else None,
+                self.confidence[i] if self.confidence is not None else None,
+                self.class_id[i] if self.class_id is not None else None,
+                self.tracker_id[i] if self.tracker_id is not None else None,
+            )
+
+    def __eq__(self, other: Detections):
+        return all(
+            [
+                np.array_equal(self.xyxy, other.xyxy),
+                any(
+                    [
+                        self.mask is None and other.mask is None,
+                        np.array_equal(self.mask, other.mask),
+                    ]
+                ),
+                any(
+                    [
+                        self.class_id is None and other.class_id is None,
+                        np.array_equal(self.class_id, other.class_id),
+                    ]
+                ),
+                any(
+                    [
+                        self.confidence is None and other.confidence is None,
+                        np.array_equal(self.confidence, other.confidence),
+                    ]
+                ),
+                any(
+                    [
+                        self.tracker_id is None and other.tracker_id is None,
+                        np.array_equal(self.tracker_id, other.tracker_id),
+                    ]
+                ),
+            ]
+        )
+
+    @classmethod
+    def from_yolov5(cls, yolov5_results) -> Detections:
+        """
+        Creates a Detections instance from a [YOLOv5](https://github.com/ultralytics/yolov5) inference result.
+
+        Args:
+            yolov5_results (yolov5.models.common.Detections): The output Detections instance from YOLOv5
+
+        Returns:
+            Detections: A new Detections object.
+
+        Example:
+            ```python
+            >>> import cv2
+            >>> import torch
+            >>> import supervision as sv
+
+            >>> image = cv2.imread(SOURCE_IMAGE_PATH)
+            >>> model = torch.hub.load('ultralytics/yolov5', 'yolov5s')
+            >>> result = model(image)
+            >>> detections = sv.Detections.from_yolov5(result)
+            ```
+        """
+        yolov5_detections_predictions = yolov5_results.pred[0].cpu().cpu().numpy()
+        return cls(
+            xyxy=yolov5_detections_predictions[:, :4],
+            confidence=yolov5_detections_predictions[:, 4],
+            class_id=yolov5_detections_predictions[:, 5].astype(int),
+        )
+
+    @classmethod
+    def from_yolov8(cls, yolov8_results) -> Detections:
+        """
+        Creates a Detections instance from a [YOLOv8](https://github.com/ultralytics/ultralytics) inference result.
+
+        Args:
+            yolov8_results (ultralytics.yolo.engine.results.Results): The output Results instance from YOLOv8
+
+        Returns:
+            Detections: A new Detections object.
+
+        Example:
+            ```python
+            >>> import cv2
+            >>> from ultralytics import YOLO
+            >>> import supervision as sv
+
+            >>> image = cv2.imread(SOURCE_IMAGE_PATH)
+            >>> model = YOLO('yolov8s.pt')
+            >>> result = model(image)[0]
+            >>> detections = sv.Detections.from_yolov8(result)
+            ```
+        """
+        return cls(
+            xyxy=yolov8_results.boxes.xyxy.cpu().numpy(),
+            confidence=yolov8_results.boxes.conf.cpu().numpy(),
+            class_id=yolov8_results.boxes.cls.cpu().numpy().astype(int),
+            mask=extract_yolov8_masks(yolov8_results),
+        )
+
+    @classmethod
+    def from_yolo_nas(cls, yolo_nas_results) -> Detections:
+        """
+        Creates a Detections instance from a [YOLO-NAS](https://github.com/Deci-AI/super-gradients/blob/master/YOLONAS.md) inference result.
+
+        Args:
+            yolo_nas_results (super_gradients.training.models.prediction_results.ImageDetectionPrediction): The output Results instance from YOLO-NAS
+
+        Returns:
+            Detections: A new Detections object.
+
+        Example:
+            ```python
+            >>> import cv2
+            >>> from super_gradients.training import models
+            >>> import supervision as sv
+
+            >>> image = cv2.imread(SOURCE_IMAGE_PATH)
+            >>> model = models.get('yolo_nas_l', pretrained_weights="coco")
+            >>> result = list(model.predict(image, conf=0.35))[0]
+            >>> detections = sv.Detections.from_yolo_nas(result)
+            ```
+        """
+        return cls(
+            xyxy=yolo_nas_results.prediction.bboxes_xyxy,
+            confidence=yolo_nas_results.prediction.confidence,
+            class_id=yolo_nas_results.prediction.labels.astype(int),
+        )
+
+    @classmethod
+    def from_transformers(cls, transformers_results: dict) -> Detections:
+        """
+        Creates a Detections instance from object detection [transformer](https://github.com/huggingface/transformers) inference result.
+
+        Returns:
+            Detections: A new Detections object.
+        """
+        return cls(
+            xyxy=transformers_results["boxes"].cpu().numpy(),
+            confidence=transformers_results["scores"].cpu().numpy(),
+            class_id=transformers_results["labels"].cpu().numpy().astype(int),
+        )
+
+    @classmethod
+    def from_detectron2(cls, detectron2_results) -> Detections:
+        """
+        Create a Detections object from the [Detectron2](https://github.com/facebookresearch/detectron2) inference result.
+
+        Args:
+            detectron2_results: The output of a Detectron2 model containing instances with prediction data.
+
+        Returns:
+            (Detections): A Detections object containing the bounding boxes, class IDs, and confidences of the predictions.
+
+        Example:
+            ```python
+            >>> import cv2
+            >>> from detectron2.engine import DefaultPredictor
+            >>> from detectron2.config import get_cfg
+            >>> import supervision as sv
+
+            >>> image = cv2.imread(SOURCE_IMAGE_PATH)
+            >>> cfg = get_cfg()
+            >>> cfg.merge_from_file("path/to/config.yaml")
+            >>> cfg.MODEL.WEIGHTS = "path/to/model_weights.pth"
+            >>> predictor = DefaultPredictor(cfg)
+            >>> result = predictor(image)
+
+            >>> detections = sv.Detections.from_detectron2(result)
+            ```
+        """
+        return cls(
+            xyxy=detectron2_results["instances"].pred_boxes.tensor.cpu().numpy(),
+            confidence=detectron2_results["instances"].scores.cpu().numpy(),
+            class_id=detectron2_results["instances"]
+            .pred_classes.cpu()
+            .numpy()
+            .astype(int),
+        )
+
+    @classmethod
+    def from_roboflow(cls, roboflow_result: dict, class_list: List[str]) -> Detections:
+        """
+        Create a Detections object from the [Roboflow](https://roboflow.com/) API inference result.
+
+        Args:
+            roboflow_result (dict): The result from the Roboflow API containing predictions.
+            class_list (List[str]): A list of class names corresponding to the class IDs in the API result.
+
+        Returns:
+            (Detections): A Detections object containing the bounding boxes, class IDs, and confidences of the predictions.
+
+        Example:
+            ```python
+            >>> import supervision as sv
+
+            >>> roboflow_result = {
+            ...     "predictions": [
+            ...         {
+            ...             "x": 0.5,
+            ...             "y": 0.5,
+            ...             "width": 0.2,
+            ...             "height": 0.3,
+            ...             "class": "person",
+            ...             "confidence": 0.9
+            ...         },
+            ...         # ... more predictions ...
+            ...     ]
+            ... }
+            >>> class_list = ["person", "car", "dog"]
+
+            >>> detections = sv.Detections.from_roboflow(roboflow_result, class_list)
+            ```
+        """
+        xyxy, confidence, class_id, masks = process_roboflow_result(
+            roboflow_result=roboflow_result, class_list=class_list
+        )
+        return Detections(
+            xyxy=xyxy,
+            confidence=confidence,
+            class_id=class_id,
+            mask=masks,
+        )
+
+    @classmethod
+    def from_sam(cls, sam_result: List[dict]) -> Detections:
+        """
+        Creates a Detections instance from [Segment Anything Model](https://github.com/facebookresearch/segment-anything) inference result.
+
+        Args:
+            sam_result (List[dict]): The output Results instance from SAM
+
+        Returns:
+            Detections: A new Detections object.
+
+        Example:
+            ```python
+            >>> import supervision as sv
+            >>> from segment_anything import sam_model_registry, SamAutomaticMaskGenerator
+
+            >>> sam = sam_model_registry[MODEL_TYPE](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)
+            ```
+        """
+        sorted_generated_masks = sorted(
+            sam_result, key=lambda x: x["area"], reverse=True
+        )
+
+        xywh = np.array([mask["bbox"] for mask in sorted_generated_masks])
+        mask = np.array([mask["segmentation"] for mask in sorted_generated_masks])
+
+        return Detections(xyxy=xywh_to_xyxy(boxes_xywh=xywh), mask=mask)
+
+    @classmethod
+    def empty(cls) -> Detections:
+        """
+        Create an empty Detections object with no bounding boxes, confidences, or class IDs.
+
+        Returns:
+            (Detections): An empty Detections object.
+
+        Example:
+            ```python
+            >>> from supervision import Detections
+
+            >>> empty_detections = Detections.empty()
+            ```
+        """
+        return cls(
+            xyxy=np.empty((0, 4), dtype=np.float32),
+            confidence=np.array([], dtype=np.float32),
+            class_id=np.array([], dtype=int),
+        )
+
+    @classmethod
+    def merge(cls, detections_list: List[Detections]) -> Detections:
+        """
+        Merge a list of Detections objects into a single Detections object.
+
+        This method takes a list of Detections objects and combines their respective fields (`xyxy`, `mask`,
+        `confidence`, `class_id`, and `tracker_id`) into a single Detections object. If all elements in a field are not
+        `None`, the corresponding field will be stacked. Otherwise, the field will be set to `None`.
+
+        Args:
+            detections_list (List[Detections]): A list of Detections objects to merge.
+
+        Returns:
+            (Detections): A single Detections object containing the merged data from the input list.
+
+        Example:
+            ```python
+            >>> from supervision import Detections
+
+            >>> detections_1 = Detections(...)
+            >>> detections_2 = Detections(...)
+
+            >>> merged_detections = Detections.merge([detections_1, detections_2])
+            ```
+        """
+        if len(detections_list) == 0:
+            return Detections.empty()
+
+        detections_tuples_list = [astuple(detection) for detection in detections_list]
+        xyxy, mask, confidence, class_id, tracker_id = [
+            list(field) for field in zip(*detections_tuples_list)
+        ]
+
+        all_not_none = lambda l: all(x is not None for x in l)
+
+        xyxy = np.vstack(xyxy)
+        mask = np.vstack(mask) if all_not_none(mask) else None
+        confidence = np.hstack(confidence) if all_not_none(confidence) else None
+        class_id = np.hstack(class_id) if all_not_none(class_id) else None
+        tracker_id = np.hstack(tracker_id) if all_not_none(tracker_id) else None
+
+        return cls(
+            xyxy=xyxy,
+            mask=mask,
+            confidence=confidence,
+            class_id=class_id,
+            tracker_id=tracker_id,
+        )
+
+    def get_anchor_coordinates(self, anchor: Position) -> np.ndarray:
+        """
+        Returns the bounding box coordinates for a specific anchor.
+
+        Args:
+            anchor (Position): Position of bounding box anchor for which to return the coordinates.
+
+        Returns:
+            np.ndarray: An array of shape `(n, 2)` containing the bounding box anchor coordinates in format `[x, y]`.
+        """
+        if anchor == Position.CENTER:
+            return np.array(
+                [
+                    (self.xyxy[:, 0] + self.xyxy[:, 2]) / 2,
+                    (self.xyxy[:, 1] + self.xyxy[:, 3]) / 2,
+                ]
+            ).transpose()
+        elif anchor == Position.BOTTOM_CENTER:
+            return np.array(
+                [(self.xyxy[:, 0] + self.xyxy[:, 2]) / 2, self.xyxy[:, 3]]
+            ).transpose()
+
+        raise ValueError(f"{anchor} is not supported.")
+
+    def __getitem__(
+        self, index: Union[int, slice, List[int], np.ndarray]
+    ) -> Detections:
+        """
+        Get a subset of the Detections object.
+
+        Args:
+            index (Union[int, slice, List[int], np.ndarray]): The index or indices of the subset of the Detections
+
+        Returns:
+            (Detections): A subset of the Detections object.
+
+        Example:
+            ```python
+            >>> import supervision as sv
+
+            >>> detections = sv.Detections(...)
+
+            >>> first_detection = detections[0]
+
+            >>> first_10_detections = detections[0:10]
+
+            >>> some_detections = detections[[0, 2, 4]]
+
+            >>> class_0_detections = detections[detections.class_id == 0]
+
+            >>> high_confidence_detections = detections[detections.confidence > 0.5]
+            ```
+        """
+        if isinstance(index, int):
+            index = [index]
+        return Detections(
+            xyxy=self.xyxy[index],
+            mask=self.mask[index] if self.mask is not None else None,
+            confidence=self.confidence[index] if self.confidence is not None else None,
+            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,
+        )
+
+    @property
+    def area(self) -> np.ndarray:
+        """
+        Calculate the area of each detection in the set of object detections. If masks field is defined property
+        returns are of each mask. If only box is given property return area of each box.
+
+        Returns:
+          np.ndarray: An array of floats containing the area of each detection in the format of `(area_1, area_2, ..., area_n)`, where n is the number of detections.
+        """
+        if self.mask is not None:
+            return np.array([np.sum(mask) for mask in self.mask])
+        else:
+            return self.box_area
+
+    @property
+    def box_area(self) -> np.ndarray:
+        """
+        Calculate the area of each bounding box in the set of object detections.
+
+        Returns:
+            np.ndarray: An array of floats containing the area of each bounding box in the format of `(area_1, area_2, ..., area_n)`, where n is the number of detections.
+        """
+        return (self.xyxy[:, 3] - self.xyxy[:, 1]) * (self.xyxy[:, 2] - self.xyxy[:, 0])
+
+    def with_nms(
+        self, threshold: float = 0.5, class_agnostic: bool = False
+    ) -> Detections:
+        """
+        Perform non-maximum suppression on the current set of object detections.
+
+        Args:
+            threshold (float, optional): The intersection-over-union threshold to use for non-maximum suppression. Defaults to 0.5.
+            class_agnostic (bool, optional): Whether to perform class-agnostic non-maximum suppression. If True, the class_id of each detection will be ignored. Defaults to False.
+
+        Returns:
+            Detections: A new Detections object containing the subset of detections after non-maximum suppression.
+
+        Raises:
+            AssertionError: If `confidence` is None and class_agnostic is False. If `class_id` is None and class_agnostic is False.
+        """
+        if len(self) == 0:
+            return self
+
+        assert (
+            self.confidence is not None
+        ), f"Detections confidence must be given for NMS to be executed."
+
+        if class_agnostic:
+            predictions = np.hstack((self.xyxy, self.confidence.reshape(-1, 1)))
+            indices = non_max_suppression(
+                predictions=predictions, iou_threshold=threshold
+            )
+            return self[indices]
+
+        assert self.class_id is not None, (
+            f"Detections class_id must be given for NMS to be executed. If you intended to perform class agnostic "
+            f"NMS set class_agnostic=True."
+        )
+
+        predictions = np.hstack(
+            (self.xyxy, self.confidence.reshape(-1, 1), self.class_id.reshape(-1, 1))
+        )
+        indices = non_max_suppression(predictions=predictions, iou_threshold=threshold)
+        return self[indices]
+
+
+ + + +
+ + + + + + + +
+ + + +

+area: np.ndarray + + + property + + +

+ + +
+ +

Calculate the area of each detection in the set of object detections. If masks field is defined property +returns are of each mask. If only box is given property return area of each box.

+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ np.ndarray + +
+

np.ndarray: An array of floats containing the area of each detection in the format of (area_1, area_2, ..., area_n), where n is the number of detections.

+
+
+
+ +
+ +
+ + + +

+box_area: np.ndarray + + + property + + +

+ + +
+ +

Calculate the area of each bounding box in the set of object detections.

+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ np.ndarray + +
+

np.ndarray: An array of floats containing the area of each bounding box in the format of (area_1, area_2, ..., area_n), where n is the number of detections.

+
+
+
+ +
+ + + +
+ + + +

+__getitem__(index) + +

+ + +
+ +

Get a subset of the Detections object.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
index + Union[int, slice, List[int], np.ndarray] + +
+

The index or indices of the subset of the Detections

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Detections + +
+

A subset of the Detections object.

+
+
+ +
+ Example +
>>> import supervision as sv
+
+>>> detections = sv.Detections(...)
+
+>>> first_detection = detections[0]
+
+>>> first_10_detections = detections[0:10]
+
+>>> some_detections = detections[[0, 2, 4]]
+
+>>> class_0_detections = detections[detections.class_id == 0]
+
+>>> high_confidence_detections = detections[detections.confidence > 0.5]
+
+
+
+ Source code in supervision/detection/core.py +
452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
def __getitem__(
+    self, index: Union[int, slice, List[int], np.ndarray]
+) -> Detections:
+    """
+    Get a subset of the Detections object.
+
+    Args:
+        index (Union[int, slice, List[int], np.ndarray]): The index or indices of the subset of the Detections
+
+    Returns:
+        (Detections): A subset of the Detections object.
+
+    Example:
+        ```python
+        >>> import supervision as sv
+
+        >>> detections = sv.Detections(...)
+
+        >>> first_detection = detections[0]
+
+        >>> first_10_detections = detections[0:10]
+
+        >>> some_detections = detections[[0, 2, 4]]
+
+        >>> class_0_detections = detections[detections.class_id == 0]
+
+        >>> high_confidence_detections = detections[detections.confidence > 0.5]
+        ```
+    """
+    if isinstance(index, int):
+        index = [index]
+    return Detections(
+        xyxy=self.xyxy[index],
+        mask=self.mask[index] if self.mask is not None else None,
+        confidence=self.confidence[index] if self.confidence is not None else None,
+        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,
+    )
+
+
+
+ +
+ +
+ + + +

+__iter__() + +

+ + +
+ +

Iterates over the Detections object and yield a tuple of (xyxy, mask, confidence, class_id, tracker_id) for each detection.

+ +
+ Source code in supervision/detection/core.py +
 89
+ 90
+ 91
+ 92
+ 93
+ 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
def __iter__(
+    self,
+) -> Iterator[
+    Tuple[
+        np.ndarray,
+        Optional[np.ndarray],
+        Optional[float],
+        Optional[int],
+        Optional[int],
+    ]
+]:
+    """
+    Iterates over the Detections object and yield a tuple of `(xyxy, mask, confidence, class_id, tracker_id)` for each detection.
+    """
+    for i in range(len(self.xyxy)):
+        yield (
+            self.xyxy[i],
+            self.mask[i] if self.mask is not None else None,
+            self.confidence[i] if self.confidence is not None else None,
+            self.class_id[i] if self.class_id is not None else None,
+            self.tracker_id[i] if self.tracker_id is not None else None,
+        )
+
+
+
+ +
+ +
+ + + +

+__len__() + +

+ + +
+ +

Returns the number of detections in the Detections object.

+ +
+ Source code in supervision/detection/core.py +
83
+84
+85
+86
+87
def __len__(self):
+    """
+    Returns the number of detections in the Detections object.
+    """
+    return len(self.xyxy)
+
+
+
+ +
+ +
+ + + +

+empty() + + + classmethod + + +

+ + +
+ +

Create an empty Detections object with no bounding boxes, confidences, or class IDs.

+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Detections + +
+

An empty Detections object.

+
+
+ +
+ Example +
>>> from supervision import Detections
+
+>>> empty_detections = Detections.empty()
+
+
+
+ Source code in supervision/detection/core.py +
358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
@classmethod
+def empty(cls) -> Detections:
+    """
+    Create an empty Detections object with no bounding boxes, confidences, or class IDs.
+
+    Returns:
+        (Detections): An empty Detections object.
+
+    Example:
+        ```python
+        >>> from supervision import Detections
+
+        >>> empty_detections = Detections.empty()
+        ```
+    """
+    return cls(
+        xyxy=np.empty((0, 4), dtype=np.float32),
+        confidence=np.array([], dtype=np.float32),
+        class_id=np.array([], dtype=int),
+    )
+
+
+
+ +
+ +
+ + + +

+from_detectron2(detectron2_results) + + + classmethod + + +

+ + +
+ +

Create a Detections object from the Detectron2 inference result.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
detectron2_results + +
+

The output of a Detectron2 model containing instances with prediction data.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Detections + +
+

A Detections object containing the bounding boxes, class IDs, and confidences of the predictions.

+
+
+ +
+ Example +
>>> import cv2
+>>> from detectron2.engine import DefaultPredictor
+>>> from detectron2.config import get_cfg
+>>> import supervision as sv
+
+>>> image = cv2.imread(SOURCE_IMAGE_PATH)
+>>> cfg = get_cfg()
+>>> cfg.merge_from_file("path/to/config.yaml")
+>>> cfg.MODEL.WEIGHTS = "path/to/model_weights.pth"
+>>> predictor = DefaultPredictor(cfg)
+>>> result = predictor(image)
+
+>>> detections = sv.Detections.from_detectron2(result)
+
+
+
+ Source code in supervision/detection/core.py +
246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
@classmethod
+def from_detectron2(cls, detectron2_results) -> Detections:
+    """
+    Create a Detections object from the [Detectron2](https://github.com/facebookresearch/detectron2) inference result.
+
+    Args:
+        detectron2_results: The output of a Detectron2 model containing instances with prediction data.
+
+    Returns:
+        (Detections): A Detections object containing the bounding boxes, class IDs, and confidences of the predictions.
+
+    Example:
+        ```python
+        >>> import cv2
+        >>> from detectron2.engine import DefaultPredictor
+        >>> from detectron2.config import get_cfg
+        >>> import supervision as sv
+
+        >>> image = cv2.imread(SOURCE_IMAGE_PATH)
+        >>> cfg = get_cfg()
+        >>> cfg.merge_from_file("path/to/config.yaml")
+        >>> cfg.MODEL.WEIGHTS = "path/to/model_weights.pth"
+        >>> predictor = DefaultPredictor(cfg)
+        >>> result = predictor(image)
+
+        >>> detections = sv.Detections.from_detectron2(result)
+        ```
+    """
+    return cls(
+        xyxy=detectron2_results["instances"].pred_boxes.tensor.cpu().numpy(),
+        confidence=detectron2_results["instances"].scores.cpu().numpy(),
+        class_id=detectron2_results["instances"]
+        .pred_classes.cpu()
+        .numpy()
+        .astype(int),
+    )
+
+
+
+ +
+ +
+ + + +

+from_roboflow(roboflow_result, class_list) + + + classmethod + + +

+ + +
+ +

Create a Detections object from the Roboflow API inference result.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
roboflow_result + dict + +
+

The result from the Roboflow API containing predictions.

+
+
+ required +
class_list + List[str] + +
+

A list of class names corresponding to the class IDs in the API result.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Detections + +
+

A Detections object containing the bounding boxes, class IDs, and confidences of the predictions.

+
+
+ +
+ Example +
>>> import supervision as sv
+
+>>> roboflow_result = {
+...     "predictions": [
+...         {
+...             "x": 0.5,
+...             "y": 0.5,
+...             "width": 0.2,
+...             "height": 0.3,
+...             "class": "person",
+...             "confidence": 0.9
+...         },
+...         # ... more predictions ...
+...     ]
+... }
+>>> class_list = ["person", "car", "dog"]
+
+>>> detections = sv.Detections.from_roboflow(roboflow_result, class_list)
+
+
+
+ Source code in supervision/detection/core.py +
283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
@classmethod
+def from_roboflow(cls, roboflow_result: dict, class_list: List[str]) -> Detections:
+    """
+    Create a Detections object from the [Roboflow](https://roboflow.com/) API inference result.
+
+    Args:
+        roboflow_result (dict): The result from the Roboflow API containing predictions.
+        class_list (List[str]): A list of class names corresponding to the class IDs in the API result.
+
+    Returns:
+        (Detections): A Detections object containing the bounding boxes, class IDs, and confidences of the predictions.
+
+    Example:
+        ```python
+        >>> import supervision as sv
+
+        >>> roboflow_result = {
+        ...     "predictions": [
+        ...         {
+        ...             "x": 0.5,
+        ...             "y": 0.5,
+        ...             "width": 0.2,
+        ...             "height": 0.3,
+        ...             "class": "person",
+        ...             "confidence": 0.9
+        ...         },
+        ...         # ... more predictions ...
+        ...     ]
+        ... }
+        >>> class_list = ["person", "car", "dog"]
+
+        >>> detections = sv.Detections.from_roboflow(roboflow_result, class_list)
+        ```
+    """
+    xyxy, confidence, class_id, masks = process_roboflow_result(
+        roboflow_result=roboflow_result, class_list=class_list
+    )
+    return Detections(
+        xyxy=xyxy,
+        confidence=confidence,
+        class_id=class_id,
+        mask=masks,
+    )
+
+
+
+ +
+ +
+ + + +

+from_sam(sam_result) + + + classmethod + + +

+ + +
+ +

Creates a Detections instance from Segment Anything Model inference result.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
sam_result + List[dict] + +
+

The output Results instance from SAM

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Detections + Detections + +
+

A new Detections object.

+
+
+ +
+ Example +
>>> import supervision as sv
+>>> from segment_anything import sam_model_registry, SamAutomaticMaskGenerator
+
+>>> sam = sam_model_registry[MODEL_TYPE](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)
+
+
+
+ Source code in supervision/detection/core.py +
327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
@classmethod
+def from_sam(cls, sam_result: List[dict]) -> Detections:
+    """
+    Creates a Detections instance from [Segment Anything Model](https://github.com/facebookresearch/segment-anything) inference result.
+
+    Args:
+        sam_result (List[dict]): The output Results instance from SAM
+
+    Returns:
+        Detections: A new Detections object.
+
+    Example:
+        ```python
+        >>> import supervision as sv
+        >>> from segment_anything import sam_model_registry, SamAutomaticMaskGenerator
+
+        >>> sam = sam_model_registry[MODEL_TYPE](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)
+        ```
+    """
+    sorted_generated_masks = sorted(
+        sam_result, key=lambda x: x["area"], reverse=True
+    )
+
+    xywh = np.array([mask["bbox"] for mask in sorted_generated_masks])
+    mask = np.array([mask["segmentation"] for mask in sorted_generated_masks])
+
+    return Detections(xyxy=xywh_to_xyxy(boxes_xywh=xywh), mask=mask)
+
+
+
+ +
+ +
+ + + +

+from_transformers(transformers_results) + + + classmethod + + +

+ + +
+ +

Creates a Detections instance from object detection transformer inference result.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Detections + Detections + +
+

A new Detections object.

+
+
+ +
+ Source code in supervision/detection/core.py +
232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
@classmethod
+def from_transformers(cls, transformers_results: dict) -> Detections:
+    """
+    Creates a Detections instance from object detection [transformer](https://github.com/huggingface/transformers) inference result.
+
+    Returns:
+        Detections: A new Detections object.
+    """
+    return cls(
+        xyxy=transformers_results["boxes"].cpu().numpy(),
+        confidence=transformers_results["scores"].cpu().numpy(),
+        class_id=transformers_results["labels"].cpu().numpy().astype(int),
+    )
+
+
+
+ +
+ +
+ + + +

+from_yolo_nas(yolo_nas_results) + + + classmethod + + +

+ + +
+ +

Creates a Detections instance from a YOLO-NAS inference result.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
yolo_nas_results + super_gradients.training.models.prediction_results.ImageDetectionPrediction + +
+

The output Results instance from YOLO-NAS

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Detections + Detections + +
+

A new Detections object.

+
+
+ +
+ Example +
>>> import cv2
+>>> from super_gradients.training import models
+>>> import supervision as sv
+
+>>> image = cv2.imread(SOURCE_IMAGE_PATH)
+>>> model = models.get('yolo_nas_l', pretrained_weights="coco")
+>>> result = list(model.predict(image, conf=0.35))[0]
+>>> detections = sv.Detections.from_yolo_nas(result)
+
+
+
+ Source code in supervision/detection/core.py +
203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
@classmethod
+def from_yolo_nas(cls, yolo_nas_results) -> Detections:
+    """
+    Creates a Detections instance from a [YOLO-NAS](https://github.com/Deci-AI/super-gradients/blob/master/YOLONAS.md) inference result.
+
+    Args:
+        yolo_nas_results (super_gradients.training.models.prediction_results.ImageDetectionPrediction): The output Results instance from YOLO-NAS
+
+    Returns:
+        Detections: A new Detections object.
+
+    Example:
+        ```python
+        >>> import cv2
+        >>> from super_gradients.training import models
+        >>> import supervision as sv
+
+        >>> image = cv2.imread(SOURCE_IMAGE_PATH)
+        >>> model = models.get('yolo_nas_l', pretrained_weights="coco")
+        >>> result = list(model.predict(image, conf=0.35))[0]
+        >>> detections = sv.Detections.from_yolo_nas(result)
+        ```
+    """
+    return cls(
+        xyxy=yolo_nas_results.prediction.bboxes_xyxy,
+        confidence=yolo_nas_results.prediction.confidence,
+        class_id=yolo_nas_results.prediction.labels.astype(int),
+    )
+
+
+
+ +
+ +
+ + + +

+from_yolov5(yolov5_results) + + + classmethod + + +

+ + +
+ +

Creates a Detections instance from a YOLOv5 inference result.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
yolov5_results + yolov5.models.common.Detections + +
+

The output Detections instance from YOLOv5

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Detections + Detections + +
+

A new Detections object.

+
+
+ +
+ Example +
>>> import cv2
+>>> import torch
+>>> import supervision as sv
+
+>>> image = cv2.imread(SOURCE_IMAGE_PATH)
+>>> model = torch.hub.load('ultralytics/yolov5', 'yolov5s')
+>>> result = model(image)
+>>> detections = sv.Detections.from_yolov5(result)
+
+
+
+ Source code in supervision/detection/core.py +
143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
@classmethod
+def from_yolov5(cls, yolov5_results) -> Detections:
+    """
+    Creates a Detections instance from a [YOLOv5](https://github.com/ultralytics/yolov5) inference result.
+
+    Args:
+        yolov5_results (yolov5.models.common.Detections): The output Detections instance from YOLOv5
+
+    Returns:
+        Detections: A new Detections object.
+
+    Example:
+        ```python
+        >>> import cv2
+        >>> import torch
+        >>> import supervision as sv
+
+        >>> image = cv2.imread(SOURCE_IMAGE_PATH)
+        >>> model = torch.hub.load('ultralytics/yolov5', 'yolov5s')
+        >>> result = model(image)
+        >>> detections = sv.Detections.from_yolov5(result)
+        ```
+    """
+    yolov5_detections_predictions = yolov5_results.pred[0].cpu().cpu().numpy()
+    return cls(
+        xyxy=yolov5_detections_predictions[:, :4],
+        confidence=yolov5_detections_predictions[:, 4],
+        class_id=yolov5_detections_predictions[:, 5].astype(int),
+    )
+
+
+
+ +
+ +
+ + + +

+from_yolov8(yolov8_results) + + + classmethod + + +

+ + +
+ +

Creates a Detections instance from a YOLOv8 inference result.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
yolov8_results + ultralytics.yolo.engine.results.Results + +
+

The output Results instance from YOLOv8

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Detections + Detections + +
+

A new Detections object.

+
+
+ +
+ Example +
>>> import cv2
+>>> from ultralytics import YOLO
+>>> import supervision as sv
+
+>>> image = cv2.imread(SOURCE_IMAGE_PATH)
+>>> model = YOLO('yolov8s.pt')
+>>> result = model(image)[0]
+>>> detections = sv.Detections.from_yolov8(result)
+
+
+
+ Source code in supervision/detection/core.py +
173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
@classmethod
+def from_yolov8(cls, yolov8_results) -> Detections:
+    """
+    Creates a Detections instance from a [YOLOv8](https://github.com/ultralytics/ultralytics) inference result.
+
+    Args:
+        yolov8_results (ultralytics.yolo.engine.results.Results): The output Results instance from YOLOv8
+
+    Returns:
+        Detections: A new Detections object.
+
+    Example:
+        ```python
+        >>> import cv2
+        >>> from ultralytics import YOLO
+        >>> import supervision as sv
+
+        >>> image = cv2.imread(SOURCE_IMAGE_PATH)
+        >>> model = YOLO('yolov8s.pt')
+        >>> result = model(image)[0]
+        >>> detections = sv.Detections.from_yolov8(result)
+        ```
+    """
+    return cls(
+        xyxy=yolov8_results.boxes.xyxy.cpu().numpy(),
+        confidence=yolov8_results.boxes.conf.cpu().numpy(),
+        class_id=yolov8_results.boxes.cls.cpu().numpy().astype(int),
+        mask=extract_yolov8_masks(yolov8_results),
+    )
+
+
+
+ +
+ +
+ + + +

+get_anchor_coordinates(anchor) + +

+ + +
+ +

Returns the bounding box coordinates for a specific anchor.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
anchor + Position + +
+

Position of bounding box anchor for which to return the coordinates.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ np.ndarray + +
+

np.ndarray: An array of shape (n, 2) containing the bounding box anchor coordinates in format [x, y].

+
+
+ +
+ Source code in supervision/detection/core.py +
428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
def get_anchor_coordinates(self, anchor: Position) -> np.ndarray:
+    """
+    Returns the bounding box coordinates for a specific anchor.
+
+    Args:
+        anchor (Position): Position of bounding box anchor for which to return the coordinates.
+
+    Returns:
+        np.ndarray: An array of shape `(n, 2)` containing the bounding box anchor coordinates in format `[x, y]`.
+    """
+    if anchor == Position.CENTER:
+        return np.array(
+            [
+                (self.xyxy[:, 0] + self.xyxy[:, 2]) / 2,
+                (self.xyxy[:, 1] + self.xyxy[:, 3]) / 2,
+            ]
+        ).transpose()
+    elif anchor == Position.BOTTOM_CENTER:
+        return np.array(
+            [(self.xyxy[:, 0] + self.xyxy[:, 2]) / 2, self.xyxy[:, 3]]
+        ).transpose()
+
+    raise ValueError(f"{anchor} is not supported.")
+
+
+
+ +
+ +
+ + + +

+merge(detections_list) + + + classmethod + + +

+ + +
+ +

Merge a list of Detections objects into a single Detections object.

+

This method takes a list of Detections objects and combines their respective fields (xyxy, mask, +confidence, class_id, and tracker_id) into a single Detections object. If all elements in a field are not +None, the corresponding field will be stacked. Otherwise, the field will be set to None.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
detections_list + List[Detections] + +
+

A list of Detections objects to merge.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Detections + +
+

A single Detections object containing the merged data from the input list.

+
+
+ +
+ Example +
>>> from supervision import Detections
+
+>>> detections_1 = Detections(...)
+>>> detections_2 = Detections(...)
+
+>>> merged_detections = Detections.merge([detections_1, detections_2])
+
+
+
+ Source code in supervision/detection/core.py +
379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
@classmethod
+def merge(cls, detections_list: List[Detections]) -> Detections:
+    """
+    Merge a list of Detections objects into a single Detections object.
+
+    This method takes a list of Detections objects and combines their respective fields (`xyxy`, `mask`,
+    `confidence`, `class_id`, and `tracker_id`) into a single Detections object. If all elements in a field are not
+    `None`, the corresponding field will be stacked. Otherwise, the field will be set to `None`.
+
+    Args:
+        detections_list (List[Detections]): A list of Detections objects to merge.
+
+    Returns:
+        (Detections): A single Detections object containing the merged data from the input list.
+
+    Example:
+        ```python
+        >>> from supervision import Detections
+
+        >>> detections_1 = Detections(...)
+        >>> detections_2 = Detections(...)
+
+        >>> merged_detections = Detections.merge([detections_1, detections_2])
+        ```
+    """
+    if len(detections_list) == 0:
+        return Detections.empty()
+
+    detections_tuples_list = [astuple(detection) for detection in detections_list]
+    xyxy, mask, confidence, class_id, tracker_id = [
+        list(field) for field in zip(*detections_tuples_list)
+    ]
+
+    all_not_none = lambda l: all(x is not None for x in l)
+
+    xyxy = np.vstack(xyxy)
+    mask = np.vstack(mask) if all_not_none(mask) else None
+    confidence = np.hstack(confidence) if all_not_none(confidence) else None
+    class_id = np.hstack(class_id) if all_not_none(class_id) else None
+    tracker_id = np.hstack(tracker_id) if all_not_none(tracker_id) else None
+
+    return cls(
+        xyxy=xyxy,
+        mask=mask,
+        confidence=confidence,
+        class_id=class_id,
+        tracker_id=tracker_id,
+    )
+
+
+
+ +
+ +
+ + + +

+with_nms(threshold=0.5, class_agnostic=False) + +

+ + +
+ +

Perform non-maximum suppression on the current set of object detections.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
threshold + float + +
+

The intersection-over-union threshold to use for non-maximum suppression. Defaults to 0.5.

+
+
+ 0.5 +
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.

+
+
+ False +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Detections + Detections + +
+

A new Detections object containing the subset of detections after non-maximum suppression.

+
+
+ +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ AssertionError + +
+

If confidence is None and class_agnostic is False. If class_id is None and class_agnostic is False.

+
+
+ +
+ Source code in supervision/detection/core.py +
515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
def with_nms(
+    self, threshold: float = 0.5, class_agnostic: bool = False
+) -> Detections:
+    """
+    Perform non-maximum suppression on the current set of object detections.
+
+    Args:
+        threshold (float, optional): The intersection-over-union threshold to use for non-maximum suppression. Defaults to 0.5.
+        class_agnostic (bool, optional): Whether to perform class-agnostic non-maximum suppression. If True, the class_id of each detection will be ignored. Defaults to False.
+
+    Returns:
+        Detections: A new Detections object containing the subset of detections after non-maximum suppression.
+
+    Raises:
+        AssertionError: If `confidence` is None and class_agnostic is False. If `class_id` is None and class_agnostic is False.
+    """
+    if len(self) == 0:
+        return self
+
+    assert (
+        self.confidence is not None
+    ), f"Detections confidence must be given for NMS to be executed."
+
+    if class_agnostic:
+        predictions = np.hstack((self.xyxy, self.confidence.reshape(-1, 1)))
+        indices = non_max_suppression(
+            predictions=predictions, iou_threshold=threshold
+        )
+        return self[indices]
+
+    assert self.class_id is not None, (
+        f"Detections class_id must be given for NMS to be executed. If you intended to perform class agnostic "
+        f"NMS set class_agnostic=True."
+    )
+
+    predictions = np.hstack(
+        (self.xyxy, self.confidence.reshape(-1, 1), self.class_id.reshape(-1, 1))
+    )
+    indices = non_max_suppression(predictions=predictions, iou_threshold=threshold)
+    return self[indices]
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/detection/tools/polygon_zone/index.html b/detection/tools/polygon_zone/index.html new file mode 100644 index 000000000..50690085c --- /dev/null +++ b/detection/tools/polygon_zone/index.html @@ -0,0 +1,1734 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Polygon Zone - Supervision + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Polygon Zone

+ +

PolygonZone

+ + +
+ + + +
+ + +

A class for defining a polygon-shaped zone within a frame for detecting objects.

+ +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
polygon + np.ndarray + +
+

A polygon represented by a numpy array of shape (N, 2), containing the x, y coordinates of the points.

+
+
frame_resolution_wh + Tuple[int, int] + +
+

The frame resolution (width, height)

+
+
triggering_position + Position + +
+

The position within the bounding box that triggers the zone (default: Position.BOTTOM_CENTER)

+
+
current_count + int + +
+

The current count of detected objects within the zone

+
+
mask + np.ndarray + +
+

The 2D bool mask for the polygon zone

+
+
+ + +
+ Source code in supervision/detection/tools/polygon_zone.py +
15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
class PolygonZone:
+    """
+    A class for defining a polygon-shaped zone within a frame for detecting objects.
+
+    Attributes:
+        polygon (np.ndarray): A polygon represented by a numpy array of shape `(N, 2)`, containing the `x`, `y` coordinates of the points.
+        frame_resolution_wh (Tuple[int, int]): The frame resolution (width, height)
+        triggering_position (Position): The position within the bounding box that triggers the zone (default: Position.BOTTOM_CENTER)
+        current_count (int): The current count of detected objects within the zone
+        mask (np.ndarray): The 2D bool mask for the polygon zone
+    """
+
+    def __init__(
+        self,
+        polygon: np.ndarray,
+        frame_resolution_wh: Tuple[int, int],
+        triggering_position: Position = Position.BOTTOM_CENTER,
+    ):
+        self.polygon = polygon.astype(int)
+        self.frame_resolution_wh = frame_resolution_wh
+        self.triggering_position = triggering_position
+        self.current_count = 0
+
+        width, height = frame_resolution_wh
+        self.mask = polygon_to_mask(
+            polygon=polygon, resolution_wh=(width + 1, height + 1)
+        )
+
+    def trigger(self, detections: Detections) -> np.ndarray:
+        """
+        Determines if the detections are within the polygon zone.
+
+        Parameters:
+            detections (Detections): The detections to be checked against the polygon zone
+
+        Returns:
+            np.ndarray: A boolean numpy array indicating if each detection is within the polygon zone
+        """
+
+        clipped_xyxy = clip_boxes(
+            boxes_xyxy=detections.xyxy, frame_resolution_wh=self.frame_resolution_wh
+        )
+        clipped_detections = replace(detections, xyxy=clipped_xyxy)
+        clipped_anchors = np.ceil(
+            clipped_detections.get_anchor_coordinates(anchor=self.triggering_position)
+        ).astype(int)
+        is_in_zone = self.mask[clipped_anchors[:, 1], clipped_anchors[:, 0]]
+        self.current_count = np.sum(is_in_zone)
+        return is_in_zone.astype(bool)
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+trigger(detections) + +

+ + +
+ +

Determines if the detections are within the polygon zone.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
detections + Detections + +
+

The detections to be checked against the polygon zone

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ np.ndarray + +
+

np.ndarray: A boolean numpy array indicating if each detection is within the polygon zone

+
+
+ +
+ Source code in supervision/detection/tools/polygon_zone.py +
43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
def trigger(self, detections: Detections) -> np.ndarray:
+    """
+    Determines if the detections are within the polygon zone.
+
+    Parameters:
+        detections (Detections): The detections to be checked against the polygon zone
+
+    Returns:
+        np.ndarray: A boolean numpy array indicating if each detection is within the polygon zone
+    """
+
+    clipped_xyxy = clip_boxes(
+        boxes_xyxy=detections.xyxy, frame_resolution_wh=self.frame_resolution_wh
+    )
+    clipped_detections = replace(detections, xyxy=clipped_xyxy)
+    clipped_anchors = np.ceil(
+        clipped_detections.get_anchor_coordinates(anchor=self.triggering_position)
+    ).astype(int)
+    is_in_zone = self.mask[clipped_anchors[:, 1], clipped_anchors[:, 0]]
+    self.current_count = np.sum(is_in_zone)
+    return is_in_zone.astype(bool)
+
+
+
+ +
+ + + +
+ +
+ +

PolygonZoneAnnotator

+ + +
+ + + +
+ + +

A class for annotating a polygon-shaped zone within a frame with a count of detected objects.

+ +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
zone + PolygonZone + +
+

The polygon zone to be annotated

+
+
color + Color + +
+

The color to draw the polygon lines

+
+
thickness + int + +
+

The thickness of the polygon lines, default is 2

+
+
text_color + Color + +
+

The color of the text on the polygon, default is black

+
+
text_scale + float + +
+

The scale of the text on the polygon, default is 0.5

+
+
text_thickness + int + +
+

The thickness of the text on the polygon, default is 1

+
+
text_padding + int + +
+

The padding around the text on the polygon, default is 10

+
+
font + int + +
+

The font type for the text on the polygon, default is cv2.FONT_HERSHEY_SIMPLEX

+
+
center + Tuple[int, int] + +
+

The center of the polygon for text placement

+
+
+ + +
+ Source code in supervision/detection/tools/polygon_zone.py +
 66
+ 67
+ 68
+ 69
+ 70
+ 71
+ 72
+ 73
+ 74
+ 75
+ 76
+ 77
+ 78
+ 79
+ 80
+ 81
+ 82
+ 83
+ 84
+ 85
+ 86
+ 87
+ 88
+ 89
+ 90
+ 91
+ 92
+ 93
+ 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
class PolygonZoneAnnotator:
+    """
+    A class for annotating a polygon-shaped zone within a frame with a count of detected objects.
+
+    Attributes:
+        zone (PolygonZone): The polygon zone to be annotated
+        color (Color): The color to draw the polygon lines
+        thickness (int): The thickness of the polygon lines, default is 2
+        text_color (Color): The color of the text on the polygon, default is black
+        text_scale (float): The scale of the text on the polygon, default is 0.5
+        text_thickness (int): The thickness of the text on the polygon, default is 1
+        text_padding (int): The padding around the text on the polygon, default is 10
+        font (int): The font type for the text on the polygon, default is cv2.FONT_HERSHEY_SIMPLEX
+        center (Tuple[int, int]): The center of the polygon for text placement
+    """
+
+    def __init__(
+        self,
+        zone: PolygonZone,
+        color: Color,
+        thickness: int = 2,
+        text_color: Color = Color.black(),
+        text_scale: float = 0.5,
+        text_thickness: int = 1,
+        text_padding: int = 10,
+    ):
+        self.zone = zone
+        self.color = color
+        self.thickness = thickness
+        self.text_color = text_color
+        self.text_scale = text_scale
+        self.text_thickness = text_thickness
+        self.text_padding = text_padding
+        self.font = cv2.FONT_HERSHEY_SIMPLEX
+        self.center = get_polygon_center(polygon=zone.polygon)
+
+    def annotate(self, scene: np.ndarray, label: Optional[str] = None) -> np.ndarray:
+        """
+        Annotates the polygon zone within a frame with a count of detected objects.
+
+        Parameters:
+            scene (np.ndarray): The image on which the polygon zone will be annotated
+            label (Optional[str]): An optional label for the count of detected objects within the polygon zone (default: None)
+
+        Returns:
+            np.ndarray: The image with the polygon zone and count of detected objects
+        """
+        annotated_frame = draw_polygon(
+            scene=scene,
+            polygon=self.zone.polygon,
+            color=self.color,
+            thickness=self.thickness,
+        )
+
+        annotated_frame = draw_text(
+            scene=annotated_frame,
+            text=str(self.zone.current_count) if label is None else label,
+            text_anchor=self.center,
+            background_color=self.color,
+            text_color=self.text_color,
+            text_scale=self.text_scale,
+            text_thickness=self.text_thickness,
+            text_padding=self.text_padding,
+            text_font=self.font,
+        )
+
+        return annotated_frame
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+annotate(scene, label=None) + +

+ + +
+ +

Annotates the polygon zone within a frame with a count of detected objects.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
scene + np.ndarray + +
+

The image on which the polygon zone will be annotated

+
+
+ required +
label + Optional[str] + +
+

An optional label for the count of detected objects within the polygon zone (default: None)

+
+
+ None +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ np.ndarray + +
+

np.ndarray: The image with the polygon zone and count of detected objects

+
+
+ +
+ Source code in supervision/detection/tools/polygon_zone.py +
102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
def annotate(self, scene: np.ndarray, label: Optional[str] = None) -> np.ndarray:
+    """
+    Annotates the polygon zone within a frame with a count of detected objects.
+
+    Parameters:
+        scene (np.ndarray): The image on which the polygon zone will be annotated
+        label (Optional[str]): An optional label for the count of detected objects within the polygon zone (default: None)
+
+    Returns:
+        np.ndarray: The image with the polygon zone and count of detected objects
+    """
+    annotated_frame = draw_polygon(
+        scene=scene,
+        polygon=self.zone.polygon,
+        color=self.color,
+        thickness=self.thickness,
+    )
+
+    annotated_frame = draw_text(
+        scene=annotated_frame,
+        text=str(self.zone.current_count) if label is None else label,
+        text_anchor=self.center,
+        background_color=self.color,
+        text_color=self.text_color,
+        text_scale=self.text_scale,
+        text_thickness=self.text_thickness,
+        text_padding=self.text_padding,
+        text_font=self.font,
+    )
+
+    return annotated_frame
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/detection/utils/index.html b/detection/utils/index.html new file mode 100644 index 000000000..7ea5118ef --- /dev/null +++ b/detection/utils/index.html @@ -0,0 +1,2004 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Utils - Supervision + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Utils

+ +

box_iou_batch

+ + +
+ + + +
+ +

Compute Intersection over Union (IoU) of two sets of bounding boxes - boxes_true and boxes_detection. Both sets +of boxes are expected to be in (x_min, y_min, x_max, y_max) format.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
boxes_true + np.ndarray + +
+

2D np.ndarray representing ground-truth boxes. shape = (N, 4) where N is number of true objects.

+
+
+ required +
boxes_detection + np.ndarray + +
+

2D np.ndarray representing detection boxes. shape = (M, 4) where M is number of detected objects.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ np.ndarray + +
+

np.ndarray: Pairwise IoU of boxes from boxes_true and boxes_detection. shape = (N, M) where N is number of true objects and M is number of detected objects.

+
+
+ +
+ Source code in supervision/detection/utils.py +
25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
def box_iou_batch(boxes_true: np.ndarray, boxes_detection: np.ndarray) -> np.ndarray:
+    """
+    Compute Intersection over Union (IoU) of two sets of bounding boxes - `boxes_true` and `boxes_detection`. Both sets
+    of boxes are expected to be in `(x_min, y_min, x_max, y_max)` format.
+
+    Args:
+        boxes_true (np.ndarray): 2D `np.ndarray` representing ground-truth boxes. `shape = (N, 4)` where `N` is number of true objects.
+        boxes_detection (np.ndarray): 2D `np.ndarray` representing detection boxes. `shape = (M, 4)` where `M` is number of detected objects.
+
+    Returns:
+        np.ndarray: Pairwise IoU of boxes from `boxes_true` and `boxes_detection`. `shape = (N, M)` where `N` is number of true objects and `M` is number of detected objects.
+    """
+
+    def box_area(box):
+        return (box[2] - box[0]) * (box[3] - box[1])
+
+    area_true = box_area(boxes_true.T)
+    area_detection = box_area(boxes_detection.T)
+
+    top_left = np.maximum(boxes_true[:, None, :2], boxes_detection[:, :2])
+    bottom_right = np.minimum(boxes_true[:, None, 2:], boxes_detection[:, 2:])
+
+    area_inter = np.prod(np.clip(bottom_right - top_left, a_min=0, a_max=None), 2)
+    return area_inter / (area_true[:, None] + area_detection - area_inter)
+
+
+
+ +

non_max_suppression

+ + +
+ + + +
+ +

Perform Non-Maximum Suppression (NMS) on object detection predictions.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
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).

+
+
+ required +
iou_threshold + float + +
+

The intersection-over-union threshold to use for non-maximum suppression.

+
+
+ 0.5 +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ np.ndarray + +
+

np.ndarray: A boolean array indicating which predictions to keep after non-maximum suppression.

+
+
+ +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ AssertionError + +
+

If iou_threshold is not within the closed range from 0 to 1.

+
+
+ +
+ Source code in supervision/detection/utils.py +
51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
def non_max_suppression(
+    predictions: np.ndarray, iou_threshold: float = 0.5
+) -> np.ndarray:
+    """
+    Perform Non-Maximum Suppression (NMS) on object detection predictions.
+
+    Args:
+        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 to use for non-maximum suppression.
+
+    Returns:
+        np.ndarray: A boolean array indicating which predictions to keep after non-maximum suppression.
+
+    Raises:
+        AssertionError: If `iou_threshold` is not within the closed range from `0` to `1`.
+    """
+    assert 0 <= iou_threshold <= 1, (
+        f"Value of `iou_threshold` must be in the closed range from 0 to 1, "
+        f"{iou_threshold} given."
+    )
+    rows, columns = predictions.shape
+
+    # add column #5 - category filled with zeros for agnostic nms
+    if columns == 5:
+        predictions = np.c_[predictions, np.zeros(rows)]
+
+    # sort predictions column #4 - score
+    sort_index = np.flip(predictions[:, 4].argsort())
+    predictions = predictions[sort_index]
+
+    boxes = predictions[:, :4]
+    categories = predictions[:, 5]
+    ious = box_iou_batch(boxes, boxes)
+    ious = ious - np.eye(rows)
+
+    keep = np.ones(rows, dtype=bool)
+
+    for index, (iou, category) in enumerate(zip(ious, categories)):
+        if not keep[index]:
+            continue
+
+        # drop detections with iou > iou_threshold and same category as current detections
+        condition = (iou > iou_threshold) & (categories == category)
+        keep = keep & ~condition
+
+    return keep[sort_index.argsort()]
+
+
+
+ +

polygon_to_mask

+ + +
+ + + +
+ +

Generate a mask from a polygon.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
polygon + np.ndarray + +
+

The polygon for which the mask should be generated, given as a list of vertices.

+
+
+ required +
resolution_wh + Tuple[int, int] + +
+

The width and height of the desired resolution.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ np.ndarray + +
+

np.ndarray: The generated 2D mask, where the polygon is marked with 1's and the rest is filled with 0's.

+
+
+ +
+ Source code in supervision/detection/utils.py +
 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
def polygon_to_mask(polygon: np.ndarray, resolution_wh: Tuple[int, int]) -> np.ndarray:
+    """Generate a mask from a polygon.
+
+    Args:
+        polygon (np.ndarray): The polygon for which the mask should be generated, given as a list of vertices.
+        resolution_wh (Tuple[int, int]): The width and height of the desired resolution.
+
+    Returns:
+        np.ndarray: The generated 2D mask, where the polygon is marked with `1`'s and the rest is filled with `0`'s.
+    """
+    width, height = resolution_wh
+    mask = np.zeros((height, width), dtype=np.uint8)
+    cv2.fillPoly(mask, [polygon], color=1)
+    return mask
+
+
+
+ +

mask_to_xyxy

+ + +
+ + + +
+ +

Converts a 3D np.array of 2D bool masks into a 2D np.array of bounding boxes.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
masks + np.ndarray + +
+

A 3D np.array of shape (N, W, H) containing 2D bool masks

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ np.ndarray + +
+

np.ndarray: A 2D np.array of shape (N, 4) containing the bounding boxes (x_min, y_min, x_max, y_max) for each mask

+
+
+ +
+ Source code in supervision/detection/utils.py +
129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
def mask_to_xyxy(masks: np.ndarray) -> np.ndarray:
+    """
+    Converts a 3D `np.array` of 2D bool masks into a 2D `np.array` of bounding boxes.
+
+    Parameters:
+        masks (np.ndarray): A 3D `np.array` of shape `(N, W, H)` containing 2D bool masks
+
+    Returns:
+        np.ndarray: A 2D `np.array` of shape `(N, 4)` containing the bounding boxes `(x_min, y_min, x_max, y_max)` for each mask
+    """
+    n = masks.shape[0]
+    bboxes = np.zeros((n, 4), dtype=int)
+
+    for i, mask in enumerate(masks):
+        rows, cols = np.where(mask)
+
+        if len(rows) > 0 and len(cols) > 0:
+            x_min, x_max = np.min(cols), np.max(cols)
+            y_min, y_max = np.min(rows), np.max(rows)
+            bboxes[i, :] = [x_min, y_min, x_max, y_max]
+
+    return bboxes
+
+
+
+ +

mask_to_polygons

+ + +
+ + + +
+ +

Converts a binary mask to a list of polygons.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
mask + np.ndarray + +
+

A binary mask represented as a 2D NumPy array of shape (H, W), +where H and W are the height and width of the mask, respectively.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ List[np.ndarray] + +
+

List[np.ndarray]: A list of polygons, where each polygon is represented by a NumPy array of shape (N, 2), +containing the x, y coordinates of the points. Polygons with fewer points than MIN_POLYGON_POINT_COUNT = 3 +are excluded from the output.

+
+
+ +
+ Source code in supervision/detection/utils.py +
153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
def mask_to_polygons(mask: np.ndarray) -> List[np.ndarray]:
+    """
+    Converts a binary mask to a list of polygons.
+
+    Parameters:
+        mask (np.ndarray): A binary mask represented as a 2D NumPy array of shape `(H, W)`,
+            where H and W are the height and width of the mask, respectively.
+
+    Returns:
+        List[np.ndarray]: A list of polygons, where each polygon is represented by a NumPy array of shape `(N, 2)`,
+            containing the `x`, `y` coordinates of the points. Polygons with fewer points than `MIN_POLYGON_POINT_COUNT = 3`
+            are excluded from the output.
+    """
+
+    contours, _ = cv2.findContours(
+        mask.astype(np.uint8), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE
+    )
+    return [
+        np.squeeze(contour, axis=1)
+        for contour in contours
+        if contour.shape[0] >= MIN_POLYGON_POINT_COUNT
+    ]
+
+
+
+ +

polygon_to_xyxy

+ + +
+ + + +
+ +

Converts a polygon represented by a NumPy array into a bounding box.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
polygon + np.ndarray + +
+

A polygon represented by a NumPy array of shape (N, 2), +containing the x, y coordinates of the points.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ np.ndarray + +
+

np.ndarray: A 1D NumPy array containing the bounding box (x_min, y_min, x_max, y_max) of the input polygon.

+
+
+ +
+ Source code in supervision/detection/utils.py +
207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
def polygon_to_xyxy(polygon: np.ndarray) -> np.ndarray:
+    """
+    Converts a polygon represented by a NumPy array into a bounding box.
+
+    Parameters:
+        polygon (np.ndarray): A polygon represented by a NumPy array of shape `(N, 2)`,
+            containing the `x`, `y` coordinates of the points.
+
+    Returns:
+        np.ndarray: A 1D NumPy array containing the bounding box `(x_min, y_min, x_max, y_max)` of the input polygon.
+    """
+    x_min, y_min = np.min(polygon, axis=0)
+    x_max, y_max = np.max(polygon, axis=0)
+    return np.array([x_min, y_min, x_max, y_max])
+
+
+
+ +

filter_polygons_by_area

+ + +
+ + + +
+ +

Filters a list of polygons based on their area.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
polygons + List[np.ndarray] + +
+

A list of polygons, where each polygon is represented by a NumPy array of shape (N, 2), +containing the x, y coordinates of the points.

+
+
+ required +
min_area + Optional[float] + +
+

The minimum area threshold. Only polygons with an area greater than or equal to this value +will be included in the output. If set to None, no minimum area constraint will be applied.

+
+
+ None +
max_area + Optional[float] + +
+

The maximum area threshold. Only polygons with an area less than or equal to this value +will be included in the output. If set to None, no maximum area constraint will be applied.

+
+
+ None +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ List[np.ndarray] + +
+

List[np.ndarray]: A new list of polygons containing only those with areas within the specified thresholds.

+
+
+ +
+ Source code in supervision/detection/utils.py +
177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
def filter_polygons_by_area(
+    polygons: List[np.ndarray],
+    min_area: Optional[float] = None,
+    max_area: Optional[float] = None,
+) -> List[np.ndarray]:
+    """
+    Filters a list of polygons based on their area.
+
+    Parameters:
+        polygons (List[np.ndarray]): A list of polygons, where each polygon is represented by a NumPy array of shape `(N, 2)`,
+            containing the `x`, `y` coordinates of the points.
+        min_area (Optional[float]): The minimum area threshold. Only polygons with an area greater than or equal to this value
+            will be included in the output. If set to None, no minimum area constraint will be applied.
+        max_area (Optional[float]): The maximum area threshold. Only polygons with an area less than or equal to this value
+            will be included in the output. If set to None, no maximum area constraint will be applied.
+
+    Returns:
+        List[np.ndarray]: A new list of polygons containing only those with areas within the specified thresholds.
+    """
+    if min_area is None and max_area is None:
+        return polygons
+    ares = [cv2.contourArea(polygon) for polygon in polygons]
+    return [
+        polygon
+        for polygon, area in zip(polygons, ares)
+        if (min_area is None or area >= min_area)
+        and (max_area is None or area <= max_area)
+    ]
+
+
+
+ +
+ + + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/draw/utils/index.html b/draw/utils/index.html new file mode 100644 index 000000000..cd4a539fc --- /dev/null +++ b/draw/utils/index.html @@ -0,0 +1,1960 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Utils - Supervision + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Utils

+ +

draw_line

+ + +
+ + + +
+ +

Draws a line on a given scene.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
scene + np.ndarray + +
+

The scene on which the line will be drawn

+
+
+ required +
start + Point + +
+

The starting point of the line

+
+
+ required +
end + Point + +
+

The end point of the line

+
+
+ required +
color + Color + +
+

The color of the line

+
+
+ required +
thickness + int + +
+

The thickness of the line

+
+
+ 2 +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ np.ndarray + +
+

np.ndarray: The scene with the line drawn on it

+
+
+ +
+ Source code in supervision/draw/utils.py +
10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
def draw_line(
+    scene: np.ndarray, start: Point, end: Point, color: Color, thickness: int = 2
+) -> np.ndarray:
+    """
+    Draws a line on a given scene.
+
+    Parameters:
+        scene (np.ndarray): The scene on which the line will be drawn
+        start (Point): The starting point of the line
+        end (Point): The end point of the line
+        color (Color): The color of the line
+        thickness (int): The thickness of the line
+
+    Returns:
+        np.ndarray: The scene with the line drawn on it
+    """
+    cv2.line(
+        scene,
+        start.as_xy_int_tuple(),
+        end.as_xy_int_tuple(),
+        color.as_bgr(),
+        thickness=thickness,
+    )
+    return scene
+
+
+
+ +

draw_rectangle

+ + +
+ + + +
+ +

Draws a rectangle on an image.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
scene + np.ndarray + +
+

The scene on which the rectangle will be drawn

+
+
+ required +
rect + Rect + +
+

The rectangle to be drawn

+
+
+ required +
color + Color + +
+

The color of the rectangle

+
+
+ required +
thickness + int + +
+

The thickness of the rectangle border

+
+
+ 2 +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ np.ndarray + +
+

np.ndarray: The scene with the rectangle drawn on it

+
+
+ +
+ Source code in supervision/draw/utils.py +
36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
def draw_rectangle(
+    scene: np.ndarray, rect: Rect, color: Color, thickness: int = 2
+) -> np.ndarray:
+    """
+    Draws a rectangle on an image.
+
+    Parameters:
+        scene (np.ndarray): The scene on which the rectangle will be drawn
+        rect (Rect): The rectangle to be drawn
+        color (Color): The color of the rectangle
+        thickness (int): The thickness of the rectangle border
+
+    Returns:
+        np.ndarray: The scene with the rectangle drawn on it
+    """
+    cv2.rectangle(
+        scene,
+        rect.top_left.as_xy_int_tuple(),
+        rect.bottom_right.as_xy_int_tuple(),
+        color.as_bgr(),
+        thickness=thickness,
+    )
+    return scene
+
+
+
+ +

draw_filled_rectangle

+ + +
+ + + +
+ +

Draws a filled rectangle on an image.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
scene + np.ndarray + +
+

The scene on which the rectangle will be drawn

+
+
+ required +
rect + Rect + +
+

The rectangle to be drawn

+
+
+ required +
color + Color + +
+

The color of the rectangle

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ np.ndarray + +
+

np.ndarray: The scene with the rectangle drawn on it

+
+
+ +
+ Source code in supervision/draw/utils.py +
61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
def draw_filled_rectangle(scene: np.ndarray, rect: Rect, color: Color) -> np.ndarray:
+    """
+    Draws a filled rectangle on an image.
+
+    Parameters:
+        scene (np.ndarray): The scene on which the rectangle will be drawn
+        rect (Rect): The rectangle to be drawn
+        color (Color): The color of the rectangle
+
+    Returns:
+        np.ndarray: The scene with the rectangle drawn on it
+    """
+    cv2.rectangle(
+        scene,
+        rect.top_left.as_xy_int_tuple(),
+        rect.bottom_right.as_xy_int_tuple(),
+        color.as_bgr(),
+        -1,
+    )
+    return scene
+
+
+
+ +

draw_polygon

+ + +
+ + + +
+ +

Draw a polygon on a scene.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
scene + np.ndarray + +
+

The scene to draw the polygon on.

+
+
+ required +
polygon + np.ndarray + +
+

The polygon to be drawn, given as a list of vertices.

+
+
+ required +
color + Color + +
+

The color of the polygon.

+
+
+ required +
thickness + int + +
+

The thickness of the polygon lines, by default 2.

+
+
+ 2 +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ np.ndarray + +
+

np.ndarray: The scene with the polygon drawn on it.

+
+
+ +
+ Source code in supervision/draw/utils.py +
 83
+ 84
+ 85
+ 86
+ 87
+ 88
+ 89
+ 90
+ 91
+ 92
+ 93
+ 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
def draw_polygon(
+    scene: np.ndarray, polygon: np.ndarray, color: Color, thickness: int = 2
+) -> np.ndarray:
+    """Draw a polygon on a scene.
+
+    Parameters:
+        scene (np.ndarray): The scene to draw the polygon on.
+        polygon (np.ndarray): The polygon to be drawn, given as a list of vertices.
+        color (Color): The color of the polygon.
+        thickness (int, optional): The thickness of the polygon lines, by default 2.
+
+    Returns:
+        np.ndarray: The scene with the polygon drawn on it.
+    """
+    cv2.polylines(
+        scene, [polygon], isClosed=True, color=color.as_bgr(), thickness=thickness
+    )
+    return scene
+
+
+
+ +

draw_text

+ + +
+ + + +
+ +

Draw text with background on a scene.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
scene + np.ndarray + +
+

A 2-dimensional numpy ndarray representing an image or scene.

+
+
+ required +
text + str + +
+

The text to be drawn.

+
+
+ required +
text_anchor + Point + +
+

The anchor point for the text, represented as a Point object with x and y attributes.

+
+
+ required +
text_color + Color + +
+

The color of the text. Defaults to black.

+
+
+ Color.black() +
text_scale + float + +
+

The scale of the text. Defaults to 0.5.

+
+
+ 0.5 +
text_thickness + int + +
+

The thickness of the text. Defaults to 1.

+
+
+ 1 +
text_padding + int + +
+

The amount of padding to add around the text when drawing a rectangle in the background. Defaults to 10.

+
+
+ 10 +
text_font + int + +
+

The font to use for the text. Defaults to cv2.FONT_HERSHEY_SIMPLEX.

+
+
+ cv2.FONT_HERSHEY_SIMPLEX +
background_color + Color + +
+

The color of the background rectangle, if one is to be drawn. Defaults to None.

+
+
+ None +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ np.ndarray + +
+

np.ndarray: The input scene with the text drawn on it.

+
+
+ +

Examples:

+
>>> scene = np.zeros((100, 100, 3), dtype=np.uint8)
+>>> text_anchor = Point(x=50, y=50)
+>>> scene = draw_text(scene=scene, text="Hello, world!", text_anchor=text_anchor)
+
+ +
+ Source code in supervision/draw/utils.py +
103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
def draw_text(
+    scene: np.ndarray,
+    text: str,
+    text_anchor: Point,
+    text_color: Color = Color.black(),
+    text_scale: float = 0.5,
+    text_thickness: int = 1,
+    text_padding: int = 10,
+    text_font: int = cv2.FONT_HERSHEY_SIMPLEX,
+    background_color: Optional[Color] = None,
+) -> np.ndarray:
+    """
+    Draw text with background on a scene.
+
+    Parameters:
+        scene (np.ndarray): A 2-dimensional numpy ndarray representing an image or scene.
+        text (str): The text to be drawn.
+        text_anchor (Point): The anchor point for the text, represented as a Point object with x and y attributes.
+        text_color (Color, optional): The color of the text. Defaults to black.
+        text_scale (float, optional): The scale of the text. Defaults to 0.5.
+        text_thickness (int, optional): The thickness of the text. Defaults to 1.
+        text_padding (int, optional): The amount of padding to add around the text when drawing a rectangle in the background. Defaults to 10.
+        text_font (int, optional): The font to use for the text. Defaults to cv2.FONT_HERSHEY_SIMPLEX.
+        background_color (Color, optional): The color of the background rectangle, if one is to be drawn. Defaults to None.
+
+    Returns:
+        np.ndarray: The input scene with the text drawn on it.
+
+    Examples:
+        ```python
+        >>> scene = np.zeros((100, 100, 3), dtype=np.uint8)
+        >>> text_anchor = Point(x=50, y=50)
+        >>> scene = draw_text(scene=scene, text="Hello, world!", text_anchor=text_anchor)
+        ```
+    """
+    text_width, text_height = cv2.getTextSize(
+        text=text,
+        fontFace=text_font,
+        fontScale=text_scale,
+        thickness=text_thickness,
+    )[0]
+    text_rect = Rect(
+        x=text_anchor.x - text_width // 2,
+        y=text_anchor.y - text_height // 2,
+        width=text_width,
+        height=text_height,
+    ).pad(text_padding)
+
+    if background_color is not None:
+        scene = draw_filled_rectangle(
+            scene=scene, rect=text_rect, color=background_color
+        )
+
+    cv2.putText(
+        img=scene,
+        text=text,
+        org=(text_anchor.x - text_width // 2, text_anchor.y + text_height // 2),
+        fontFace=text_font,
+        fontScale=text_scale,
+        color=text_color.as_bgr(),
+        thickness=text_thickness,
+        lineType=cv2.LINE_AA,
+    )
+    return scene
+
+
+
+ +
+ + + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 000000000..833545a29 --- /dev/null +++ b/index.html @@ -0,0 +1,958 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Supervision + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Home

+ +
+

+ + + +

+
+ +

👋 Welcome

+

Supervision is a set of easy-to-use utilities that will come in handy in any computer vision project.

+

Supervision is still in +pre-release stage 🚧 Keep your eyes open for potential bugs and be aware that at this stage our API is still fluid and may change.

+

💻 How to Install

+

You can install supervision with pip in a +3.11>=Python>=3.7 environment.

+
+

Pip install method (recommended)

+
pip install supervision
+
+
+
+

Git clone method (for development)

+

git https://github.com/roboflow/supervision.git
+cd supervision
+pip install -e '.[dev]'
+
+See contributing section to know more about contributing to the project

+
+ + + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/objects.inv b/objects.inv new file mode 100644 index 000000000..469fa9510 Binary files /dev/null and b/objects.inv differ diff --git a/quickstart/detections/index.html b/quickstart/detections/index.html new file mode 100644 index 000000000..b72ca195a --- /dev/null +++ b/quickstart/detections/index.html @@ -0,0 +1,1286 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Detections - Supervision + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Detections

+ +

advanced filtering

+

The advanced filtering capabilities of the Detections class offer users a versatile and efficient way to narrow down +and refine object detections. This section outlines various filtering methods, including filtering by specific class +or a set of classes, confidence, object area, bounding box area, relative area, box dimensions, and designated zones. +Each method is demonstrated with concise code examples to provide users with a clear understanding of how to implement +the filters in their applications.

+

by specific class

+

Allows you to select detections that belong only to one selected class.

+
+
+
+
import supervision as sv
+
+detections = sv.Detections(...)
+detections = detections[detections.class_id == 0]
+
+
+

by-specific-class

+
+
+
+
import supervision as sv
+
+detections = sv.Detections(...)
+detections = detections[detections.class_id == 0]
+
+
+

original

+
+
+
+
+

by set of classes

+

Allows you to select detections that belong only to selected set of classes.

+
+
+
+
import numpy as np
+import supervision as sv
+
+selected_classes = [0, 2, 3]
+detections = sv.Detections(...)
+detections = detections[np.isin(detections.class_id, selected_classes)]
+
+
+

by-set-of-classes

+
+
+
+
import numpy as np
+import supervision as sv
+
+class_id = [0, 2, 3]
+detections = sv.Detections(...)
+detections = detections[np.isin(detections.class_id, class_id)]
+
+
+

original

+
+
+
+
+

by confidence

+

Allows you to select detections with specific confidence value, for example higher than selected threshold.

+
+
+
+
import supervision as sv
+
+detections = sv.Detections(...)
+detections = detections[detections.confidence > 0.5]
+
+
+

by-set-of-classes

+
+
+
+
import supervision as sv
+
+detections = sv.Detections(...)
+detections = detections[detections.confidence > 0.5]
+
+
+

original

+
+
+
+
+

by area

+

Allows you to select detections based on their size. We define the area as the number of pixels occupied by the +detection in the image. In the example below, we have sifted out the detections that are too small.

+
+
+
+
import supervision as sv
+
+detections = sv.Detections(...)
+detections = detections[detections.area > 1000]
+
+
+

by-area

+
+
+
+
import supervision as sv
+
+detections = sv.Detections(...)
+detections = detections[detections.area > 1000]
+
+
+

original

+
+
+
+
+

by relative area

+

Allows you to select detections based on their size in relation to the size of whole image. Sometimes the concept of +detection size changes depending on the image. Detection occupying 10000 square px can be large on a 1280x720 image +but small on a 3840x2160 image. In such cases, we can filter out detections based on the percentage of the image area +occupied by them. In the example below, we remove too large detections.

+
+
+
+
import supervision as sv
+
+image = ...
+height, width, channels = image.shape
+image_area = height * width
+
+detections = sv.Detections(...)
+detections = detections[(detections.area / image_area) < 0.8]
+
+
+

by-relative-area

+
+
+
+
import supervision as sv
+
+image = ...
+height, width, channels = image.shape
+image_area = height * width
+
+detections = sv.Detections(...)
+detections = detections[(detections.area / image_area) < 0.8]
+
+
+

original

+
+
+
+
+

by box dimensions

+

Allows you to select detections based on their dimensions. The size of the bounding box, as well as its coordinates, +can be criteria for rejecting detection. Implementing such filtering requires a bit of custom code but is relatively +simple and fast.

+
+
+
+
import supervision as sv
+
+detections = sv.Detections(...)
+w = detections.xyxy[:, 2] - detections.xyxy[:, 0]
+h = detections.xyxy[:, 3] - detections.xyxy[:, 1]
+detections = detections[(w > 200) & (h > 200)]
+
+
+

by-box-dimensions

+
+
+
+
import supervision as sv
+
+detections = sv.Detections(...)
+w = detections.xyxy[:, 2] - detections.xyxy[:, 0]
+h = detections.xyxy[:, 3] - detections.xyxy[:, 1]
+detections = detections[(w > 200) & (h > 200)]
+
+
+

original

+
+
+
+
+

by PolygonZone

+

Allows you to use Detections in combination with PolygonZone to weed out bounding boxes that are in and out of the +zone. In the example below you can see how to filter out all detections located in the lower part of the image.

+
+
+
+
import supervision as sv
+
+zone = sv.PolygonZone(...)
+detections = sv.Detections(...)
+mask = zone.trigger(detections=detections)
+detections = detections[mask]
+
+
+

by-polygon-zone

+
+
+
+
import supervision as sv
+
+zone = sv.PolygonZone(...)
+detections = sv.Detections(...)
+mask = zone.trigger(detections=detections)
+detections = detections[mask]
+
+
+

original

+
+
+
+
+

by mixed conditions

+

Detections' greatest strength, however, is that you can build arbitrarily complex logical conditions by simply combining separate conditions using & or |.

+
+
+
+
import supervision as sv
+
+zone = sv.PolygonZone(...)
+detections = sv.Detections(...)
+mask = zone.trigger(detections=detections)
+detections = detections[(detections.confidence > 0.7) & mask]
+
+
+

by-mixed-conditions

+
+
+
+
import supervision as sv
+
+zone = sv.PolygonZone(...)
+detections = sv.Detections(...)
+mask = zone.trigger(detections=detections)
+detections = detections[mask]
+
+
+

original

+
+
+
+
+ + + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/search/search_index.json b/search/search_index.json new file mode 100644 index 000000000..fbd33db10 --- /dev/null +++ b/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#welcome","title":"\ud83d\udc4b Welcome","text":"

Supervision is a set of easy-to-use utilities that will come in handy in any computer vision project.

Supervision is still in pre-release stage \ud83d\udea7 Keep your eyes open for potential bugs and be aware that at this stage our API is still fluid and may change.

"},{"location":"#how-to-install","title":"\ud83d\udcbb How to Install","text":"

You can install supervision with pip in a 3.11>=Python>=3.7 environment.

Pip install method (recommended)

pip install supervision\n

Git clone method (for development)

git https://github.com/roboflow/supervision.git\ncd supervision\npip install -e '.[dev]'\n
See contributing section to know more about contributing to the project

"},{"location":"changelog/","title":"Changelog","text":""},{"location":"changelog/#0100-june-14-2023","title":"0.10.0 June 14, 2023","text":"
  • Added [#125]: ability to load and save sv.ClassificationDataset in a folder structure format.
>>> import supervision as sv\n\n>>> cs = sv.ClassificationDataset.from_folder_structure(\n...     root_directory_path='...'\n... )\n\n>>> cs.as_folder_structure(\n...     root_directory_path='...'\n... )\n
  • Added [#125]: support for sv.ClassificationDataset.split allowing to divide sv.ClassificationDataset into two parts.

  • Added [#110]: ability to extract masks from Roboflow API results using sv.Detections.from_roboflow.

  • Added [commit hash]: Supervision Quickstart notebook where you can learn more about Detection, Dataset and Video APIs.

  • Changed [#135]: sv.get_video_frames_generator documentation to better describe actual behavior.

"},{"location":"changelog/#090-june-7-2023","title":"0.9.0 June 7, 2023","text":"
  • Added [#118]: ability to select sv.Detections by index, list of indexes or slice. Here is an example illustrating the new selection methods.
>>> import supervision as sv\n\n>>> detections = sv.Detections(...)\n>>> len(detections[0])\n1\n>>> len(detections[[0, 1]])\n2\n>>> len(detections[0:2])\n2\n
  • Added [#101]: ability to extract masks from YOLOv8 result using sv.Detections.from_yolov8. Here is an example illustrating how to extract boolean masks from the result of the YOLOv8 model inference.

  • Added [#122]: ability to crop image using sv.crop. Here is an example showing how to get a separate crop for each detection in sv.Detections.

  • Added [#120]: ability to conveniently save multiple images into directory using sv.ImageSink. Here is an example showing how to save every tenth video frame as a separate image.

>>> import supervision as sv\n\n>>> with sv.ImageSink(target_dir_path='target/directory/path') as sink:\n...     for image in sv.get_video_frames_generator(source_path='source_video.mp4', stride=10):\n...         sink.save_image(image=image)\n
  • Fixed [#106]: inconvenient handling of sv.PolygonZone coordinates. Now sv.PolygonZone accepts coordinates in the form of [[x1, y1], [x2, y2], ...] that can be both integers and floats.
"},{"location":"changelog/#080-may-17-2023","title":"0.8.0 May 17, 2023","text":"
  • Added [#100]: support for dataset inheritance. The current Dataset got renamed to DetectionDataset. Now DetectionDataset inherits from BaseDataset. This change was made to enforce the future consistency of APIs of different types of computer vision datasets.
  • Added [#100]: ability to save datasets in YOLO format using DetectionDataset.as_yolo.
>>> import roboflow\n>>> from roboflow import Roboflow\n>>> import supervision as sv\n\n>>> roboflow.login()\n\n>>> rf = Roboflow()\n\n>>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)\n>>> dataset = project.version(PROJECT_VERSION).download(\"yolov5\")\n\n>>> ds = sv.DetectionDataset.from_yolo(\n...     images_directory_path=f\"{dataset.location}/train/images\",\n...     annotations_directory_path=f\"{dataset.location}/train/labels\",\n...     data_yaml_path=f\"{dataset.location}/data.yaml\"\n... )\n\n>>> ds.classes\n['dog', 'person']\n
  • Added [#102]: support for DetectionDataset.split allowing to divide DetectionDataset into two parts.
>>> import supervision as sv\n\n>>> ds = sv.DetectionDataset(...)\n>>> train_ds, test_ds = ds.split(split_ratio=0.7, random_state=42, shuffle=True)\n\n>>> len(train_ds), len(test_ds)\n(700, 300)\n
  • Changed [#100]: default value of approximation_percentage parameter from 0.75 to 0.0 in DetectionDataset.as_yolo and DetectionDataset.as_pascal_voc.
"},{"location":"changelog/#070-may-11-2023","title":"0.7.0 May 11, 2023","text":"
  • Added [#91]: Detections.from_yolo_nas to enable seamless integration with YOLO-NAS model.
  • Added [#86]: ability to load datasets in YOLO format using Dataset.from_yolo.
  • Added [#84]: Detections.merge to merge multiple Detections objects together.
  • Fixed [#81]: LineZoneAnnotator.annotate does not return annotated frame.
  • Changed [#44]: LineZoneAnnotator.annotate to allow for custom text for the in and out tags.
"},{"location":"changelog/#060-april-19-2023","title":"0.6.0 April 19, 2023","text":"
  • Added [#71]: initial Dataset support and ability to save Detections in Pascal VOC XML format.
  • Added [#71]: new mask_to_polygons, filter_polygons_by_area, polygon_to_xyxy and approximate_polygon utilities.
  • Added [#72]: ability to load Pascal VOC XML object detections dataset as Dataset.
  • Changed [#70]: order of Detections attributes to make it consistent with order of objects in __iter__ tuple.
  • Changed [#71]: generate_2d_mask to polygon_to_mask.
"},{"location":"changelog/#052-april-13-2023","title":"0.5.2 April 13, 2023","text":"
  • Fixed [#63]: LineZone.trigger function expects 4 values instead of 5.
"},{"location":"changelog/#051-april-12-2023","title":"0.5.1 April 12, 2023","text":"
  • Fixed Detections.__getitem__ method did not return mask for selected item.
  • Fixed Detections.area crashed for mask detections.
"},{"location":"changelog/#050-april-10-2023","title":"0.5.0 April 10, 2023","text":"
  • Added [#58]: Detections.mask to enable segmentation support.
  • Added [#58]: MaskAnnotator to allow easy Detections.mask annotation.
  • Added [#58]: Detections.from_sam to enable native Segment Anything Model (SAM) support.
  • Changed [#58]: Detections.area behaviour to work not only with boxes but also with masks.
"},{"location":"changelog/#040-april-5-2023","title":"0.4.0 April 5, 2023","text":"
  • Added [#46]: Detections.empty to allow easy creation of empty Detections objects.
  • Added [#56]: Detections.from_roboflow to allow easy creation of Detections objects from Roboflow API inference results.
  • Added [#56]: plot_images_grid to allow easy plotting of multiple images on single plot.
  • Added [#56]: initial support for Pascal VOC XML format with detections_to_voc_xml method.
  • Changed [#56]: show_frame_in_notebook refactored and renamed to plot_image.
"},{"location":"changelog/#032-march-23-2023","title":"0.3.2 March 23, 2023","text":"
  • Changed [#50]: Allow Detections.class_id to be None.
"},{"location":"changelog/#031-march-6-2023","title":"0.3.1 March 6, 2023","text":"
  • Fixed [#41]: PolygonZone throws an exception when the object touches the bottom edge of the image.
  • Fixed [#42]: Detections.wth_nms method throws an exception when Detections is empty.
  • Changed [#36]: Detections.wth_nms support class agnostic and non-class agnostic case.
"},{"location":"changelog/#030-march-6-2023","title":"0.3.0 March 6, 2023","text":"
  • Changed: Allow Detections.confidence to be None.
  • Added: Detections.from_transformers and Detections.from_detectron2 to enable seamless integration with Transformers and Detectron2 models.
  • Added: Detections.area to dynamically calculate bounding box area.
  • Added: Detections.wth_nms to filter out double detections with NMS. Initial - only class agnostic - implementation.
"},{"location":"changelog/#020-february-2-2023","title":"0.2.0 February 2, 2023","text":"
  • Added: Advanced Detections filtering with pandas-like API.
  • Added: Detections.from_yolov5 and Detections.from_yolov8 to enable seamless integration with YOLOv5 and YOLOv8 models.
"},{"location":"changelog/#010-january-19-2023","title":"0.1.0 January 19, 2023","text":"

Say hello to Supervision \ud83d\udc4b

"},{"location":"classification/core/","title":"Core","text":""},{"location":"classification/core/#classifications","title":"Classifications","text":"Source code in supervision/classification/core.py
@dataclass\nclass Classifications:\n    class_id: np.ndarray\n    confidence: Optional[np.ndarray] = None\n\n    def __post_init__(self) -> None:\n\"\"\"\n        Validate the classification inputs.\n        \"\"\"\n        n = len(self.class_id)\n\n        _validate_class_ids(self.class_id, n)\n        _validate_confidence(self.confidence, n)\n\n    @classmethod\n    def from_yolov8(cls, yolov8_results) -> Classifications:\n\"\"\"\n        Creates a Classifications instance from a [YOLOv8](https://github.com/ultralytics/ultralytics) inference result.\n\n        Args:\n            yolov8_results (ultralytics.yolo.engine.results.Results): The output Results instance from YOLOv8\n\n        Returns:\n            Detections: A new Classifications object.\n\n        Example:\n            ```python\n            >>> import cv2\n            >>> from ultralytics import YOLO\n            >>> import supervision as sv\n\n            >>> image = cv2.imread(SOURCE_IMAGE_PATH)\n            >>> model = YOLO('yolov8s-cls.pt')\n            >>> result = model(image)[0]\n            >>> classifications = sv.Classifications.from_yolov8(result)\n            ```\n        \"\"\"\n        confidence = yolov8_results.probs.data.cpu().numpy()\n        return cls(class_id=np.arange(confidence.shape[0]), confidence=confidence)\n\n    def get_top_k(self, k: int) -> Tuple[np.ndarray, np.ndarray]:\n\"\"\"\n        Retrieve the top k class IDs and confidences, ordered in descending order by confidence.\n\n        Args:\n            k (int): The number of top class IDs and confidences to retrieve.\n\n        Returns:\n            Tuple[np.ndarray, np.ndarray]: A tuple containing the top k class IDs and confidences.\n\n        Example:\n            ```python\n            >>> import supervision as sv\n\n            >>> classifications = sv.Classifications(...)\n\n            >>> classifications.get_top_k(1)\n\n            (array([1]), array([0.9]))\n            ```\n        \"\"\"\n        if self.confidence is None:\n            raise ValueError(\"top_k could not be calculated, confidence is None\")\n\n        order = np.argsort(self.confidence)[::-1]\n        top_k_order = order[:k]\n        top_k_class_id = self.class_id[top_k_order]\n        top_k_confidence = self.confidence[top_k_order]\n\n        return top_k_class_id, top_k_confidence\n
"},{"location":"classification/core/#supervision.classification.core.Classifications.__post_init__","title":"__post_init__()","text":"

Validate the classification inputs.

Source code in supervision/classification/core.py
def __post_init__(self) -> None:\n\"\"\"\n    Validate the classification inputs.\n    \"\"\"\n    n = len(self.class_id)\n\n    _validate_class_ids(self.class_id, n)\n    _validate_confidence(self.confidence, n)\n
"},{"location":"classification/core/#supervision.classification.core.Classifications.from_yolov8","title":"from_yolov8(yolov8_results) classmethod","text":"

Creates a Classifications instance from a YOLOv8 inference result.

Parameters:

Name Type Description Default yolov8_results ultralytics.yolo.engine.results.Results

The output Results instance from YOLOv8

required

Returns:

Name Type Description Detections Classifications

A new Classifications object.

Example
>>> import cv2\n>>> from ultralytics import YOLO\n>>> import supervision as sv\n\n>>> image = cv2.imread(SOURCE_IMAGE_PATH)\n>>> model = YOLO('yolov8s-cls.pt')\n>>> result = model(image)[0]\n>>> classifications = sv.Classifications.from_yolov8(result)\n
Source code in supervision/classification/core.py
@classmethod\ndef from_yolov8(cls, yolov8_results) -> Classifications:\n\"\"\"\n    Creates a Classifications instance from a [YOLOv8](https://github.com/ultralytics/ultralytics) inference result.\n\n    Args:\n        yolov8_results (ultralytics.yolo.engine.results.Results): The output Results instance from YOLOv8\n\n    Returns:\n        Detections: A new Classifications object.\n\n    Example:\n        ```python\n        >>> import cv2\n        >>> from ultralytics import YOLO\n        >>> import supervision as sv\n\n        >>> image = cv2.imread(SOURCE_IMAGE_PATH)\n        >>> model = YOLO('yolov8s-cls.pt')\n        >>> result = model(image)[0]\n        >>> classifications = sv.Classifications.from_yolov8(result)\n        ```\n    \"\"\"\n    confidence = yolov8_results.probs.data.cpu().numpy()\n    return cls(class_id=np.arange(confidence.shape[0]), confidence=confidence)\n
"},{"location":"classification/core/#supervision.classification.core.Classifications.get_top_k","title":"get_top_k(k)","text":"

Retrieve the top k class IDs and confidences, ordered in descending order by confidence.

Parameters:

Name Type Description Default k int

The number of top class IDs and confidences to retrieve.

required

Returns:

Type Description Tuple[np.ndarray, np.ndarray]

Tuple[np.ndarray, np.ndarray]: A tuple containing the top k class IDs and confidences.

Example
>>> import supervision as sv\n\n>>> classifications = sv.Classifications(...)\n\n>>> classifications.get_top_k(1)\n\n(array([1]), array([0.9]))\n
Source code in supervision/classification/core.py
def get_top_k(self, k: int) -> Tuple[np.ndarray, np.ndarray]:\n\"\"\"\n    Retrieve the top k class IDs and confidences, ordered in descending order by confidence.\n\n    Args:\n        k (int): The number of top class IDs and confidences to retrieve.\n\n    Returns:\n        Tuple[np.ndarray, np.ndarray]: A tuple containing the top k class IDs and confidences.\n\n    Example:\n        ```python\n        >>> import supervision as sv\n\n        >>> classifications = sv.Classifications(...)\n\n        >>> classifications.get_top_k(1)\n\n        (array([1]), array([0.9]))\n        ```\n    \"\"\"\n    if self.confidence is None:\n        raise ValueError(\"top_k could not be calculated, confidence is None\")\n\n    order = np.argsort(self.confidence)[::-1]\n    top_k_order = order[:k]\n    top_k_class_id = self.class_id[top_k_order]\n    top_k_confidence = self.confidence[top_k_order]\n\n    return top_k_class_id, top_k_confidence\n
"},{"location":"dataset/core/","title":"Core","text":"

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.

"},{"location":"dataset/core/#detectiondataset","title":"DetectionDataset","text":"

Bases: BaseDataset

Dataclass containing information about object detection dataset.

Attributes:

Name Type Description classes List[str]

List containing dataset class names.

images Dict[str, np.ndarray]

Dictionary mapping image name to image.

annotations Dict[str, Detections]

Dictionary mapping image name to annotations.

Source code in supervision/dataset/core.py
@dataclass\nclass DetectionDataset(BaseDataset):\n\"\"\"\n    Dataclass containing information about object detection dataset.\n\n    Attributes:\n        classes (List[str]): List containing dataset class names.\n        images (Dict[str, np.ndarray]): Dictionary mapping image name to image.\n        annotations (Dict[str, Detections]): Dictionary mapping image name to annotations.\n    \"\"\"\n\n    classes: List[str]\n    images: Dict[str, np.ndarray]\n    annotations: Dict[str, Detections]\n\n    def __len__(self) -> int:\n\"\"\"\n        Return the number of images in the dataset.\n\n        Returns:\n            int: The number of images.\n        \"\"\"\n        return len(self.images)\n\n    def __iter__(self) -> Iterator[Tuple[str, np.ndarray, Detections]]:\n\"\"\"\n        Iterate over the images and annotations in the dataset.\n\n        Yields:\n            Iterator[Tuple[str, np.ndarray, Detections]]: An iterator that yields tuples containing the image name,\n                                                          the image data, and its corresponding annotation.\n        \"\"\"\n        for image_name, image in self.images.items():\n            yield image_name, image, self.annotations.get(image_name, None)\n\n    def split(\n        self, split_ratio=0.8, random_state=None, shuffle: bool = True\n    ) -> Tuple[DetectionDataset, DetectionDataset]:\n\"\"\"\n        Splits the dataset into two parts (training and testing) using the provided split_ratio.\n\n        Args:\n            split_ratio (float, optional): The ratio of the training set to the entire dataset.\n            random_state (int, optional): The seed for the random number generator. This is used for reproducibility.\n            shuffle (bool, optional): Whether to shuffle the data before splitting.\n\n        Returns:\n            Tuple[DetectionDataset, DetectionDataset]: A tuple containing the training and testing datasets.\n\n        Example:\n            ```python\n            >>> import supervision as sv\n\n            >>> ds = sv.DetectionDataset(...)\n            >>> train_ds, test_ds = ds.split(split_ratio=0.7, random_state=42, shuffle=True)\n            >>> len(train_ds), len(test_ds)\n            (700, 300)\n            ```\n        \"\"\"\n\n        image_names = list(self.images.keys())\n        train_names, test_names = train_test_split(\n            data=image_names,\n            train_ratio=split_ratio,\n            random_state=random_state,\n            shuffle=shuffle,\n        )\n\n        train_dataset = DetectionDataset(\n            classes=self.classes,\n            images={name: self.images[name] for name in train_names},\n            annotations={name: self.annotations[name] for name in train_names},\n        )\n        test_dataset = DetectionDataset(\n            classes=self.classes,\n            images={name: self.images[name] for name in test_names},\n            annotations={name: self.annotations[name] for name in test_names},\n        )\n        return train_dataset, test_dataset\n\n    def as_pascal_voc(\n        self,\n        images_directory_path: Optional[str] = None,\n        annotations_directory_path: Optional[str] = None,\n        min_image_area_percentage: float = 0.0,\n        max_image_area_percentage: float = 1.0,\n        approximation_percentage: float = 0.0,\n    ) -> None:\n\"\"\"\n        Exports the dataset to PASCAL VOC format. This method saves the images and their corresponding annotations in\n        PASCAL VOC format, which consists of XML files. The method allows filtering the detections based on their area\n        percentage.\n\n        Args:\n            images_directory_path (Optional[str]): The path to the directory where the images should be saved.\n                If not provided, images will not be saved.\n            annotations_directory_path (Optional[str]): The path to the directory where the annotations in\n                PASCAL VOC format should be saved. If not provided, annotations will not be saved.\n            min_image_area_percentage (float): The minimum percentage of detection area relative to\n                the image area for a detection to be included.\n            max_image_area_percentage (float): The maximum percentage of detection area relative to\n                the image area for a detection to be included.\n            approximation_percentage (float): The percentage of polygon points to be removed from the input polygon, in the range [0, 1).\n        \"\"\"\n        if images_directory_path:\n            images_path = Path(images_directory_path)\n            images_path.mkdir(parents=True, exist_ok=True)\n\n        if annotations_directory_path:\n            annotations_path = Path(annotations_directory_path)\n            annotations_path.mkdir(parents=True, exist_ok=True)\n\n        for image_name, image in self.images.items():\n            detections = self.annotations[image_name]\n\n            if images_directory_path:\n                cv2.imwrite(str(images_path / image_name), image)\n\n            if annotations_directory_path:\n                annotation_name = Path(image_name).stem\n                pascal_voc_xml = detections_to_pascal_voc(\n                    detections=detections,\n                    classes=self.classes,\n                    filename=image_name,\n                    image_shape=image.shape,\n                    min_image_area_percentage=min_image_area_percentage,\n                    max_image_area_percentage=max_image_area_percentage,\n                    approximation_percentage=approximation_percentage,\n                )\n\n                with open(annotations_path / f\"{annotation_name}.xml\", \"w\") as f:\n                    f.write(pascal_voc_xml)\n\n    @classmethod\n    def from_pascal_voc(\n        cls, images_directory_path: str, annotations_directory_path: str\n    ) -> DetectionDataset:\n\"\"\"\n        Creates a Dataset instance from PASCAL VOC formatted data.\n\n        Args:\n            images_directory_path (str): The path to the directory containing the images.\n            annotations_directory_path (str): The path to the directory containing the PASCAL VOC XML annotations.\n\n        Returns:\n            DetectionDataset: A DetectionDataset instance containing the loaded images and annotations.\n\n        Example:\n            ```python\n            >>> import roboflow\n            >>> from roboflow import Roboflow\n            >>> import supervision as sv\n\n            >>> roboflow.login()\n\n            >>> rf = Roboflow()\n\n            >>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)\n            >>> dataset = project.version(PROJECT_VERSION).download(\"voc\")\n\n            >>> ds = sv.DetectionDataset.from_yolo(\n            ...     images_directory_path=f\"{dataset.location}/train/images\",\n            ...     annotations_directory_path=f\"{dataset.location}/train/labels\"\n            ... )\n\n            >>> ds.classes\n            ['dog', 'person']\n            ```\n        \"\"\"\n        image_paths = list_files_with_extensions(\n            directory=images_directory_path, extensions=[\"jpg\", \"jpeg\", \"png\"]\n        )\n        annotation_paths = list_files_with_extensions(\n            directory=annotations_directory_path, extensions=[\"xml\"]\n        )\n\n        raw_annotations: List[Tuple[str, Detections, List[str]]] = [\n            load_pascal_voc_annotations(annotation_path=str(annotation_path))\n            for annotation_path in annotation_paths\n        ]\n\n        classes = []\n        for annotation in raw_annotations:\n            classes.extend(annotation[2])\n        classes = list(set(classes))\n\n        for annotation in raw_annotations:\n            class_id = [classes.index(class_name) for class_name in annotation[2]]\n            annotation[1].class_id = np.array(class_id)\n\n        images = {\n            image_path.name: cv2.imread(str(image_path)) for image_path in image_paths\n        }\n\n        annotations = {\n            image_name: detections for image_name, detections, _ in raw_annotations\n        }\n        return DetectionDataset(classes=classes, images=images, annotations=annotations)\n\n    @classmethod\n    def from_yolo(\n        cls,\n        images_directory_path: str,\n        annotations_directory_path: str,\n        data_yaml_path: str,\n        force_masks: bool = False,\n    ) -> DetectionDataset:\n\"\"\"\n        Creates a Dataset instance from YOLO formatted data.\n\n        Args:\n            images_directory_path (str): The path to the directory containing the images.\n            annotations_directory_path (str): The path to the directory containing the YOLO annotation files.\n            data_yaml_path (str): The path to the data YAML file containing class information.\n            force_masks (bool, optional): If True, forces masks to be loaded for all annotations, regardless of whether they are present.\n\n        Returns:\n            DetectionDataset: A DetectionDataset instance containing the loaded images and annotations.\n\n        Example:\n            ```python\n            >>> import roboflow\n            >>> from roboflow import Roboflow\n            >>> import supervision as sv\n\n            >>> roboflow.login()\n\n            >>> rf = Roboflow()\n\n            >>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)\n            >>> dataset = project.version(PROJECT_VERSION).download(\"yolov5\")\n\n            >>> ds = sv.DetectionDataset.from_yolo(\n            ...     images_directory_path=f\"{dataset.location}/train/images\",\n            ...     annotations_directory_path=f\"{dataset.location}/train/labels\",\n            ...     data_yaml_path=f\"{dataset.location}/data.yaml\"\n            ... )\n\n            >>> ds.classes\n            ['dog', 'person']\n            ```\n        \"\"\"\n        classes, images, annotations = load_yolo_annotations(\n            images_directory_path=images_directory_path,\n            annotations_directory_path=annotations_directory_path,\n            data_yaml_path=data_yaml_path,\n            force_masks=force_masks,\n        )\n        return DetectionDataset(classes=classes, images=images, annotations=annotations)\n\n    def as_yolo(\n        self,\n        images_directory_path: Optional[str] = None,\n        annotations_directory_path: Optional[str] = None,\n        data_yaml_path: Optional[str] = None,\n        min_image_area_percentage: float = 0.0,\n        max_image_area_percentage: float = 1.0,\n        approximation_percentage: float = 0.0,\n    ) -> None:\n\"\"\"\n        Exports the dataset to YOLO format. This method saves the images and their corresponding\n        annotations in YOLO format, which is a simple text file that describes an object in the image. It also allows\n        for the optional saving of a data.yaml file, used in YOLOv5, that contains metadata about the dataset.\n\n        The method allows filtering the detections based on their area percentage and offers an option for polygon approximation.\n\n        Args:\n            images_directory_path (Optional[str]): The path to the directory where the images should be saved.\n                If not provided, images will not be saved.\n            annotations_directory_path (Optional[str]): The path to the directory where the annotations in\n                YOLO format should be saved. If not provided, annotations will not be saved.\n            data_yaml_path (Optional[str]): The path where the data.yaml file should be saved.\n                If not provided, the file will not be saved.\n            min_image_area_percentage (float): The minimum percentage of detection area relative to\n                the image area for a detection to be included.\n            max_image_area_percentage (float): The maximum percentage of detection area relative to\n                the image area for a detection to be included.\n            approximation_percentage (float): The percentage of polygon points to be removed from the input polygon,\n                in the range [0, 1). This is useful for simplifying the annotations.\n        \"\"\"\n        if images_directory_path is not None:\n            save_dataset_images(\n                images_directory_path=images_directory_path, images=self.images\n            )\n        if annotations_directory_path is not None:\n            save_yolo_annotations(\n                annotations_directory_path=annotations_directory_path,\n                images=self.images,\n                annotations=self.annotations,\n                min_image_area_percentage=min_image_area_percentage,\n                max_image_area_percentage=max_image_area_percentage,\n                approximation_percentage=approximation_percentage,\n            )\n        if data_yaml_path is not None:\n            save_data_yaml(data_yaml_path=data_yaml_path, classes=self.classes)\n\n    @classmethod\n    def from_coco(\n        cls,\n        images_directory_path: str,\n        annotations_path: str,\n        force_masks: bool = False,\n    ) -> DetectionDataset:\n\"\"\"\n        Creates a Dataset instance from YOLO formatted data.\n\n        Args:\n            images_directory_path (str): The path to the directory containing the images.\n            annotations_path (str): The path to the json annotation files.\n            force_masks (bool, optional): If True, forces masks to be loaded for all annotations, regardless of whether they are present.\n\n        Returns:\n            DetectionDataset: A DetectionDataset instance containing the loaded images and annotations.\n\n        Example:\n            ```python\n            >>> import roboflow\n            >>> from roboflow import Roboflow\n            >>> import supervision as sv\n\n            >>> roboflow.login()\n\n            >>> rf = Roboflow()\n\n            >>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)\n            >>> dataset = project.version(PROJECT_VERSION).download(\"coco\")\n\n            >>> ds = sv.DetectionDataset.from_coco(\n            ...     images_directory_path=f\"{dataset.location}/train\",\n            ...     annotations_path=f\"{dataset.location}/train/_annotations.coco.json\",\n            ... )\n\n            >>> ds.classes\n            ['dog', 'person']\n            ```\n        \"\"\"\n        classes, images, annotations = load_coco_annotations(\n            images_directory_path=images_directory_path,\n            annotations_path=annotations_path,\n            force_masks=force_masks,\n        )\n        return DetectionDataset(classes=classes, images=images, annotations=annotations)\n\n    def as_coco(\n        self,\n        images_directory_path: Optional[str] = None,\n        annotations_path: Optional[str] = None,\n        min_image_area_percentage: float = 0.0,\n        max_image_area_percentage: float = 1.0,\n        approximation_percentage: float = 0.0,\n        licenses: Optional[list] = None,\n        info: Optional[dict] = None,\n    ) -> None:\n\"\"\"\n        Exports the dataset to COCO format. This method saves the images and their corresponding\n        annotations in COCO format, which is a simple json file that describes an object in the image.\n        Annotation json file also include category maps.\n\n        The method allows filtering the detections based on their area percentage and offers an option for polygon approximation.\n\n        Args:\n            images_directory_path (Optional[str]): The path to the directory where the images should be saved.\n                If not provided, images will not be saved.\n            annotations_directory_path (Optional[str]): The path to the directory where the annotations in\n                YOLO format should be saved. If not provided, annotations will not be saved.\n            min_image_area_percentage (float): The minimum percentage of detection area relative to\n                the image area for a detection to be included.\n            max_image_area_percentage (float): The maximum percentage of detection area relative to\n                the image area for a detection to be included.\n            approximation_percentage (float): The percentage of polygon points to be removed from the input polygon,\n                in the range [0, 1). This is useful for simplifying the annotations.\n            licenses (Optional[str]): List of licenses for images\n            info (Optional[dict]): Information of Dataset as dictionary\n        \"\"\"\n        if images_directory_path is not None:\n            save_dataset_images(\n                images_directory_path=images_directory_path, images=self.images\n            )\n        if annotations_path is not None:\n            save_coco_annotations(\n                annotation_path=annotations_path,\n                images=self.images,\n                annotations=self.annotations,\n                classes=self.classes,\n                min_image_area_percentage=min_image_area_percentage,\n                max_image_area_percentage=max_image_area_percentage,\n                approximation_percentage=approximation_percentage,\n                licenses=licenses,\n                info=info,\n            )\n
"},{"location":"dataset/core/#supervision.dataset.core.DetectionDataset.__iter__","title":"__iter__()","text":"

Iterate over the images and annotations in the dataset.

Yields:

Type Description str

Iterator[Tuple[str, np.ndarray, Detections]]: An iterator that yields tuples containing the image name, the image data, and its corresponding annotation.

Source code in supervision/dataset/core.py
def __iter__(self) -> Iterator[Tuple[str, np.ndarray, Detections]]:\n\"\"\"\n    Iterate over the images and annotations in the dataset.\n\n    Yields:\n        Iterator[Tuple[str, np.ndarray, Detections]]: An iterator that yields tuples containing the image name,\n                                                      the image data, and its corresponding annotation.\n    \"\"\"\n    for image_name, image in self.images.items():\n        yield image_name, image, self.annotations.get(image_name, None)\n
"},{"location":"dataset/core/#supervision.dataset.core.DetectionDataset.__len__","title":"__len__()","text":"

Return the number of images in the dataset.

Returns:

Name Type Description int int

The number of images.

Source code in supervision/dataset/core.py
def __len__(self) -> int:\n\"\"\"\n    Return the number of images in the dataset.\n\n    Returns:\n        int: The number of images.\n    \"\"\"\n    return len(self.images)\n
"},{"location":"dataset/core/#supervision.dataset.core.DetectionDataset.as_coco","title":"as_coco(images_directory_path=None, annotations_path=None, min_image_area_percentage=0.0, max_image_area_percentage=1.0, approximation_percentage=0.0, licenses=None, info=None)","text":"

Exports the dataset to COCO format. This method saves the images and their corresponding annotations in COCO format, which is a simple json file that describes an object in the image. Annotation json file also include category maps.

The method allows filtering the detections based on their area percentage and offers an option for polygon approximation.

Parameters:

Name Type Description Default images_directory_path Optional[str]

The path to the directory where the images should be saved. If not provided, images will not be saved.

None annotations_directory_path Optional[str]

The path to the directory where the annotations in YOLO format should be saved. If not provided, annotations will not be saved.

required min_image_area_percentage float

The minimum percentage of detection area relative to the image area for a detection to be included.

0.0 max_image_area_percentage float

The maximum percentage of detection area relative to the image area for a detection to be included.

1.0 approximation_percentage float

The percentage of polygon points to be removed from the input polygon, in the range [0, 1). This is useful for simplifying the annotations.

0.0 licenses Optional[str]

List of licenses for images

None info Optional[dict]

Information of Dataset as dictionary

None Source code in supervision/dataset/core.py
def as_coco(\n    self,\n    images_directory_path: Optional[str] = None,\n    annotations_path: Optional[str] = None,\n    min_image_area_percentage: float = 0.0,\n    max_image_area_percentage: float = 1.0,\n    approximation_percentage: float = 0.0,\n    licenses: Optional[list] = None,\n    info: Optional[dict] = None,\n) -> None:\n\"\"\"\n    Exports the dataset to COCO format. This method saves the images and their corresponding\n    annotations in COCO format, which is a simple json file that describes an object in the image.\n    Annotation json file also include category maps.\n\n    The method allows filtering the detections based on their area percentage and offers an option for polygon approximation.\n\n    Args:\n        images_directory_path (Optional[str]): The path to the directory where the images should be saved.\n            If not provided, images will not be saved.\n        annotations_directory_path (Optional[str]): The path to the directory where the annotations in\n            YOLO format should be saved. If not provided, annotations will not be saved.\n        min_image_area_percentage (float): The minimum percentage of detection area relative to\n            the image area for a detection to be included.\n        max_image_area_percentage (float): The maximum percentage of detection area relative to\n            the image area for a detection to be included.\n        approximation_percentage (float): The percentage of polygon points to be removed from the input polygon,\n            in the range [0, 1). This is useful for simplifying the annotations.\n        licenses (Optional[str]): List of licenses for images\n        info (Optional[dict]): Information of Dataset as dictionary\n    \"\"\"\n    if images_directory_path is not None:\n        save_dataset_images(\n            images_directory_path=images_directory_path, images=self.images\n        )\n    if annotations_path is not None:\n        save_coco_annotations(\n            annotation_path=annotations_path,\n            images=self.images,\n            annotations=self.annotations,\n            classes=self.classes,\n            min_image_area_percentage=min_image_area_percentage,\n            max_image_area_percentage=max_image_area_percentage,\n            approximation_percentage=approximation_percentage,\n            licenses=licenses,\n            info=info,\n        )\n
"},{"location":"dataset/core/#supervision.dataset.core.DetectionDataset.as_pascal_voc","title":"as_pascal_voc(images_directory_path=None, annotations_directory_path=None, min_image_area_percentage=0.0, max_image_area_percentage=1.0, approximation_percentage=0.0)","text":"

Exports the dataset to PASCAL VOC format. This method saves the images and their corresponding annotations in PASCAL VOC format, which consists of XML files. The method allows filtering the detections based on their area percentage.

Parameters:

Name Type Description Default images_directory_path Optional[str]

The path to the directory where the images should be saved. If not provided, images will not be saved.

None annotations_directory_path Optional[str]

The path to the directory where the annotations in PASCAL VOC format should be saved. If not provided, annotations will not be saved.

None min_image_area_percentage float

The minimum percentage of detection area relative to the image area for a detection to be included.

0.0 max_image_area_percentage float

The maximum percentage of detection area relative to the image area for a detection to be included.

1.0 approximation_percentage float

The percentage of polygon points to be removed from the input polygon, in the range [0, 1).

0.0 Source code in supervision/dataset/core.py
def as_pascal_voc(\n    self,\n    images_directory_path: Optional[str] = None,\n    annotations_directory_path: Optional[str] = None,\n    min_image_area_percentage: float = 0.0,\n    max_image_area_percentage: float = 1.0,\n    approximation_percentage: float = 0.0,\n) -> None:\n\"\"\"\n    Exports the dataset to PASCAL VOC format. This method saves the images and their corresponding annotations in\n    PASCAL VOC format, which consists of XML files. The method allows filtering the detections based on their area\n    percentage.\n\n    Args:\n        images_directory_path (Optional[str]): The path to the directory where the images should be saved.\n            If not provided, images will not be saved.\n        annotations_directory_path (Optional[str]): The path to the directory where the annotations in\n            PASCAL VOC format should be saved. If not provided, annotations will not be saved.\n        min_image_area_percentage (float): The minimum percentage of detection area relative to\n            the image area for a detection to be included.\n        max_image_area_percentage (float): The maximum percentage of detection area relative to\n            the image area for a detection to be included.\n        approximation_percentage (float): The percentage of polygon points to be removed from the input polygon, in the range [0, 1).\n    \"\"\"\n    if images_directory_path:\n        images_path = Path(images_directory_path)\n        images_path.mkdir(parents=True, exist_ok=True)\n\n    if annotations_directory_path:\n        annotations_path = Path(annotations_directory_path)\n        annotations_path.mkdir(parents=True, exist_ok=True)\n\n    for image_name, image in self.images.items():\n        detections = self.annotations[image_name]\n\n        if images_directory_path:\n            cv2.imwrite(str(images_path / image_name), image)\n\n        if annotations_directory_path:\n            annotation_name = Path(image_name).stem\n            pascal_voc_xml = detections_to_pascal_voc(\n                detections=detections,\n                classes=self.classes,\n                filename=image_name,\n                image_shape=image.shape,\n                min_image_area_percentage=min_image_area_percentage,\n                max_image_area_percentage=max_image_area_percentage,\n                approximation_percentage=approximation_percentage,\n            )\n\n            with open(annotations_path / f\"{annotation_name}.xml\", \"w\") as f:\n                f.write(pascal_voc_xml)\n
"},{"location":"dataset/core/#supervision.dataset.core.DetectionDataset.as_yolo","title":"as_yolo(images_directory_path=None, annotations_directory_path=None, data_yaml_path=None, min_image_area_percentage=0.0, max_image_area_percentage=1.0, approximation_percentage=0.0)","text":"

Exports the dataset to YOLO format. This method saves the images and their corresponding annotations in YOLO format, which is a simple text file that describes an object in the image. It also allows for the optional saving of a data.yaml file, used in YOLOv5, that contains metadata about the dataset.

The method allows filtering the detections based on their area percentage and offers an option for polygon approximation.

Parameters:

Name Type Description Default images_directory_path Optional[str]

The path to the directory where the images should be saved. If not provided, images will not be saved.

None annotations_directory_path Optional[str]

The path to the directory where the annotations in YOLO format should be saved. If not provided, annotations will not be saved.

None data_yaml_path Optional[str]

The path where the data.yaml file should be saved. If not provided, the file will not be saved.

None min_image_area_percentage float

The minimum percentage of detection area relative to the image area for a detection to be included.

0.0 max_image_area_percentage float

The maximum percentage of detection area relative to the image area for a detection to be included.

1.0 approximation_percentage float

The percentage of polygon points to be removed from the input polygon, in the range [0, 1). This is useful for simplifying the annotations.

0.0 Source code in supervision/dataset/core.py
def as_yolo(\n    self,\n    images_directory_path: Optional[str] = None,\n    annotations_directory_path: Optional[str] = None,\n    data_yaml_path: Optional[str] = None,\n    min_image_area_percentage: float = 0.0,\n    max_image_area_percentage: float = 1.0,\n    approximation_percentage: float = 0.0,\n) -> None:\n\"\"\"\n    Exports the dataset to YOLO format. This method saves the images and their corresponding\n    annotations in YOLO format, which is a simple text file that describes an object in the image. It also allows\n    for the optional saving of a data.yaml file, used in YOLOv5, that contains metadata about the dataset.\n\n    The method allows filtering the detections based on their area percentage and offers an option for polygon approximation.\n\n    Args:\n        images_directory_path (Optional[str]): The path to the directory where the images should be saved.\n            If not provided, images will not be saved.\n        annotations_directory_path (Optional[str]): The path to the directory where the annotations in\n            YOLO format should be saved. If not provided, annotations will not be saved.\n        data_yaml_path (Optional[str]): The path where the data.yaml file should be saved.\n            If not provided, the file will not be saved.\n        min_image_area_percentage (float): The minimum percentage of detection area relative to\n            the image area for a detection to be included.\n        max_image_area_percentage (float): The maximum percentage of detection area relative to\n            the image area for a detection to be included.\n        approximation_percentage (float): The percentage of polygon points to be removed from the input polygon,\n            in the range [0, 1). This is useful for simplifying the annotations.\n    \"\"\"\n    if images_directory_path is not None:\n        save_dataset_images(\n            images_directory_path=images_directory_path, images=self.images\n        )\n    if annotations_directory_path is not None:\n        save_yolo_annotations(\n            annotations_directory_path=annotations_directory_path,\n            images=self.images,\n            annotations=self.annotations,\n            min_image_area_percentage=min_image_area_percentage,\n            max_image_area_percentage=max_image_area_percentage,\n            approximation_percentage=approximation_percentage,\n        )\n    if data_yaml_path is not None:\n        save_data_yaml(data_yaml_path=data_yaml_path, classes=self.classes)\n
"},{"location":"dataset/core/#supervision.dataset.core.DetectionDataset.from_coco","title":"from_coco(images_directory_path, annotations_path, force_masks=False) classmethod","text":"

Creates a Dataset instance from YOLO formatted data.

Parameters:

Name Type Description Default images_directory_path str

The path to the directory containing the images.

required annotations_path str

The path to the json annotation files.

required force_masks bool

If True, forces masks to be loaded for all annotations, regardless of whether they are present.

False

Returns:

Name Type Description DetectionDataset DetectionDataset

A DetectionDataset instance containing the loaded images and annotations.

Example
>>> import roboflow\n>>> from roboflow import Roboflow\n>>> import supervision as sv\n\n>>> roboflow.login()\n\n>>> rf = Roboflow()\n\n>>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)\n>>> dataset = project.version(PROJECT_VERSION).download(\"coco\")\n\n>>> ds = sv.DetectionDataset.from_coco(\n...     images_directory_path=f\"{dataset.location}/train\",\n...     annotations_path=f\"{dataset.location}/train/_annotations.coco.json\",\n... )\n\n>>> ds.classes\n['dog', 'person']\n
Source code in supervision/dataset/core.py
@classmethod\ndef from_coco(\n    cls,\n    images_directory_path: str,\n    annotations_path: str,\n    force_masks: bool = False,\n) -> DetectionDataset:\n\"\"\"\n    Creates a Dataset instance from YOLO formatted data.\n\n    Args:\n        images_directory_path (str): The path to the directory containing the images.\n        annotations_path (str): The path to the json annotation files.\n        force_masks (bool, optional): If True, forces masks to be loaded for all annotations, regardless of whether they are present.\n\n    Returns:\n        DetectionDataset: A DetectionDataset instance containing the loaded images and annotations.\n\n    Example:\n        ```python\n        >>> import roboflow\n        >>> from roboflow import Roboflow\n        >>> import supervision as sv\n\n        >>> roboflow.login()\n\n        >>> rf = Roboflow()\n\n        >>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)\n        >>> dataset = project.version(PROJECT_VERSION).download(\"coco\")\n\n        >>> ds = sv.DetectionDataset.from_coco(\n        ...     images_directory_path=f\"{dataset.location}/train\",\n        ...     annotations_path=f\"{dataset.location}/train/_annotations.coco.json\",\n        ... )\n\n        >>> ds.classes\n        ['dog', 'person']\n        ```\n    \"\"\"\n    classes, images, annotations = load_coco_annotations(\n        images_directory_path=images_directory_path,\n        annotations_path=annotations_path,\n        force_masks=force_masks,\n    )\n    return DetectionDataset(classes=classes, images=images, annotations=annotations)\n
"},{"location":"dataset/core/#supervision.dataset.core.DetectionDataset.from_pascal_voc","title":"from_pascal_voc(images_directory_path, annotations_directory_path) classmethod","text":"

Creates a Dataset instance from PASCAL VOC formatted data.

Parameters:

Name Type Description Default images_directory_path str

The path to the directory containing the images.

required annotations_directory_path str

The path to the directory containing the PASCAL VOC XML annotations.

required

Returns:

Name Type Description DetectionDataset DetectionDataset

A DetectionDataset instance containing the loaded images and annotations.

Example
>>> import roboflow\n>>> from roboflow import Roboflow\n>>> import supervision as sv\n\n>>> roboflow.login()\n\n>>> rf = Roboflow()\n\n>>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)\n>>> dataset = project.version(PROJECT_VERSION).download(\"voc\")\n\n>>> ds = sv.DetectionDataset.from_yolo(\n...     images_directory_path=f\"{dataset.location}/train/images\",\n...     annotations_directory_path=f\"{dataset.location}/train/labels\"\n... )\n\n>>> ds.classes\n['dog', 'person']\n
Source code in supervision/dataset/core.py
@classmethod\ndef from_pascal_voc(\n    cls, images_directory_path: str, annotations_directory_path: str\n) -> DetectionDataset:\n\"\"\"\n    Creates a Dataset instance from PASCAL VOC formatted data.\n\n    Args:\n        images_directory_path (str): The path to the directory containing the images.\n        annotations_directory_path (str): The path to the directory containing the PASCAL VOC XML annotations.\n\n    Returns:\n        DetectionDataset: A DetectionDataset instance containing the loaded images and annotations.\n\n    Example:\n        ```python\n        >>> import roboflow\n        >>> from roboflow import Roboflow\n        >>> import supervision as sv\n\n        >>> roboflow.login()\n\n        >>> rf = Roboflow()\n\n        >>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)\n        >>> dataset = project.version(PROJECT_VERSION).download(\"voc\")\n\n        >>> ds = sv.DetectionDataset.from_yolo(\n        ...     images_directory_path=f\"{dataset.location}/train/images\",\n        ...     annotations_directory_path=f\"{dataset.location}/train/labels\"\n        ... )\n\n        >>> ds.classes\n        ['dog', 'person']\n        ```\n    \"\"\"\n    image_paths = list_files_with_extensions(\n        directory=images_directory_path, extensions=[\"jpg\", \"jpeg\", \"png\"]\n    )\n    annotation_paths = list_files_with_extensions(\n        directory=annotations_directory_path, extensions=[\"xml\"]\n    )\n\n    raw_annotations: List[Tuple[str, Detections, List[str]]] = [\n        load_pascal_voc_annotations(annotation_path=str(annotation_path))\n        for annotation_path in annotation_paths\n    ]\n\n    classes = []\n    for annotation in raw_annotations:\n        classes.extend(annotation[2])\n    classes = list(set(classes))\n\n    for annotation in raw_annotations:\n        class_id = [classes.index(class_name) for class_name in annotation[2]]\n        annotation[1].class_id = np.array(class_id)\n\n    images = {\n        image_path.name: cv2.imread(str(image_path)) for image_path in image_paths\n    }\n\n    annotations = {\n        image_name: detections for image_name, detections, _ in raw_annotations\n    }\n    return DetectionDataset(classes=classes, images=images, annotations=annotations)\n
"},{"location":"dataset/core/#supervision.dataset.core.DetectionDataset.from_yolo","title":"from_yolo(images_directory_path, annotations_directory_path, data_yaml_path, force_masks=False) classmethod","text":"

Creates a Dataset instance from YOLO formatted data.

Parameters:

Name Type Description Default images_directory_path str

The path to the directory containing the images.

required annotations_directory_path str

The path to the directory containing the YOLO annotation files.

required data_yaml_path str

The path to the data YAML file containing class information.

required force_masks bool

If True, forces masks to be loaded for all annotations, regardless of whether they are present.

False

Returns:

Name Type Description DetectionDataset DetectionDataset

A DetectionDataset instance containing the loaded images and annotations.

Example
>>> import roboflow\n>>> from roboflow import Roboflow\n>>> import supervision as sv\n\n>>> roboflow.login()\n\n>>> rf = Roboflow()\n\n>>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)\n>>> dataset = project.version(PROJECT_VERSION).download(\"yolov5\")\n\n>>> ds = sv.DetectionDataset.from_yolo(\n...     images_directory_path=f\"{dataset.location}/train/images\",\n...     annotations_directory_path=f\"{dataset.location}/train/labels\",\n...     data_yaml_path=f\"{dataset.location}/data.yaml\"\n... )\n\n>>> ds.classes\n['dog', 'person']\n
Source code in supervision/dataset/core.py
@classmethod\ndef from_yolo(\n    cls,\n    images_directory_path: str,\n    annotations_directory_path: str,\n    data_yaml_path: str,\n    force_masks: bool = False,\n) -> DetectionDataset:\n\"\"\"\n    Creates a Dataset instance from YOLO formatted data.\n\n    Args:\n        images_directory_path (str): The path to the directory containing the images.\n        annotations_directory_path (str): The path to the directory containing the YOLO annotation files.\n        data_yaml_path (str): The path to the data YAML file containing class information.\n        force_masks (bool, optional): If True, forces masks to be loaded for all annotations, regardless of whether they are present.\n\n    Returns:\n        DetectionDataset: A DetectionDataset instance containing the loaded images and annotations.\n\n    Example:\n        ```python\n        >>> import roboflow\n        >>> from roboflow import Roboflow\n        >>> import supervision as sv\n\n        >>> roboflow.login()\n\n        >>> rf = Roboflow()\n\n        >>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)\n        >>> dataset = project.version(PROJECT_VERSION).download(\"yolov5\")\n\n        >>> ds = sv.DetectionDataset.from_yolo(\n        ...     images_directory_path=f\"{dataset.location}/train/images\",\n        ...     annotations_directory_path=f\"{dataset.location}/train/labels\",\n        ...     data_yaml_path=f\"{dataset.location}/data.yaml\"\n        ... )\n\n        >>> ds.classes\n        ['dog', 'person']\n        ```\n    \"\"\"\n    classes, images, annotations = load_yolo_annotations(\n        images_directory_path=images_directory_path,\n        annotations_directory_path=annotations_directory_path,\n        data_yaml_path=data_yaml_path,\n        force_masks=force_masks,\n    )\n    return DetectionDataset(classes=classes, images=images, annotations=annotations)\n
"},{"location":"dataset/core/#supervision.dataset.core.DetectionDataset.split","title":"split(split_ratio=0.8, random_state=None, shuffle=True)","text":"

Splits the dataset into two parts (training and testing) using the provided split_ratio.

Parameters:

Name Type Description Default split_ratio float

The ratio of the training set to the entire dataset.

0.8 random_state int

The seed for the random number generator. This is used for reproducibility.

None shuffle bool

Whether to shuffle the data before splitting.

True

Returns:

Type Description Tuple[DetectionDataset, DetectionDataset]

Tuple[DetectionDataset, DetectionDataset]: A tuple containing the training and testing datasets.

Example
>>> import supervision as sv\n\n>>> ds = sv.DetectionDataset(...)\n>>> train_ds, test_ds = ds.split(split_ratio=0.7, random_state=42, shuffle=True)\n>>> len(train_ds), len(test_ds)\n(700, 300)\n
Source code in supervision/dataset/core.py
def split(\n    self, split_ratio=0.8, random_state=None, shuffle: bool = True\n) -> Tuple[DetectionDataset, DetectionDataset]:\n\"\"\"\n    Splits the dataset into two parts (training and testing) using the provided split_ratio.\n\n    Args:\n        split_ratio (float, optional): The ratio of the training set to the entire dataset.\n        random_state (int, optional): The seed for the random number generator. This is used for reproducibility.\n        shuffle (bool, optional): Whether to shuffle the data before splitting.\n\n    Returns:\n        Tuple[DetectionDataset, DetectionDataset]: A tuple containing the training and testing datasets.\n\n    Example:\n        ```python\n        >>> import supervision as sv\n\n        >>> ds = sv.DetectionDataset(...)\n        >>> train_ds, test_ds = ds.split(split_ratio=0.7, random_state=42, shuffle=True)\n        >>> len(train_ds), len(test_ds)\n        (700, 300)\n        ```\n    \"\"\"\n\n    image_names = list(self.images.keys())\n    train_names, test_names = train_test_split(\n        data=image_names,\n        train_ratio=split_ratio,\n        random_state=random_state,\n        shuffle=shuffle,\n    )\n\n    train_dataset = DetectionDataset(\n        classes=self.classes,\n        images={name: self.images[name] for name in train_names},\n        annotations={name: self.annotations[name] for name in train_names},\n    )\n    test_dataset = DetectionDataset(\n        classes=self.classes,\n        images={name: self.images[name] for name in test_names},\n        annotations={name: self.annotations[name] for name in test_names},\n    )\n    return train_dataset, test_dataset\n
"},{"location":"dataset/core/#classificationdataset","title":"ClassificationDataset","text":"

Bases: BaseDataset

Dataclass containing information about a classification dataset.

Attributes:

Name Type Description classes List[str]

List containing dataset class names.

images Dict[str, np.ndarray]

Dictionary mapping image name to image.

annotations Dict[str, Detections]

Dictionary mapping image name to annotations.

Source code in supervision/dataset/core.py
@dataclass\nclass ClassificationDataset(BaseDataset):\n\"\"\"\n    Dataclass containing information about a classification dataset.\n\n    Attributes:\n        classes (List[str]): List containing dataset class names.\n        images (Dict[str, np.ndarray]): Dictionary mapping image name to image.\n        annotations (Dict[str, Detections]): Dictionary mapping image name to annotations.\n    \"\"\"\n\n    classes: List[str]\n    images: Dict[str, np.ndarray]\n    annotations: Dict[str, Classifications]\n\n    def __len__(self) -> int:\n        return len(self.images)\n\n    def split(\n        self, split_ratio=0.8, random_state=None, shuffle: bool = True\n    ) -> Tuple[ClassificationDataset, ClassificationDataset]:\n\"\"\"\n        Splits the dataset into two parts (training and testing) using the provided split_ratio.\n\n        Args:\n            split_ratio (float, optional): The ratio of the training set to the entire dataset.\n            random_state (int, optional): The seed for the random number generator. This is used for reproducibility.\n            shuffle (bool, optional): Whether to shuffle the data before splitting.\n\n        Returns:\n            Tuple[ClassificationDataset, ClassificationDataset]: A tuple containing the training and testing datasets.\n\n        Example:\n            ```python\n            >>> import supervision as sv\n\n            >>> cd = sv.ClassificationDataset(...)\n            >>> train_cd, test_cd = cd.split(split_ratio=0.7, random_state=42, shuffle=True)\n            >>> len(train_cd), len(test_cd)\n            (700, 300)\n            ```\n        \"\"\"\n        image_names = list(self.images.keys())\n        train_names, test_names = train_test_split(\n            data=image_names,\n            train_ratio=split_ratio,\n            random_state=random_state,\n            shuffle=shuffle,\n        )\n\n        train_dataset = ClassificationDataset(\n            classes=self.classes,\n            images={name: self.images[name] for name in train_names},\n            annotations={name: self.annotations[name] for name in train_names},\n        )\n        test_dataset = ClassificationDataset(\n            classes=self.classes,\n            images={name: self.images[name] for name in test_names},\n            annotations={name: self.annotations[name] for name in test_names},\n        )\n        return train_dataset, test_dataset\n\n    def as_folder_structure(self, root_directory_path: str) -> None:\n\"\"\"\n        Saves the dataset as a multi-class folder structure.\n\n        Args:\n            root_directory_path (str): The path to the directory where the dataset will be saved.\n        \"\"\"\n        os.makedirs(root_directory_path, exist_ok=True)\n\n        for class_name in self.classes:\n            os.makedirs(os.path.join(root_directory_path, class_name), exist_ok=True)\n\n        for image_name in self.images:\n            classification = self.annotations[image_name]\n            image = self.images[image_name]\n            class_id = (\n                classification.class_id[0]\n                if classification.confidence is None\n                else classification.get_top_k(1)[0]\n            )\n            class_name = self.classes[class_id]\n            image_path = os.path.join(root_directory_path, class_name, image_name)\n            cv2.imwrite(image_path, image)\n\n    @classmethod\n    def from_folder_structure(cls, root_directory_path: str) -> ClassificationDataset:\n\"\"\"\n        Load data from a multiclass folder structure into a ClassificationDataset.\n\n        Args:\n            root_directory_path (str): The path to the dataset directory.\n\n        Returns:\n            ClassificationDataset: The dataset.\n\n        Example:\n            ```python\n            >>> import roboflow\n            >>> from roboflow import Roboflow\n            >>> import supervision as sv\n\n            >>> roboflow.login()\n\n            >>> rf = Roboflow()\n\n            >>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)\n            >>> dataset = project.version(PROJECT_VERSION).download(\"folder\")\n\n            >>> cd = sv.ClassificationDataset.from_folder_structure(\n            ...     root_directory_path=f\"{dataset.location}/train\"\n            ... )\n            ```\n        \"\"\"\n        classes = os.listdir(root_directory_path)\n        classes = sorted(set(classes))\n\n        images = {}\n        annotations = {}\n\n        for class_name in classes:\n            class_id = classes.index(class_name)\n\n            for image in os.listdir(os.path.join(root_directory_path, class_name)):\n                image_dir = os.path.join(root_directory_path, class_name, image)\n                images[image] = cv2.imread(image_dir)\n                annotations[image] = Classifications(\n                    class_id=np.array([class_id]),\n                )\n\n        return cls(\n            classes=classes,\n            images=images,\n            annotations=annotations,\n        )\n
"},{"location":"dataset/core/#supervision.dataset.core.ClassificationDataset.as_folder_structure","title":"as_folder_structure(root_directory_path)","text":"

Saves the dataset as a multi-class folder structure.

Parameters:

Name Type Description Default root_directory_path str

The path to the directory where the dataset will be saved.

required Source code in supervision/dataset/core.py
def as_folder_structure(self, root_directory_path: str) -> None:\n\"\"\"\n    Saves the dataset as a multi-class folder structure.\n\n    Args:\n        root_directory_path (str): The path to the directory where the dataset will be saved.\n    \"\"\"\n    os.makedirs(root_directory_path, exist_ok=True)\n\n    for class_name in self.classes:\n        os.makedirs(os.path.join(root_directory_path, class_name), exist_ok=True)\n\n    for image_name in self.images:\n        classification = self.annotations[image_name]\n        image = self.images[image_name]\n        class_id = (\n            classification.class_id[0]\n            if classification.confidence is None\n            else classification.get_top_k(1)[0]\n        )\n        class_name = self.classes[class_id]\n        image_path = os.path.join(root_directory_path, class_name, image_name)\n        cv2.imwrite(image_path, image)\n
"},{"location":"dataset/core/#supervision.dataset.core.ClassificationDataset.from_folder_structure","title":"from_folder_structure(root_directory_path) classmethod","text":"

Load data from a multiclass folder structure into a ClassificationDataset.

Parameters:

Name Type Description Default root_directory_path str

The path to the dataset directory.

required

Returns:

Name Type Description ClassificationDataset ClassificationDataset

The dataset.

Example
>>> import roboflow\n>>> from roboflow import Roboflow\n>>> import supervision as sv\n\n>>> roboflow.login()\n\n>>> rf = Roboflow()\n\n>>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)\n>>> dataset = project.version(PROJECT_VERSION).download(\"folder\")\n\n>>> cd = sv.ClassificationDataset.from_folder_structure(\n...     root_directory_path=f\"{dataset.location}/train\"\n... )\n
Source code in supervision/dataset/core.py
@classmethod\ndef from_folder_structure(cls, root_directory_path: str) -> ClassificationDataset:\n\"\"\"\n    Load data from a multiclass folder structure into a ClassificationDataset.\n\n    Args:\n        root_directory_path (str): The path to the dataset directory.\n\n    Returns:\n        ClassificationDataset: The dataset.\n\n    Example:\n        ```python\n        >>> import roboflow\n        >>> from roboflow import Roboflow\n        >>> import supervision as sv\n\n        >>> roboflow.login()\n\n        >>> rf = Roboflow()\n\n        >>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)\n        >>> dataset = project.version(PROJECT_VERSION).download(\"folder\")\n\n        >>> cd = sv.ClassificationDataset.from_folder_structure(\n        ...     root_directory_path=f\"{dataset.location}/train\"\n        ... )\n        ```\n    \"\"\"\n    classes = os.listdir(root_directory_path)\n    classes = sorted(set(classes))\n\n    images = {}\n    annotations = {}\n\n    for class_name in classes:\n        class_id = classes.index(class_name)\n\n        for image in os.listdir(os.path.join(root_directory_path, class_name)):\n            image_dir = os.path.join(root_directory_path, class_name, image)\n            images[image] = cv2.imread(image_dir)\n            annotations[image] = Classifications(\n                class_id=np.array([class_id]),\n            )\n\n    return cls(\n        classes=classes,\n        images=images,\n        annotations=annotations,\n    )\n
"},{"location":"dataset/core/#supervision.dataset.core.ClassificationDataset.split","title":"split(split_ratio=0.8, random_state=None, shuffle=True)","text":"

Splits the dataset into two parts (training and testing) using the provided split_ratio.

Parameters:

Name Type Description Default split_ratio float

The ratio of the training set to the entire dataset.

0.8 random_state int

The seed for the random number generator. This is used for reproducibility.

None shuffle bool

Whether to shuffle the data before splitting.

True

Returns:

Type Description Tuple[ClassificationDataset, ClassificationDataset]

Tuple[ClassificationDataset, ClassificationDataset]: A tuple containing the training and testing datasets.

Example
>>> import supervision as sv\n\n>>> cd = sv.ClassificationDataset(...)\n>>> train_cd, test_cd = cd.split(split_ratio=0.7, random_state=42, shuffle=True)\n>>> len(train_cd), len(test_cd)\n(700, 300)\n
Source code in supervision/dataset/core.py
def split(\n    self, split_ratio=0.8, random_state=None, shuffle: bool = True\n) -> Tuple[ClassificationDataset, ClassificationDataset]:\n\"\"\"\n    Splits the dataset into two parts (training and testing) using the provided split_ratio.\n\n    Args:\n        split_ratio (float, optional): The ratio of the training set to the entire dataset.\n        random_state (int, optional): The seed for the random number generator. This is used for reproducibility.\n        shuffle (bool, optional): Whether to shuffle the data before splitting.\n\n    Returns:\n        Tuple[ClassificationDataset, ClassificationDataset]: A tuple containing the training and testing datasets.\n\n    Example:\n        ```python\n        >>> import supervision as sv\n\n        >>> cd = sv.ClassificationDataset(...)\n        >>> train_cd, test_cd = cd.split(split_ratio=0.7, random_state=42, shuffle=True)\n        >>> len(train_cd), len(test_cd)\n        (700, 300)\n        ```\n    \"\"\"\n    image_names = list(self.images.keys())\n    train_names, test_names = train_test_split(\n        data=image_names,\n        train_ratio=split_ratio,\n        random_state=random_state,\n        shuffle=shuffle,\n    )\n\n    train_dataset = ClassificationDataset(\n        classes=self.classes,\n        images={name: self.images[name] for name in train_names},\n        annotations={name: self.annotations[name] for name in train_names},\n    )\n    test_dataset = ClassificationDataset(\n        classes=self.classes,\n        images={name: self.images[name] for name in test_names},\n        annotations={name: self.annotations[name] for name in test_names},\n    )\n    return train_dataset, test_dataset\n
"},{"location":"detection/annotate/","title":"Annotate","text":""},{"location":"detection/annotate/#boxannotator","title":"BoxAnnotator","text":"

A class for drawing bounding boxes on an image using detections provided.

Attributes:

Name Type Description color Union[Color, ColorPalette]

The color to draw the bounding box, can be a single color or a color palette

thickness int

The thickness of the bounding box lines, default is 2

text_color Color

The color of the text on the bounding box, default is white

text_scale float

The scale of the text on the bounding box, default is 0.5

text_thickness int

The thickness of the text on the bounding box, default is 1

text_padding int

The padding around the text on the bounding box, default is 5

Source code in supervision/detection/annotate.py
class BoxAnnotator:\n\"\"\"\n    A class for drawing bounding boxes on an image using detections provided.\n\n    Attributes:\n        color (Union[Color, ColorPalette]): The color to draw the bounding box, can be a single color or a color palette\n        thickness (int): The thickness of the bounding box lines, default is 2\n        text_color (Color): The color of the text on the bounding box, default is white\n        text_scale (float): The scale of the text on the bounding box, default is 0.5\n        text_thickness (int): The thickness of the text on the bounding box, default is 1\n        text_padding (int): The padding around the text on the bounding box, default is 5\n\n    \"\"\"\n\n    def __init__(\n        self,\n        color: Union[Color, ColorPalette] = ColorPalette.default(),\n        thickness: int = 2,\n        text_color: Color = Color.black(),\n        text_scale: float = 0.5,\n        text_thickness: int = 1,\n        text_padding: int = 10,\n    ):\n        self.color: Union[Color, ColorPalette] = color\n        self.thickness: int = thickness\n        self.text_color: Color = text_color\n        self.text_scale: float = text_scale\n        self.text_thickness: int = text_thickness\n        self.text_padding: int = text_padding\n\n    def annotate(\n        self,\n        scene: np.ndarray,\n        detections: Detections,\n        labels: Optional[List[str]] = None,\n        skip_label: bool = False,\n    ) -> np.ndarray:\n\"\"\"\n        Draws bounding boxes on the frame using the detections provided.\n\n        Args:\n            scene (np.ndarray): The image on which the bounding boxes will be drawn\n            detections (Detections): The detections for which the bounding boxes will be drawn\n            labels (Optional[List[str]]): An optional list of labels corresponding to each detection. If `labels` are not provided, corresponding `class_id` will be used as label.\n            skip_label (bool): Is set to `True`, skips bounding box label annotation.\n        Returns:\n            np.ndarray: The image with the bounding boxes drawn on it\n\n        Example:\n            ```python\n            >>> import supervision as sv\n\n            >>> classes = ['person', ...]\n            >>> image = ...\n            >>> detections = sv.Detections(...)\n\n            >>> box_annotator = sv.BoxAnnotator()\n            >>> labels = [\n            ...     f\"{classes[class_id]} {confidence:0.2f}\"\n            ...     for _, _, confidence, class_id, _\n            ...     in detections\n            ... ]\n            >>> annotated_frame = box_annotator.annotate(\n            ...     scene=image.copy(),\n            ...     detections=detections,\n            ...     labels=labels\n            ... )\n            ```\n        \"\"\"\n        font = cv2.FONT_HERSHEY_SIMPLEX\n        for i in range(len(detections)):\n            x1, y1, x2, y2 = detections.xyxy[i].astype(int)\n            class_id = (\n                detections.class_id[i] if detections.class_id is not None else None\n            )\n            idx = class_id if class_id is not None else i\n            color = (\n                self.color.by_idx(idx)\n                if isinstance(self.color, ColorPalette)\n                else self.color\n            )\n            cv2.rectangle(\n                img=scene,\n                pt1=(x1, y1),\n                pt2=(x2, y2),\n                color=color.as_bgr(),\n                thickness=self.thickness,\n            )\n            if skip_label:\n                continue\n\n            text = (\n                f\"{class_id}\"\n                if (labels is None or len(detections) != len(labels))\n                else labels[i]\n            )\n\n            text_width, text_height = cv2.getTextSize(\n                text=text,\n                fontFace=font,\n                fontScale=self.text_scale,\n                thickness=self.text_thickness,\n            )[0]\n\n            text_x = x1 + self.text_padding\n            text_y = y1 - self.text_padding\n\n            text_background_x1 = x1\n            text_background_y1 = y1 - 2 * self.text_padding - text_height\n\n            text_background_x2 = x1 + 2 * self.text_padding + text_width\n            text_background_y2 = y1\n\n            cv2.rectangle(\n                img=scene,\n                pt1=(text_background_x1, text_background_y1),\n                pt2=(text_background_x2, text_background_y2),\n                color=color.as_bgr(),\n                thickness=cv2.FILLED,\n            )\n            cv2.putText(\n                img=scene,\n                text=text,\n                org=(text_x, text_y),\n                fontFace=font,\n                fontScale=self.text_scale,\n                color=self.text_color.as_rgb(),\n                thickness=self.text_thickness,\n                lineType=cv2.LINE_AA,\n            )\n        return scene\n
"},{"location":"detection/annotate/#supervision.detection.annotate.BoxAnnotator.annotate","title":"annotate(scene, detections, labels=None, skip_label=False)","text":"

Draws bounding boxes on the frame using the detections provided.

Parameters:

Name Type Description Default scene np.ndarray

The image on which the bounding boxes will be drawn

required detections Detections

The detections for which the bounding boxes will be drawn

required labels Optional[List[str]]

An optional list of labels corresponding to each detection. If labels are not provided, corresponding class_id will be used as label.

None skip_label bool

Is set to True, skips bounding box label annotation.

False

Returns:

Type Description np.ndarray

np.ndarray: The image with the bounding boxes drawn on it

Example
>>> import supervision as sv\n\n>>> classes = ['person', ...]\n>>> image = ...\n>>> detections = sv.Detections(...)\n\n>>> box_annotator = sv.BoxAnnotator()\n>>> labels = [\n...     f\"{classes[class_id]} {confidence:0.2f}\"\n...     for _, _, confidence, class_id, _\n...     in detections\n... ]\n>>> annotated_frame = box_annotator.annotate(\n...     scene=image.copy(),\n...     detections=detections,\n...     labels=labels\n... )\n
Source code in supervision/detection/annotate.py
def annotate(\n    self,\n    scene: np.ndarray,\n    detections: Detections,\n    labels: Optional[List[str]] = None,\n    skip_label: bool = False,\n) -> np.ndarray:\n\"\"\"\n    Draws bounding boxes on the frame using the detections provided.\n\n    Args:\n        scene (np.ndarray): The image on which the bounding boxes will be drawn\n        detections (Detections): The detections for which the bounding boxes will be drawn\n        labels (Optional[List[str]]): An optional list of labels corresponding to each detection. If `labels` are not provided, corresponding `class_id` will be used as label.\n        skip_label (bool): Is set to `True`, skips bounding box label annotation.\n    Returns:\n        np.ndarray: The image with the bounding boxes drawn on it\n\n    Example:\n        ```python\n        >>> import supervision as sv\n\n        >>> classes = ['person', ...]\n        >>> image = ...\n        >>> detections = sv.Detections(...)\n\n        >>> box_annotator = sv.BoxAnnotator()\n        >>> labels = [\n        ...     f\"{classes[class_id]} {confidence:0.2f}\"\n        ...     for _, _, confidence, class_id, _\n        ...     in detections\n        ... ]\n        >>> annotated_frame = box_annotator.annotate(\n        ...     scene=image.copy(),\n        ...     detections=detections,\n        ...     labels=labels\n        ... )\n        ```\n    \"\"\"\n    font = cv2.FONT_HERSHEY_SIMPLEX\n    for i in range(len(detections)):\n        x1, y1, x2, y2 = detections.xyxy[i].astype(int)\n        class_id = (\n            detections.class_id[i] if detections.class_id is not None else None\n        )\n        idx = class_id if class_id is not None else i\n        color = (\n            self.color.by_idx(idx)\n            if isinstance(self.color, ColorPalette)\n            else self.color\n        )\n        cv2.rectangle(\n            img=scene,\n            pt1=(x1, y1),\n            pt2=(x2, y2),\n            color=color.as_bgr(),\n            thickness=self.thickness,\n        )\n        if skip_label:\n            continue\n\n        text = (\n            f\"{class_id}\"\n            if (labels is None or len(detections) != len(labels))\n            else labels[i]\n        )\n\n        text_width, text_height = cv2.getTextSize(\n            text=text,\n            fontFace=font,\n            fontScale=self.text_scale,\n            thickness=self.text_thickness,\n        )[0]\n\n        text_x = x1 + self.text_padding\n        text_y = y1 - self.text_padding\n\n        text_background_x1 = x1\n        text_background_y1 = y1 - 2 * self.text_padding - text_height\n\n        text_background_x2 = x1 + 2 * self.text_padding + text_width\n        text_background_y2 = y1\n\n        cv2.rectangle(\n            img=scene,\n            pt1=(text_background_x1, text_background_y1),\n            pt2=(text_background_x2, text_background_y2),\n            color=color.as_bgr(),\n            thickness=cv2.FILLED,\n        )\n        cv2.putText(\n            img=scene,\n            text=text,\n            org=(text_x, text_y),\n            fontFace=font,\n            fontScale=self.text_scale,\n            color=self.text_color.as_rgb(),\n            thickness=self.text_thickness,\n            lineType=cv2.LINE_AA,\n        )\n    return scene\n
"},{"location":"detection/annotate/#maskannotator","title":"MaskAnnotator","text":"

A class for overlaying masks on an image using detections provided.

Attributes:

Name Type Description color Union[Color, ColorPalette]

The color to fill the mask, can be a single color or a color palette

Source code in supervision/detection/annotate.py
class MaskAnnotator:\n\"\"\"\n    A class for overlaying masks on an image using detections provided.\n\n    Attributes:\n        color (Union[Color, ColorPalette]): The color to fill the mask, can be a single color or a color palette\n    \"\"\"\n\n    def __init__(\n        self,\n        color: Union[Color, ColorPalette] = ColorPalette.default(),\n    ):\n        self.color: Union[Color, ColorPalette] = color\n\n    def annotate(\n        self, scene: np.ndarray, detections: Detections, opacity: float = 0.5\n    ) -> np.ndarray:\n\"\"\"\n        Overlays the masks on the given image based on the provided detections, with a specified opacity.\n\n        Args:\n            scene (np.ndarray): The image on which the masks will be overlaid\n            detections (Detections): The detections for which the masks will be overlaid\n            opacity (float): The opacity of the masks, between 0 and 1, default is 0.5\n\n        Returns:\n            np.ndarray: The image with the masks overlaid\n        \"\"\"\n        if detections.mask is None:\n            return scene\n\n        for i in np.flip(np.argsort(detections.area)):\n            class_id = (\n                detections.class_id[i] if detections.class_id is not None else None\n            )\n            idx = class_id if class_id is not None else i\n            color = (\n                self.color.by_idx(idx)\n                if isinstance(self.color, ColorPalette)\n                else self.color\n            )\n\n            mask = detections.mask[i]\n            colored_mask = np.zeros_like(scene, dtype=np.uint8)\n            colored_mask[:] = color.as_bgr()\n\n            scene = np.where(\n                np.expand_dims(mask, axis=-1),\n                np.uint8(opacity * colored_mask + (1 - opacity) * scene),\n                scene,\n            )\n\n        return scene\n
"},{"location":"detection/annotate/#supervision.detection.annotate.MaskAnnotator.annotate","title":"annotate(scene, detections, opacity=0.5)","text":"

Overlays the masks on the given image based on the provided detections, with a specified opacity.

Parameters:

Name Type Description Default scene np.ndarray

The image on which the masks will be overlaid

required detections Detections

The detections for which the masks will be overlaid

required opacity float

The opacity of the masks, between 0 and 1, default is 0.5

0.5

Returns:

Type Description np.ndarray

np.ndarray: The image with the masks overlaid

Source code in supervision/detection/annotate.py
def annotate(\n    self, scene: np.ndarray, detections: Detections, opacity: float = 0.5\n) -> np.ndarray:\n\"\"\"\n    Overlays the masks on the given image based on the provided detections, with a specified opacity.\n\n    Args:\n        scene (np.ndarray): The image on which the masks will be overlaid\n        detections (Detections): The detections for which the masks will be overlaid\n        opacity (float): The opacity of the masks, between 0 and 1, default is 0.5\n\n    Returns:\n        np.ndarray: The image with the masks overlaid\n    \"\"\"\n    if detections.mask is None:\n        return scene\n\n    for i in np.flip(np.argsort(detections.area)):\n        class_id = (\n            detections.class_id[i] if detections.class_id is not None else None\n        )\n        idx = class_id if class_id is not None else i\n        color = (\n            self.color.by_idx(idx)\n            if isinstance(self.color, ColorPalette)\n            else self.color\n        )\n\n        mask = detections.mask[i]\n        colored_mask = np.zeros_like(scene, dtype=np.uint8)\n        colored_mask[:] = color.as_bgr()\n\n        scene = np.where(\n            np.expand_dims(mask, axis=-1),\n            np.uint8(opacity * colored_mask + (1 - opacity) * scene),\n            scene,\n        )\n\n    return scene\n
"},{"location":"detection/core/","title":"Core","text":""},{"location":"detection/core/#detections","title":"Detections","text":"

Data class containing information about the detections in a video frame.

Attributes:

Name Type Description xyxy np.ndarray

An array of shape (n, 4) containing the bounding boxes coordinates in format [x1, y1, x2, y2]

mask np.Optional[np.ndarray]

(Optional[np.ndarray]): An array of shape (n, W, H) containing the segmentation masks.

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 (n,) containing the class ids of the detections.

tracker_id Optional[np.ndarray]

An array of shape (n,) containing the tracker ids of the detections.

Source code in supervision/detection/core.py
@dataclass\nclass Detections:\n\"\"\"\n    Data class containing information about the detections in a video frame.\n    Attributes:\n        xyxy (np.ndarray): An array of shape `(n, 4)` containing the bounding boxes coordinates in format `[x1, y1, x2, y2]`\n        mask: (Optional[np.ndarray]): An array of shape `(n, W, H)` containing the segmentation masks.\n        confidence (Optional[np.ndarray]): An array of shape `(n,)` containing the confidence scores of the detections.\n        class_id (Optional[np.ndarray]): An array of shape `(n,)` containing the class ids of the detections.\n        tracker_id (Optional[np.ndarray]): An array of shape `(n,)` containing the tracker ids of the detections.\n    \"\"\"\n\n    xyxy: np.ndarray\n    mask: np.Optional[np.ndarray] = None\n    confidence: Optional[np.ndarray] = None\n    class_id: Optional[np.ndarray] = None\n    tracker_id: Optional[np.ndarray] = None\n\n    def __post_init__(self):\n        n = len(self.xyxy)\n        _validate_xyxy(xyxy=self.xyxy, n=n)\n        _validate_mask(mask=self.mask, n=n)\n        _validate_class_id(class_id=self.class_id, n=n)\n        _validate_confidence(confidence=self.confidence, n=n)\n        _validate_tracker_id(tracker_id=self.tracker_id, n=n)\n\n    def __len__(self):\n\"\"\"\n        Returns the number of detections in the Detections object.\n        \"\"\"\n        return len(self.xyxy)\n\n    def __iter__(\n        self,\n    ) -> Iterator[\n        Tuple[\n            np.ndarray,\n            Optional[np.ndarray],\n            Optional[float],\n            Optional[int],\n            Optional[int],\n        ]\n    ]:\n\"\"\"\n        Iterates over the Detections object and yield a tuple of `(xyxy, mask, confidence, class_id, tracker_id)` for each detection.\n        \"\"\"\n        for i in range(len(self.xyxy)):\n            yield (\n                self.xyxy[i],\n                self.mask[i] if self.mask is not None else None,\n                self.confidence[i] if self.confidence is not None else None,\n                self.class_id[i] if self.class_id is not None else None,\n                self.tracker_id[i] if self.tracker_id is not None else None,\n            )\n\n    def __eq__(self, other: Detections):\n        return all(\n            [\n                np.array_equal(self.xyxy, other.xyxy),\n                any(\n                    [\n                        self.mask is None and other.mask is None,\n                        np.array_equal(self.mask, other.mask),\n                    ]\n                ),\n                any(\n                    [\n                        self.class_id is None and other.class_id is None,\n                        np.array_equal(self.class_id, other.class_id),\n                    ]\n                ),\n                any(\n                    [\n                        self.confidence is None and other.confidence is None,\n                        np.array_equal(self.confidence, other.confidence),\n                    ]\n                ),\n                any(\n                    [\n                        self.tracker_id is None and other.tracker_id is None,\n                        np.array_equal(self.tracker_id, other.tracker_id),\n                    ]\n                ),\n            ]\n        )\n\n    @classmethod\n    def from_yolov5(cls, yolov5_results) -> Detections:\n\"\"\"\n        Creates a Detections instance from a [YOLOv5](https://github.com/ultralytics/yolov5) inference result.\n\n        Args:\n            yolov5_results (yolov5.models.common.Detections): The output Detections instance from YOLOv5\n\n        Returns:\n            Detections: A new Detections object.\n\n        Example:\n            ```python\n            >>> import cv2\n            >>> import torch\n            >>> import supervision as sv\n\n            >>> image = cv2.imread(SOURCE_IMAGE_PATH)\n            >>> model = torch.hub.load('ultralytics/yolov5', 'yolov5s')\n            >>> result = model(image)\n            >>> detections = sv.Detections.from_yolov5(result)\n            ```\n        \"\"\"\n        yolov5_detections_predictions = yolov5_results.pred[0].cpu().cpu().numpy()\n        return cls(\n            xyxy=yolov5_detections_predictions[:, :4],\n            confidence=yolov5_detections_predictions[:, 4],\n            class_id=yolov5_detections_predictions[:, 5].astype(int),\n        )\n\n    @classmethod\n    def from_yolov8(cls, yolov8_results) -> Detections:\n\"\"\"\n        Creates a Detections instance from a [YOLOv8](https://github.com/ultralytics/ultralytics) inference result.\n\n        Args:\n            yolov8_results (ultralytics.yolo.engine.results.Results): The output Results instance from YOLOv8\n\n        Returns:\n            Detections: A new Detections object.\n\n        Example:\n            ```python\n            >>> import cv2\n            >>> from ultralytics import YOLO\n            >>> import supervision as sv\n\n            >>> image = cv2.imread(SOURCE_IMAGE_PATH)\n            >>> model = YOLO('yolov8s.pt')\n            >>> result = model(image)[0]\n            >>> detections = sv.Detections.from_yolov8(result)\n            ```\n        \"\"\"\n        return cls(\n            xyxy=yolov8_results.boxes.xyxy.cpu().numpy(),\n            confidence=yolov8_results.boxes.conf.cpu().numpy(),\n            class_id=yolov8_results.boxes.cls.cpu().numpy().astype(int),\n            mask=extract_yolov8_masks(yolov8_results),\n        )\n\n    @classmethod\n    def from_yolo_nas(cls, yolo_nas_results) -> Detections:\n\"\"\"\n        Creates a Detections instance from a [YOLO-NAS](https://github.com/Deci-AI/super-gradients/blob/master/YOLONAS.md) inference result.\n\n        Args:\n            yolo_nas_results (super_gradients.training.models.prediction_results.ImageDetectionPrediction): The output Results instance from YOLO-NAS\n\n        Returns:\n            Detections: A new Detections object.\n\n        Example:\n            ```python\n            >>> import cv2\n            >>> from super_gradients.training import models\n            >>> import supervision as sv\n\n            >>> image = cv2.imread(SOURCE_IMAGE_PATH)\n            >>> model = models.get('yolo_nas_l', pretrained_weights=\"coco\")\n            >>> result = list(model.predict(image, conf=0.35))[0]\n            >>> detections = sv.Detections.from_yolo_nas(result)\n            ```\n        \"\"\"\n        return cls(\n            xyxy=yolo_nas_results.prediction.bboxes_xyxy,\n            confidence=yolo_nas_results.prediction.confidence,\n            class_id=yolo_nas_results.prediction.labels.astype(int),\n        )\n\n    @classmethod\n    def from_transformers(cls, transformers_results: dict) -> Detections:\n\"\"\"\n        Creates a Detections instance from object detection [transformer](https://github.com/huggingface/transformers) inference result.\n\n        Returns:\n            Detections: A new Detections object.\n        \"\"\"\n        return cls(\n            xyxy=transformers_results[\"boxes\"].cpu().numpy(),\n            confidence=transformers_results[\"scores\"].cpu().numpy(),\n            class_id=transformers_results[\"labels\"].cpu().numpy().astype(int),\n        )\n\n    @classmethod\n    def from_detectron2(cls, detectron2_results) -> Detections:\n\"\"\"\n        Create a Detections object from the [Detectron2](https://github.com/facebookresearch/detectron2) inference result.\n\n        Args:\n            detectron2_results: The output of a Detectron2 model containing instances with prediction data.\n\n        Returns:\n            (Detections): A Detections object containing the bounding boxes, class IDs, and confidences of the predictions.\n\n        Example:\n            ```python\n            >>> import cv2\n            >>> from detectron2.engine import DefaultPredictor\n            >>> from detectron2.config import get_cfg\n            >>> import supervision as sv\n\n            >>> image = cv2.imread(SOURCE_IMAGE_PATH)\n            >>> cfg = get_cfg()\n            >>> cfg.merge_from_file(\"path/to/config.yaml\")\n            >>> cfg.MODEL.WEIGHTS = \"path/to/model_weights.pth\"\n            >>> predictor = DefaultPredictor(cfg)\n            >>> result = predictor(image)\n\n            >>> detections = sv.Detections.from_detectron2(result)\n            ```\n        \"\"\"\n        return cls(\n            xyxy=detectron2_results[\"instances\"].pred_boxes.tensor.cpu().numpy(),\n            confidence=detectron2_results[\"instances\"].scores.cpu().numpy(),\n            class_id=detectron2_results[\"instances\"]\n            .pred_classes.cpu()\n            .numpy()\n            .astype(int),\n        )\n\n    @classmethod\n    def from_roboflow(cls, roboflow_result: dict, class_list: List[str]) -> Detections:\n\"\"\"\n        Create a Detections object from the [Roboflow](https://roboflow.com/) API inference result.\n\n        Args:\n            roboflow_result (dict): The result from the Roboflow API containing predictions.\n            class_list (List[str]): A list of class names corresponding to the class IDs in the API result.\n\n        Returns:\n            (Detections): A Detections object containing the bounding boxes, class IDs, and confidences of the predictions.\n\n        Example:\n            ```python\n            >>> import supervision as sv\n\n            >>> roboflow_result = {\n            ...     \"predictions\": [\n            ...         {\n            ...             \"x\": 0.5,\n            ...             \"y\": 0.5,\n            ...             \"width\": 0.2,\n            ...             \"height\": 0.3,\n            ...             \"class\": \"person\",\n            ...             \"confidence\": 0.9\n            ...         },\n            ...         # ... more predictions ...\n            ...     ]\n            ... }\n            >>> class_list = [\"person\", \"car\", \"dog\"]\n\n            >>> detections = sv.Detections.from_roboflow(roboflow_result, class_list)\n            ```\n        \"\"\"\n        xyxy, confidence, class_id, masks = process_roboflow_result(\n            roboflow_result=roboflow_result, class_list=class_list\n        )\n        return Detections(\n            xyxy=xyxy,\n            confidence=confidence,\n            class_id=class_id,\n            mask=masks,\n        )\n\n    @classmethod\n    def from_sam(cls, sam_result: List[dict]) -> Detections:\n\"\"\"\n        Creates a Detections instance from [Segment Anything Model](https://github.com/facebookresearch/segment-anything) inference result.\n\n        Args:\n            sam_result (List[dict]): The output Results instance from SAM\n\n        Returns:\n            Detections: A new Detections object.\n\n        Example:\n            ```python\n            >>> import supervision as sv\n            >>> from segment_anything import sam_model_registry, SamAutomaticMaskGenerator\n\n            >>> sam = sam_model_registry[MODEL_TYPE](checkpoint=CHECKPOINT_PATH).to(device=DEVICE)\n            >>> mask_generator = SamAutomaticMaskGenerator(sam)\n            >>> sam_result = mask_generator.generate(IMAGE)\n            >>> detections = sv.Detections.from_sam(sam_result=sam_result)\n            ```\n        \"\"\"\n        sorted_generated_masks = sorted(\n            sam_result, key=lambda x: x[\"area\"], reverse=True\n        )\n\n        xywh = np.array([mask[\"bbox\"] for mask in sorted_generated_masks])\n        mask = np.array([mask[\"segmentation\"] for mask in sorted_generated_masks])\n\n        return Detections(xyxy=xywh_to_xyxy(boxes_xywh=xywh), mask=mask)\n\n    @classmethod\n    def empty(cls) -> Detections:\n\"\"\"\n        Create an empty Detections object with no bounding boxes, confidences, or class IDs.\n\n        Returns:\n            (Detections): An empty Detections object.\n\n        Example:\n            ```python\n            >>> from supervision import Detections\n\n            >>> empty_detections = Detections.empty()\n            ```\n        \"\"\"\n        return cls(\n            xyxy=np.empty((0, 4), dtype=np.float32),\n            confidence=np.array([], dtype=np.float32),\n            class_id=np.array([], dtype=int),\n        )\n\n    @classmethod\n    def merge(cls, detections_list: List[Detections]) -> Detections:\n\"\"\"\n        Merge a list of Detections objects into a single Detections object.\n\n        This method takes a list of Detections objects and combines their respective fields (`xyxy`, `mask`,\n        `confidence`, `class_id`, and `tracker_id`) into a single Detections object. If all elements in a field are not\n        `None`, the corresponding field will be stacked. Otherwise, the field will be set to `None`.\n\n        Args:\n            detections_list (List[Detections]): A list of Detections objects to merge.\n\n        Returns:\n            (Detections): A single Detections object containing the merged data from the input list.\n\n        Example:\n            ```python\n            >>> from supervision import Detections\n\n            >>> detections_1 = Detections(...)\n            >>> detections_2 = Detections(...)\n\n            >>> merged_detections = Detections.merge([detections_1, detections_2])\n            ```\n        \"\"\"\n        if len(detections_list) == 0:\n            return Detections.empty()\n\n        detections_tuples_list = [astuple(detection) for detection in detections_list]\n        xyxy, mask, confidence, class_id, tracker_id = [\n            list(field) for field in zip(*detections_tuples_list)\n        ]\n\n        all_not_none = lambda l: all(x is not None for x in l)\n\n        xyxy = np.vstack(xyxy)\n        mask = np.vstack(mask) if all_not_none(mask) else None\n        confidence = np.hstack(confidence) if all_not_none(confidence) else None\n        class_id = np.hstack(class_id) if all_not_none(class_id) else None\n        tracker_id = np.hstack(tracker_id) if all_not_none(tracker_id) else None\n\n        return cls(\n            xyxy=xyxy,\n            mask=mask,\n            confidence=confidence,\n            class_id=class_id,\n            tracker_id=tracker_id,\n        )\n\n    def get_anchor_coordinates(self, anchor: Position) -> np.ndarray:\n\"\"\"\n        Returns the bounding box coordinates for a specific anchor.\n\n        Args:\n            anchor (Position): Position of bounding box anchor for which to return the coordinates.\n\n        Returns:\n            np.ndarray: An array of shape `(n, 2)` containing the bounding box anchor coordinates in format `[x, y]`.\n        \"\"\"\n        if anchor == Position.CENTER:\n            return np.array(\n                [\n                    (self.xyxy[:, 0] + self.xyxy[:, 2]) / 2,\n                    (self.xyxy[:, 1] + self.xyxy[:, 3]) / 2,\n                ]\n            ).transpose()\n        elif anchor == Position.BOTTOM_CENTER:\n            return np.array(\n                [(self.xyxy[:, 0] + self.xyxy[:, 2]) / 2, self.xyxy[:, 3]]\n            ).transpose()\n\n        raise ValueError(f\"{anchor} is not supported.\")\n\n    def __getitem__(\n        self, index: Union[int, slice, List[int], np.ndarray]\n    ) -> Detections:\n\"\"\"\n        Get a subset of the Detections object.\n\n        Args:\n            index (Union[int, slice, List[int], np.ndarray]): The index or indices of the subset of the Detections\n\n        Returns:\n            (Detections): A subset of the Detections object.\n\n        Example:\n            ```python\n            >>> import supervision as sv\n\n            >>> detections = sv.Detections(...)\n\n            >>> first_detection = detections[0]\n\n            >>> first_10_detections = detections[0:10]\n\n            >>> some_detections = detections[[0, 2, 4]]\n\n            >>> class_0_detections = detections[detections.class_id == 0]\n\n            >>> high_confidence_detections = detections[detections.confidence > 0.5]\n            ```\n        \"\"\"\n        if isinstance(index, int):\n            index = [index]\n        return Detections(\n            xyxy=self.xyxy[index],\n            mask=self.mask[index] if self.mask is not None else None,\n            confidence=self.confidence[index] if self.confidence is not None else None,\n            class_id=self.class_id[index] if self.class_id is not None else None,\n            tracker_id=self.tracker_id[index] if self.tracker_id is not None else None,\n        )\n\n    @property\n    def area(self) -> np.ndarray:\n\"\"\"\n        Calculate the area of each detection in the set of object detections. If masks field is defined property\n        returns are of each mask. If only box is given property return area of each box.\n\n        Returns:\n          np.ndarray: An array of floats containing the area of each detection in the format of `(area_1, area_2, ..., area_n)`, where n is the number of detections.\n        \"\"\"\n        if self.mask is not None:\n            return np.array([np.sum(mask) for mask in self.mask])\n        else:\n            return self.box_area\n\n    @property\n    def box_area(self) -> np.ndarray:\n\"\"\"\n        Calculate the area of each bounding box in the set of object detections.\n\n        Returns:\n            np.ndarray: An array of floats containing the area of each bounding box in the format of `(area_1, area_2, ..., area_n)`, where n is the number of detections.\n        \"\"\"\n        return (self.xyxy[:, 3] - self.xyxy[:, 1]) * (self.xyxy[:, 2] - self.xyxy[:, 0])\n\n    def with_nms(\n        self, threshold: float = 0.5, class_agnostic: bool = False\n    ) -> Detections:\n\"\"\"\n        Perform non-maximum suppression on the current set of object detections.\n\n        Args:\n            threshold (float, optional): The intersection-over-union threshold to use for non-maximum suppression. Defaults to 0.5.\n            class_agnostic (bool, optional): Whether to perform class-agnostic non-maximum suppression. If True, the class_id of each detection will be ignored. Defaults to False.\n\n        Returns:\n            Detections: A new Detections object containing the subset of detections after non-maximum suppression.\n\n        Raises:\n            AssertionError: If `confidence` is None and class_agnostic is False. If `class_id` is None and class_agnostic is False.\n        \"\"\"\n        if len(self) == 0:\n            return self\n\n        assert (\n            self.confidence is not None\n        ), f\"Detections confidence must be given for NMS to be executed.\"\n\n        if class_agnostic:\n            predictions = np.hstack((self.xyxy, self.confidence.reshape(-1, 1)))\n            indices = non_max_suppression(\n                predictions=predictions, iou_threshold=threshold\n            )\n            return self[indices]\n\n        assert self.class_id is not None, (\n            f\"Detections class_id must be given for NMS to be executed. If you intended to perform class agnostic \"\n            f\"NMS set class_agnostic=True.\"\n        )\n\n        predictions = np.hstack(\n            (self.xyxy, self.confidence.reshape(-1, 1), self.class_id.reshape(-1, 1))\n        )\n        indices = non_max_suppression(predictions=predictions, iou_threshold=threshold)\n        return self[indices]\n
"},{"location":"detection/core/#supervision.detection.core.Detections.area","title":"area: np.ndarray property","text":"

Calculate the area of each detection in the set of object detections. If masks field is defined property returns are of each mask. If only box is given property return area of each box.

Returns:

Type Description np.ndarray

np.ndarray: An array of floats containing the area of each detection in the format of (area_1, area_2, ..., area_n), where n is the number of detections.

"},{"location":"detection/core/#supervision.detection.core.Detections.box_area","title":"box_area: np.ndarray property","text":"

Calculate the area of each bounding box in the set of object detections.

Returns:

Type Description np.ndarray

np.ndarray: An array of floats containing the area of each bounding box in the format of (area_1, area_2, ..., area_n), where n is the number of detections.

"},{"location":"detection/core/#supervision.detection.core.Detections.__getitem__","title":"__getitem__(index)","text":"

Get a subset of the Detections object.

Parameters:

Name Type Description Default index Union[int, slice, List[int], np.ndarray]

The index or indices of the subset of the Detections

required

Returns:

Type Description Detections

A subset of the Detections object.

Example
>>> import supervision as sv\n\n>>> detections = sv.Detections(...)\n\n>>> first_detection = detections[0]\n\n>>> first_10_detections = detections[0:10]\n\n>>> some_detections = detections[[0, 2, 4]]\n\n>>> class_0_detections = detections[detections.class_id == 0]\n\n>>> high_confidence_detections = detections[detections.confidence > 0.5]\n
Source code in supervision/detection/core.py
def __getitem__(\n    self, index: Union[int, slice, List[int], np.ndarray]\n) -> Detections:\n\"\"\"\n    Get a subset of the Detections object.\n\n    Args:\n        index (Union[int, slice, List[int], np.ndarray]): The index or indices of the subset of the Detections\n\n    Returns:\n        (Detections): A subset of the Detections object.\n\n    Example:\n        ```python\n        >>> import supervision as sv\n\n        >>> detections = sv.Detections(...)\n\n        >>> first_detection = detections[0]\n\n        >>> first_10_detections = detections[0:10]\n\n        >>> some_detections = detections[[0, 2, 4]]\n\n        >>> class_0_detections = detections[detections.class_id == 0]\n\n        >>> high_confidence_detections = detections[detections.confidence > 0.5]\n        ```\n    \"\"\"\n    if isinstance(index, int):\n        index = [index]\n    return Detections(\n        xyxy=self.xyxy[index],\n        mask=self.mask[index] if self.mask is not None else None,\n        confidence=self.confidence[index] if self.confidence is not None else None,\n        class_id=self.class_id[index] if self.class_id is not None else None,\n        tracker_id=self.tracker_id[index] if self.tracker_id is not None else None,\n    )\n
"},{"location":"detection/core/#supervision.detection.core.Detections.__iter__","title":"__iter__()","text":"

Iterates over the Detections object and yield a tuple of (xyxy, mask, confidence, class_id, tracker_id) for each detection.

Source code in supervision/detection/core.py
def __iter__(\n    self,\n) -> Iterator[\n    Tuple[\n        np.ndarray,\n        Optional[np.ndarray],\n        Optional[float],\n        Optional[int],\n        Optional[int],\n    ]\n]:\n\"\"\"\n    Iterates over the Detections object and yield a tuple of `(xyxy, mask, confidence, class_id, tracker_id)` for each detection.\n    \"\"\"\n    for i in range(len(self.xyxy)):\n        yield (\n            self.xyxy[i],\n            self.mask[i] if self.mask is not None else None,\n            self.confidence[i] if self.confidence is not None else None,\n            self.class_id[i] if self.class_id is not None else None,\n            self.tracker_id[i] if self.tracker_id is not None else None,\n        )\n
"},{"location":"detection/core/#supervision.detection.core.Detections.__len__","title":"__len__()","text":"

Returns the number of detections in the Detections object.

Source code in supervision/detection/core.py
def __len__(self):\n\"\"\"\n    Returns the number of detections in the Detections object.\n    \"\"\"\n    return len(self.xyxy)\n
"},{"location":"detection/core/#supervision.detection.core.Detections.empty","title":"empty() classmethod","text":"

Create an empty Detections object with no bounding boxes, confidences, or class IDs.

Returns:

Type Description Detections

An empty Detections object.

Example
>>> from supervision import Detections\n\n>>> empty_detections = Detections.empty()\n
Source code in supervision/detection/core.py
@classmethod\ndef empty(cls) -> Detections:\n\"\"\"\n    Create an empty Detections object with no bounding boxes, confidences, or class IDs.\n\n    Returns:\n        (Detections): An empty Detections object.\n\n    Example:\n        ```python\n        >>> from supervision import Detections\n\n        >>> empty_detections = Detections.empty()\n        ```\n    \"\"\"\n    return cls(\n        xyxy=np.empty((0, 4), dtype=np.float32),\n        confidence=np.array([], dtype=np.float32),\n        class_id=np.array([], dtype=int),\n    )\n
"},{"location":"detection/core/#supervision.detection.core.Detections.from_detectron2","title":"from_detectron2(detectron2_results) classmethod","text":"

Create a Detections object from the Detectron2 inference result.

Parameters:

Name Type Description Default detectron2_results

The output of a Detectron2 model containing instances with prediction data.

required

Returns:

Type Description Detections

A Detections object containing the bounding boxes, class IDs, and confidences of the predictions.

Example
>>> import cv2\n>>> from detectron2.engine import DefaultPredictor\n>>> from detectron2.config import get_cfg\n>>> import supervision as sv\n\n>>> image = cv2.imread(SOURCE_IMAGE_PATH)\n>>> cfg = get_cfg()\n>>> cfg.merge_from_file(\"path/to/config.yaml\")\n>>> cfg.MODEL.WEIGHTS = \"path/to/model_weights.pth\"\n>>> predictor = DefaultPredictor(cfg)\n>>> result = predictor(image)\n\n>>> detections = sv.Detections.from_detectron2(result)\n
Source code in supervision/detection/core.py
@classmethod\ndef from_detectron2(cls, detectron2_results) -> Detections:\n\"\"\"\n    Create a Detections object from the [Detectron2](https://github.com/facebookresearch/detectron2) inference result.\n\n    Args:\n        detectron2_results: The output of a Detectron2 model containing instances with prediction data.\n\n    Returns:\n        (Detections): A Detections object containing the bounding boxes, class IDs, and confidences of the predictions.\n\n    Example:\n        ```python\n        >>> import cv2\n        >>> from detectron2.engine import DefaultPredictor\n        >>> from detectron2.config import get_cfg\n        >>> import supervision as sv\n\n        >>> image = cv2.imread(SOURCE_IMAGE_PATH)\n        >>> cfg = get_cfg()\n        >>> cfg.merge_from_file(\"path/to/config.yaml\")\n        >>> cfg.MODEL.WEIGHTS = \"path/to/model_weights.pth\"\n        >>> predictor = DefaultPredictor(cfg)\n        >>> result = predictor(image)\n\n        >>> detections = sv.Detections.from_detectron2(result)\n        ```\n    \"\"\"\n    return cls(\n        xyxy=detectron2_results[\"instances\"].pred_boxes.tensor.cpu().numpy(),\n        confidence=detectron2_results[\"instances\"].scores.cpu().numpy(),\n        class_id=detectron2_results[\"instances\"]\n        .pred_classes.cpu()\n        .numpy()\n        .astype(int),\n    )\n
"},{"location":"detection/core/#supervision.detection.core.Detections.from_roboflow","title":"from_roboflow(roboflow_result, class_list) classmethod","text":"

Create a Detections object from the Roboflow API inference result.

Parameters:

Name Type Description Default roboflow_result dict

The result from the Roboflow API containing predictions.

required class_list List[str]

A list of class names corresponding to the class IDs in the API result.

required

Returns:

Type Description Detections

A Detections object containing the bounding boxes, class IDs, and confidences of the predictions.

Example
>>> import supervision as sv\n\n>>> roboflow_result = {\n...     \"predictions\": [\n...         {\n...             \"x\": 0.5,\n...             \"y\": 0.5,\n...             \"width\": 0.2,\n...             \"height\": 0.3,\n...             \"class\": \"person\",\n...             \"confidence\": 0.9\n...         },\n...         # ... more predictions ...\n...     ]\n... }\n>>> class_list = [\"person\", \"car\", \"dog\"]\n\n>>> detections = sv.Detections.from_roboflow(roboflow_result, class_list)\n
Source code in supervision/detection/core.py
@classmethod\ndef from_roboflow(cls, roboflow_result: dict, class_list: List[str]) -> Detections:\n\"\"\"\n    Create a Detections object from the [Roboflow](https://roboflow.com/) API inference result.\n\n    Args:\n        roboflow_result (dict): The result from the Roboflow API containing predictions.\n        class_list (List[str]): A list of class names corresponding to the class IDs in the API result.\n\n    Returns:\n        (Detections): A Detections object containing the bounding boxes, class IDs, and confidences of the predictions.\n\n    Example:\n        ```python\n        >>> import supervision as sv\n\n        >>> roboflow_result = {\n        ...     \"predictions\": [\n        ...         {\n        ...             \"x\": 0.5,\n        ...             \"y\": 0.5,\n        ...             \"width\": 0.2,\n        ...             \"height\": 0.3,\n        ...             \"class\": \"person\",\n        ...             \"confidence\": 0.9\n        ...         },\n        ...         # ... more predictions ...\n        ...     ]\n        ... }\n        >>> class_list = [\"person\", \"car\", \"dog\"]\n\n        >>> detections = sv.Detections.from_roboflow(roboflow_result, class_list)\n        ```\n    \"\"\"\n    xyxy, confidence, class_id, masks = process_roboflow_result(\n        roboflow_result=roboflow_result, class_list=class_list\n    )\n    return Detections(\n        xyxy=xyxy,\n        confidence=confidence,\n        class_id=class_id,\n        mask=masks,\n    )\n
"},{"location":"detection/core/#supervision.detection.core.Detections.from_sam","title":"from_sam(sam_result) classmethod","text":"

Creates a Detections instance from Segment Anything Model inference result.

Parameters:

Name Type Description Default sam_result List[dict]

The output Results instance from SAM

required

Returns:

Name Type Description Detections Detections

A new Detections object.

Example
>>> import supervision as sv\n>>> from segment_anything import sam_model_registry, SamAutomaticMaskGenerator\n\n>>> sam = sam_model_registry[MODEL_TYPE](checkpoint=CHECKPOINT_PATH).to(device=DEVICE)\n>>> mask_generator = SamAutomaticMaskGenerator(sam)\n>>> sam_result = mask_generator.generate(IMAGE)\n>>> detections = sv.Detections.from_sam(sam_result=sam_result)\n
Source code in supervision/detection/core.py
@classmethod\ndef from_sam(cls, sam_result: List[dict]) -> Detections:\n\"\"\"\n    Creates a Detections instance from [Segment Anything Model](https://github.com/facebookresearch/segment-anything) inference result.\n\n    Args:\n        sam_result (List[dict]): The output Results instance from SAM\n\n    Returns:\n        Detections: A new Detections object.\n\n    Example:\n        ```python\n        >>> import supervision as sv\n        >>> from segment_anything import sam_model_registry, SamAutomaticMaskGenerator\n\n        >>> sam = sam_model_registry[MODEL_TYPE](checkpoint=CHECKPOINT_PATH).to(device=DEVICE)\n        >>> mask_generator = SamAutomaticMaskGenerator(sam)\n        >>> sam_result = mask_generator.generate(IMAGE)\n        >>> detections = sv.Detections.from_sam(sam_result=sam_result)\n        ```\n    \"\"\"\n    sorted_generated_masks = sorted(\n        sam_result, key=lambda x: x[\"area\"], reverse=True\n    )\n\n    xywh = np.array([mask[\"bbox\"] for mask in sorted_generated_masks])\n    mask = np.array([mask[\"segmentation\"] for mask in sorted_generated_masks])\n\n    return Detections(xyxy=xywh_to_xyxy(boxes_xywh=xywh), mask=mask)\n
"},{"location":"detection/core/#supervision.detection.core.Detections.from_transformers","title":"from_transformers(transformers_results) classmethod","text":"

Creates a Detections instance from object detection transformer inference result.

Returns:

Name Type Description Detections Detections

A new Detections object.

Source code in supervision/detection/core.py
@classmethod\ndef from_transformers(cls, transformers_results: dict) -> Detections:\n\"\"\"\n    Creates a Detections instance from object detection [transformer](https://github.com/huggingface/transformers) inference result.\n\n    Returns:\n        Detections: A new Detections object.\n    \"\"\"\n    return cls(\n        xyxy=transformers_results[\"boxes\"].cpu().numpy(),\n        confidence=transformers_results[\"scores\"].cpu().numpy(),\n        class_id=transformers_results[\"labels\"].cpu().numpy().astype(int),\n    )\n
"},{"location":"detection/core/#supervision.detection.core.Detections.from_yolo_nas","title":"from_yolo_nas(yolo_nas_results) classmethod","text":"

Creates a Detections instance from a YOLO-NAS inference result.

Parameters:

Name Type Description Default yolo_nas_results super_gradients.training.models.prediction_results.ImageDetectionPrediction

The output Results instance from YOLO-NAS

required

Returns:

Name Type Description Detections Detections

A new Detections object.

Example
>>> import cv2\n>>> from super_gradients.training import models\n>>> import supervision as sv\n\n>>> image = cv2.imread(SOURCE_IMAGE_PATH)\n>>> model = models.get('yolo_nas_l', pretrained_weights=\"coco\")\n>>> result = list(model.predict(image, conf=0.35))[0]\n>>> detections = sv.Detections.from_yolo_nas(result)\n
Source code in supervision/detection/core.py
@classmethod\ndef from_yolo_nas(cls, yolo_nas_results) -> Detections:\n\"\"\"\n    Creates a Detections instance from a [YOLO-NAS](https://github.com/Deci-AI/super-gradients/blob/master/YOLONAS.md) inference result.\n\n    Args:\n        yolo_nas_results (super_gradients.training.models.prediction_results.ImageDetectionPrediction): The output Results instance from YOLO-NAS\n\n    Returns:\n        Detections: A new Detections object.\n\n    Example:\n        ```python\n        >>> import cv2\n        >>> from super_gradients.training import models\n        >>> import supervision as sv\n\n        >>> image = cv2.imread(SOURCE_IMAGE_PATH)\n        >>> model = models.get('yolo_nas_l', pretrained_weights=\"coco\")\n        >>> result = list(model.predict(image, conf=0.35))[0]\n        >>> detections = sv.Detections.from_yolo_nas(result)\n        ```\n    \"\"\"\n    return cls(\n        xyxy=yolo_nas_results.prediction.bboxes_xyxy,\n        confidence=yolo_nas_results.prediction.confidence,\n        class_id=yolo_nas_results.prediction.labels.astype(int),\n    )\n
"},{"location":"detection/core/#supervision.detection.core.Detections.from_yolov5","title":"from_yolov5(yolov5_results) classmethod","text":"

Creates a Detections instance from a YOLOv5 inference result.

Parameters:

Name Type Description Default yolov5_results yolov5.models.common.Detections

The output Detections instance from YOLOv5

required

Returns:

Name Type Description Detections Detections

A new Detections object.

Example
>>> import cv2\n>>> import torch\n>>> import supervision as sv\n\n>>> image = cv2.imread(SOURCE_IMAGE_PATH)\n>>> model = torch.hub.load('ultralytics/yolov5', 'yolov5s')\n>>> result = model(image)\n>>> detections = sv.Detections.from_yolov5(result)\n
Source code in supervision/detection/core.py
@classmethod\ndef from_yolov5(cls, yolov5_results) -> Detections:\n\"\"\"\n    Creates a Detections instance from a [YOLOv5](https://github.com/ultralytics/yolov5) inference result.\n\n    Args:\n        yolov5_results (yolov5.models.common.Detections): The output Detections instance from YOLOv5\n\n    Returns:\n        Detections: A new Detections object.\n\n    Example:\n        ```python\n        >>> import cv2\n        >>> import torch\n        >>> import supervision as sv\n\n        >>> image = cv2.imread(SOURCE_IMAGE_PATH)\n        >>> model = torch.hub.load('ultralytics/yolov5', 'yolov5s')\n        >>> result = model(image)\n        >>> detections = sv.Detections.from_yolov5(result)\n        ```\n    \"\"\"\n    yolov5_detections_predictions = yolov5_results.pred[0].cpu().cpu().numpy()\n    return cls(\n        xyxy=yolov5_detections_predictions[:, :4],\n        confidence=yolov5_detections_predictions[:, 4],\n        class_id=yolov5_detections_predictions[:, 5].astype(int),\n    )\n
"},{"location":"detection/core/#supervision.detection.core.Detections.from_yolov8","title":"from_yolov8(yolov8_results) classmethod","text":"

Creates a Detections instance from a YOLOv8 inference result.

Parameters:

Name Type Description Default yolov8_results ultralytics.yolo.engine.results.Results

The output Results instance from YOLOv8

required

Returns:

Name Type Description Detections Detections

A new Detections object.

Example
>>> import cv2\n>>> from ultralytics import YOLO\n>>> import supervision as sv\n\n>>> image = cv2.imread(SOURCE_IMAGE_PATH)\n>>> model = YOLO('yolov8s.pt')\n>>> result = model(image)[0]\n>>> detections = sv.Detections.from_yolov8(result)\n
Source code in supervision/detection/core.py
@classmethod\ndef from_yolov8(cls, yolov8_results) -> Detections:\n\"\"\"\n    Creates a Detections instance from a [YOLOv8](https://github.com/ultralytics/ultralytics) inference result.\n\n    Args:\n        yolov8_results (ultralytics.yolo.engine.results.Results): The output Results instance from YOLOv8\n\n    Returns:\n        Detections: A new Detections object.\n\n    Example:\n        ```python\n        >>> import cv2\n        >>> from ultralytics import YOLO\n        >>> import supervision as sv\n\n        >>> image = cv2.imread(SOURCE_IMAGE_PATH)\n        >>> model = YOLO('yolov8s.pt')\n        >>> result = model(image)[0]\n        >>> detections = sv.Detections.from_yolov8(result)\n        ```\n    \"\"\"\n    return cls(\n        xyxy=yolov8_results.boxes.xyxy.cpu().numpy(),\n        confidence=yolov8_results.boxes.conf.cpu().numpy(),\n        class_id=yolov8_results.boxes.cls.cpu().numpy().astype(int),\n        mask=extract_yolov8_masks(yolov8_results),\n    )\n
"},{"location":"detection/core/#supervision.detection.core.Detections.get_anchor_coordinates","title":"get_anchor_coordinates(anchor)","text":"

Returns the bounding box coordinates for a specific anchor.

Parameters:

Name Type Description Default anchor Position

Position of bounding box anchor for which to return the coordinates.

required

Returns:

Type Description np.ndarray

np.ndarray: An array of shape (n, 2) containing the bounding box anchor coordinates in format [x, y].

Source code in supervision/detection/core.py
def get_anchor_coordinates(self, anchor: Position) -> np.ndarray:\n\"\"\"\n    Returns the bounding box coordinates for a specific anchor.\n\n    Args:\n        anchor (Position): Position of bounding box anchor for which to return the coordinates.\n\n    Returns:\n        np.ndarray: An array of shape `(n, 2)` containing the bounding box anchor coordinates in format `[x, y]`.\n    \"\"\"\n    if anchor == Position.CENTER:\n        return np.array(\n            [\n                (self.xyxy[:, 0] + self.xyxy[:, 2]) / 2,\n                (self.xyxy[:, 1] + self.xyxy[:, 3]) / 2,\n            ]\n        ).transpose()\n    elif anchor == Position.BOTTOM_CENTER:\n        return np.array(\n            [(self.xyxy[:, 0] + self.xyxy[:, 2]) / 2, self.xyxy[:, 3]]\n        ).transpose()\n\n    raise ValueError(f\"{anchor} is not supported.\")\n
"},{"location":"detection/core/#supervision.detection.core.Detections.merge","title":"merge(detections_list) classmethod","text":"

Merge a list of Detections objects into a single Detections object.

This method takes a list of Detections objects and combines their respective fields (xyxy, mask, confidence, class_id, and tracker_id) into a single Detections object. If all elements in a field are not None, the corresponding field will be stacked. Otherwise, the field will be set to None.

Parameters:

Name Type Description Default detections_list List[Detections]

A list of Detections objects to merge.

required

Returns:

Type Description Detections

A single Detections object containing the merged data from the input list.

Example
>>> from supervision import Detections\n\n>>> detections_1 = Detections(...)\n>>> detections_2 = Detections(...)\n\n>>> merged_detections = Detections.merge([detections_1, detections_2])\n
Source code in supervision/detection/core.py
@classmethod\ndef merge(cls, detections_list: List[Detections]) -> Detections:\n\"\"\"\n    Merge a list of Detections objects into a single Detections object.\n\n    This method takes a list of Detections objects and combines their respective fields (`xyxy`, `mask`,\n    `confidence`, `class_id`, and `tracker_id`) into a single Detections object. If all elements in a field are not\n    `None`, the corresponding field will be stacked. Otherwise, the field will be set to `None`.\n\n    Args:\n        detections_list (List[Detections]): A list of Detections objects to merge.\n\n    Returns:\n        (Detections): A single Detections object containing the merged data from the input list.\n\n    Example:\n        ```python\n        >>> from supervision import Detections\n\n        >>> detections_1 = Detections(...)\n        >>> detections_2 = Detections(...)\n\n        >>> merged_detections = Detections.merge([detections_1, detections_2])\n        ```\n    \"\"\"\n    if len(detections_list) == 0:\n        return Detections.empty()\n\n    detections_tuples_list = [astuple(detection) for detection in detections_list]\n    xyxy, mask, confidence, class_id, tracker_id = [\n        list(field) for field in zip(*detections_tuples_list)\n    ]\n\n    all_not_none = lambda l: all(x is not None for x in l)\n\n    xyxy = np.vstack(xyxy)\n    mask = np.vstack(mask) if all_not_none(mask) else None\n    confidence = np.hstack(confidence) if all_not_none(confidence) else None\n    class_id = np.hstack(class_id) if all_not_none(class_id) else None\n    tracker_id = np.hstack(tracker_id) if all_not_none(tracker_id) else None\n\n    return cls(\n        xyxy=xyxy,\n        mask=mask,\n        confidence=confidence,\n        class_id=class_id,\n        tracker_id=tracker_id,\n    )\n
"},{"location":"detection/core/#supervision.detection.core.Detections.with_nms","title":"with_nms(threshold=0.5, class_agnostic=False)","text":"

Perform non-maximum suppression on the current set of object detections.

Parameters:

Name Type Description Default threshold float

The intersection-over-union threshold to use for non-maximum suppression. Defaults to 0.5.

0.5 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.

False

Returns:

Name Type Description Detections Detections

A new Detections object containing the subset of detections after non-maximum suppression.

Raises:

Type Description AssertionError

If confidence is None and class_agnostic is False. If class_id is None and class_agnostic is False.

Source code in supervision/detection/core.py
def with_nms(\n    self, threshold: float = 0.5, class_agnostic: bool = False\n) -> Detections:\n\"\"\"\n    Perform non-maximum suppression on the current set of object detections.\n\n    Args:\n        threshold (float, optional): The intersection-over-union threshold to use for non-maximum suppression. Defaults to 0.5.\n        class_agnostic (bool, optional): Whether to perform class-agnostic non-maximum suppression. If True, the class_id of each detection will be ignored. Defaults to False.\n\n    Returns:\n        Detections: A new Detections object containing the subset of detections after non-maximum suppression.\n\n    Raises:\n        AssertionError: If `confidence` is None and class_agnostic is False. If `class_id` is None and class_agnostic is False.\n    \"\"\"\n    if len(self) == 0:\n        return self\n\n    assert (\n        self.confidence is not None\n    ), f\"Detections confidence must be given for NMS to be executed.\"\n\n    if class_agnostic:\n        predictions = np.hstack((self.xyxy, self.confidence.reshape(-1, 1)))\n        indices = non_max_suppression(\n            predictions=predictions, iou_threshold=threshold\n        )\n        return self[indices]\n\n    assert self.class_id is not None, (\n        f\"Detections class_id must be given for NMS to be executed. If you intended to perform class agnostic \"\n        f\"NMS set class_agnostic=True.\"\n    )\n\n    predictions = np.hstack(\n        (self.xyxy, self.confidence.reshape(-1, 1), self.class_id.reshape(-1, 1))\n    )\n    indices = non_max_suppression(predictions=predictions, iou_threshold=threshold)\n    return self[indices]\n
"},{"location":"detection/utils/","title":"Utils","text":""},{"location":"detection/utils/#box_iou_batch","title":"box_iou_batch","text":"

Compute Intersection over Union (IoU) of two sets of bounding boxes - boxes_true and boxes_detection. Both sets of boxes are expected to be in (x_min, y_min, x_max, y_max) format.

Parameters:

Name Type Description Default boxes_true np.ndarray

2D np.ndarray representing ground-truth boxes. shape = (N, 4) where N is number of true objects.

required boxes_detection np.ndarray

2D np.ndarray representing detection boxes. shape = (M, 4) where M is number of detected objects.

required

Returns:

Type Description np.ndarray

np.ndarray: Pairwise IoU of boxes from boxes_true and boxes_detection. shape = (N, M) where N is number of true objects and M is number of detected objects.

Source code in supervision/detection/utils.py
def box_iou_batch(boxes_true: np.ndarray, boxes_detection: np.ndarray) -> np.ndarray:\n\"\"\"\n    Compute Intersection over Union (IoU) of two sets of bounding boxes - `boxes_true` and `boxes_detection`. Both sets\n    of boxes are expected to be in `(x_min, y_min, x_max, y_max)` format.\n\n    Args:\n        boxes_true (np.ndarray): 2D `np.ndarray` representing ground-truth boxes. `shape = (N, 4)` where `N` is number of true objects.\n        boxes_detection (np.ndarray): 2D `np.ndarray` representing detection boxes. `shape = (M, 4)` where `M` is number of detected objects.\n\n    Returns:\n        np.ndarray: Pairwise IoU of boxes from `boxes_true` and `boxes_detection`. `shape = (N, M)` where `N` is number of true objects and `M` is number of detected objects.\n    \"\"\"\n\n    def box_area(box):\n        return (box[2] - box[0]) * (box[3] - box[1])\n\n    area_true = box_area(boxes_true.T)\n    area_detection = box_area(boxes_detection.T)\n\n    top_left = np.maximum(boxes_true[:, None, :2], boxes_detection[:, :2])\n    bottom_right = np.minimum(boxes_true[:, None, 2:], boxes_detection[:, 2:])\n\n    area_inter = np.prod(np.clip(bottom_right - top_left, a_min=0, a_max=None), 2)\n    return area_inter / (area_true[:, None] + area_detection - area_inter)\n
"},{"location":"detection/utils/#non_max_suppression","title":"non_max_suppression","text":"

Perform Non-Maximum Suppression (NMS) on object detection predictions.

Parameters:

Name Type Description Default 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).

required iou_threshold float

The intersection-over-union threshold to use for non-maximum suppression.

0.5

Returns:

Type Description np.ndarray

np.ndarray: A boolean array indicating which predictions to keep after non-maximum suppression.

Raises:

Type Description AssertionError

If iou_threshold is not within the closed range from 0 to 1.

Source code in supervision/detection/utils.py
def non_max_suppression(\n    predictions: np.ndarray, iou_threshold: float = 0.5\n) -> np.ndarray:\n\"\"\"\n    Perform Non-Maximum Suppression (NMS) on object detection predictions.\n\n    Args:\n        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)`.\n        iou_threshold (float, optional): The intersection-over-union threshold to use for non-maximum suppression.\n\n    Returns:\n        np.ndarray: A boolean array indicating which predictions to keep after non-maximum suppression.\n\n    Raises:\n        AssertionError: If `iou_threshold` is not within the closed range from `0` to `1`.\n    \"\"\"\n    assert 0 <= iou_threshold <= 1, (\n        f\"Value of `iou_threshold` must be in the closed range from 0 to 1, \"\n        f\"{iou_threshold} given.\"\n    )\n    rows, columns = predictions.shape\n\n    # add column #5 - category filled with zeros for agnostic nms\n    if columns == 5:\n        predictions = np.c_[predictions, np.zeros(rows)]\n\n    # sort predictions column #4 - score\n    sort_index = np.flip(predictions[:, 4].argsort())\n    predictions = predictions[sort_index]\n\n    boxes = predictions[:, :4]\n    categories = predictions[:, 5]\n    ious = box_iou_batch(boxes, boxes)\n    ious = ious - np.eye(rows)\n\n    keep = np.ones(rows, dtype=bool)\n\n    for index, (iou, category) in enumerate(zip(ious, categories)):\n        if not keep[index]:\n            continue\n\n        # drop detections with iou > iou_threshold and same category as current detections\n        condition = (iou > iou_threshold) & (categories == category)\n        keep = keep & ~condition\n\n    return keep[sort_index.argsort()]\n
"},{"location":"detection/utils/#polygon_to_mask","title":"polygon_to_mask","text":"

Generate a mask from a polygon.

Parameters:

Name Type Description Default polygon np.ndarray

The polygon for which the mask should be generated, given as a list of vertices.

required resolution_wh Tuple[int, int]

The width and height of the desired resolution.

required

Returns:

Type Description np.ndarray

np.ndarray: The generated 2D mask, where the polygon is marked with 1's and the rest is filled with 0's.

Source code in supervision/detection/utils.py
def polygon_to_mask(polygon: np.ndarray, resolution_wh: Tuple[int, int]) -> np.ndarray:\n\"\"\"Generate a mask from a polygon.\n\n    Args:\n        polygon (np.ndarray): The polygon for which the mask should be generated, given as a list of vertices.\n        resolution_wh (Tuple[int, int]): The width and height of the desired resolution.\n\n    Returns:\n        np.ndarray: The generated 2D mask, where the polygon is marked with `1`'s and the rest is filled with `0`'s.\n    \"\"\"\n    width, height = resolution_wh\n    mask = np.zeros((height, width), dtype=np.uint8)\n    cv2.fillPoly(mask, [polygon], color=1)\n    return mask\n
"},{"location":"detection/utils/#mask_to_xyxy","title":"mask_to_xyxy","text":"

Converts a 3D np.array of 2D bool masks into a 2D np.array of bounding boxes.

Parameters:

Name Type Description Default masks np.ndarray

A 3D np.array of shape (N, W, H) containing 2D bool masks

required

Returns:

Type Description np.ndarray

np.ndarray: A 2D np.array of shape (N, 4) containing the bounding boxes (x_min, y_min, x_max, y_max) for each mask

Source code in supervision/detection/utils.py
def mask_to_xyxy(masks: np.ndarray) -> np.ndarray:\n\"\"\"\n    Converts a 3D `np.array` of 2D bool masks into a 2D `np.array` of bounding boxes.\n\n    Parameters:\n        masks (np.ndarray): A 3D `np.array` of shape `(N, W, H)` containing 2D bool masks\n\n    Returns:\n        np.ndarray: A 2D `np.array` of shape `(N, 4)` containing the bounding boxes `(x_min, y_min, x_max, y_max)` for each mask\n    \"\"\"\n    n = masks.shape[0]\n    bboxes = np.zeros((n, 4), dtype=int)\n\n    for i, mask in enumerate(masks):\n        rows, cols = np.where(mask)\n\n        if len(rows) > 0 and len(cols) > 0:\n            x_min, x_max = np.min(cols), np.max(cols)\n            y_min, y_max = np.min(rows), np.max(rows)\n            bboxes[i, :] = [x_min, y_min, x_max, y_max]\n\n    return bboxes\n
"},{"location":"detection/utils/#mask_to_polygons","title":"mask_to_polygons","text":"

Converts a binary mask to a list of polygons.

Parameters:

Name Type Description Default mask np.ndarray

A binary mask represented as a 2D NumPy array of shape (H, W), where H and W are the height and width of the mask, respectively.

required

Returns:

Type Description List[np.ndarray]

List[np.ndarray]: A list of polygons, where each polygon is represented by a NumPy array of shape (N, 2), containing the x, y coordinates of the points. Polygons with fewer points than MIN_POLYGON_POINT_COUNT = 3 are excluded from the output.

Source code in supervision/detection/utils.py
def mask_to_polygons(mask: np.ndarray) -> List[np.ndarray]:\n\"\"\"\n    Converts a binary mask to a list of polygons.\n\n    Parameters:\n        mask (np.ndarray): A binary mask represented as a 2D NumPy array of shape `(H, W)`,\n            where H and W are the height and width of the mask, respectively.\n\n    Returns:\n        List[np.ndarray]: A list of polygons, where each polygon is represented by a NumPy array of shape `(N, 2)`,\n            containing the `x`, `y` coordinates of the points. Polygons with fewer points than `MIN_POLYGON_POINT_COUNT = 3`\n            are excluded from the output.\n    \"\"\"\n\n    contours, _ = cv2.findContours(\n        mask.astype(np.uint8), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE\n    )\n    return [\n        np.squeeze(contour, axis=1)\n        for contour in contours\n        if contour.shape[0] >= MIN_POLYGON_POINT_COUNT\n    ]\n
"},{"location":"detection/utils/#polygon_to_xyxy","title":"polygon_to_xyxy","text":"

Converts a polygon represented by a NumPy array into a bounding box.

Parameters:

Name Type Description Default polygon np.ndarray

A polygon represented by a NumPy array of shape (N, 2), containing the x, y coordinates of the points.

required

Returns:

Type Description np.ndarray

np.ndarray: A 1D NumPy array containing the bounding box (x_min, y_min, x_max, y_max) of the input polygon.

Source code in supervision/detection/utils.py
def polygon_to_xyxy(polygon: np.ndarray) -> np.ndarray:\n\"\"\"\n    Converts a polygon represented by a NumPy array into a bounding box.\n\n    Parameters:\n        polygon (np.ndarray): A polygon represented by a NumPy array of shape `(N, 2)`,\n            containing the `x`, `y` coordinates of the points.\n\n    Returns:\n        np.ndarray: A 1D NumPy array containing the bounding box `(x_min, y_min, x_max, y_max)` of the input polygon.\n    \"\"\"\n    x_min, y_min = np.min(polygon, axis=0)\n    x_max, y_max = np.max(polygon, axis=0)\n    return np.array([x_min, y_min, x_max, y_max])\n
"},{"location":"detection/utils/#filter_polygons_by_area","title":"filter_polygons_by_area","text":"

Filters a list of polygons based on their area.

Parameters:

Name Type Description Default polygons List[np.ndarray]

A list of polygons, where each polygon is represented by a NumPy array of shape (N, 2), containing the x, y coordinates of the points.

required min_area Optional[float]

The minimum area threshold. Only polygons with an area greater than or equal to this value will be included in the output. If set to None, no minimum area constraint will be applied.

None max_area Optional[float]

The maximum area threshold. Only polygons with an area less than or equal to this value will be included in the output. If set to None, no maximum area constraint will be applied.

None

Returns:

Type Description List[np.ndarray]

List[np.ndarray]: A new list of polygons containing only those with areas within the specified thresholds.

Source code in supervision/detection/utils.py
def filter_polygons_by_area(\n    polygons: List[np.ndarray],\n    min_area: Optional[float] = None,\n    max_area: Optional[float] = None,\n) -> List[np.ndarray]:\n\"\"\"\n    Filters a list of polygons based on their area.\n\n    Parameters:\n        polygons (List[np.ndarray]): A list of polygons, where each polygon is represented by a NumPy array of shape `(N, 2)`,\n            containing the `x`, `y` coordinates of the points.\n        min_area (Optional[float]): The minimum area threshold. Only polygons with an area greater than or equal to this value\n            will be included in the output. If set to None, no minimum area constraint will be applied.\n        max_area (Optional[float]): The maximum area threshold. Only polygons with an area less than or equal to this value\n            will be included in the output. If set to None, no maximum area constraint will be applied.\n\n    Returns:\n        List[np.ndarray]: A new list of polygons containing only those with areas within the specified thresholds.\n    \"\"\"\n    if min_area is None and max_area is None:\n        return polygons\n    ares = [cv2.contourArea(polygon) for polygon in polygons]\n    return [\n        polygon\n        for polygon, area in zip(polygons, ares)\n        if (min_area is None or area >= min_area)\n        and (max_area is None or area <= max_area)\n    ]\n
"},{"location":"detection/tools/polygon_zone/","title":"Polygon Zone","text":""},{"location":"detection/tools/polygon_zone/#polygonzone","title":"PolygonZone","text":"

A class for defining a polygon-shaped zone within a frame for detecting objects.

Attributes:

Name Type Description polygon np.ndarray

A polygon represented by a numpy array of shape (N, 2), containing the x, y coordinates of the points.

frame_resolution_wh Tuple[int, int]

The frame resolution (width, height)

triggering_position Position

The position within the bounding box that triggers the zone (default: Position.BOTTOM_CENTER)

current_count int

The current count of detected objects within the zone

mask np.ndarray

The 2D bool mask for the polygon zone

Source code in supervision/detection/tools/polygon_zone.py
class PolygonZone:\n\"\"\"\n    A class for defining a polygon-shaped zone within a frame for detecting objects.\n\n    Attributes:\n        polygon (np.ndarray): A polygon represented by a numpy array of shape `(N, 2)`, containing the `x`, `y` coordinates of the points.\n        frame_resolution_wh (Tuple[int, int]): The frame resolution (width, height)\n        triggering_position (Position): The position within the bounding box that triggers the zone (default: Position.BOTTOM_CENTER)\n        current_count (int): The current count of detected objects within the zone\n        mask (np.ndarray): The 2D bool mask for the polygon zone\n    \"\"\"\n\n    def __init__(\n        self,\n        polygon: np.ndarray,\n        frame_resolution_wh: Tuple[int, int],\n        triggering_position: Position = Position.BOTTOM_CENTER,\n    ):\n        self.polygon = polygon.astype(int)\n        self.frame_resolution_wh = frame_resolution_wh\n        self.triggering_position = triggering_position\n        self.current_count = 0\n\n        width, height = frame_resolution_wh\n        self.mask = polygon_to_mask(\n            polygon=polygon, resolution_wh=(width + 1, height + 1)\n        )\n\n    def trigger(self, detections: Detections) -> np.ndarray:\n\"\"\"\n        Determines if the detections are within the polygon zone.\n\n        Parameters:\n            detections (Detections): The detections to be checked against the polygon zone\n\n        Returns:\n            np.ndarray: A boolean numpy array indicating if each detection is within the polygon zone\n        \"\"\"\n\n        clipped_xyxy = clip_boxes(\n            boxes_xyxy=detections.xyxy, frame_resolution_wh=self.frame_resolution_wh\n        )\n        clipped_detections = replace(detections, xyxy=clipped_xyxy)\n        clipped_anchors = np.ceil(\n            clipped_detections.get_anchor_coordinates(anchor=self.triggering_position)\n        ).astype(int)\n        is_in_zone = self.mask[clipped_anchors[:, 1], clipped_anchors[:, 0]]\n        self.current_count = np.sum(is_in_zone)\n        return is_in_zone.astype(bool)\n
"},{"location":"detection/tools/polygon_zone/#supervision.detection.tools.polygon_zone.PolygonZone.trigger","title":"trigger(detections)","text":"

Determines if the detections are within the polygon zone.

Parameters:

Name Type Description Default detections Detections

The detections to be checked against the polygon zone

required

Returns:

Type Description np.ndarray

np.ndarray: A boolean numpy array indicating if each detection is within the polygon zone

Source code in supervision/detection/tools/polygon_zone.py
def trigger(self, detections: Detections) -> np.ndarray:\n\"\"\"\n    Determines if the detections are within the polygon zone.\n\n    Parameters:\n        detections (Detections): The detections to be checked against the polygon zone\n\n    Returns:\n        np.ndarray: A boolean numpy array indicating if each detection is within the polygon zone\n    \"\"\"\n\n    clipped_xyxy = clip_boxes(\n        boxes_xyxy=detections.xyxy, frame_resolution_wh=self.frame_resolution_wh\n    )\n    clipped_detections = replace(detections, xyxy=clipped_xyxy)\n    clipped_anchors = np.ceil(\n        clipped_detections.get_anchor_coordinates(anchor=self.triggering_position)\n    ).astype(int)\n    is_in_zone = self.mask[clipped_anchors[:, 1], clipped_anchors[:, 0]]\n    self.current_count = np.sum(is_in_zone)\n    return is_in_zone.astype(bool)\n
"},{"location":"detection/tools/polygon_zone/#polygonzoneannotator","title":"PolygonZoneAnnotator","text":"

A class for annotating a polygon-shaped zone within a frame with a count of detected objects.

Attributes:

Name Type Description zone PolygonZone

The polygon zone to be annotated

color Color

The color to draw the polygon lines

thickness int

The thickness of the polygon lines, default is 2

text_color Color

The color of the text on the polygon, default is black

text_scale float

The scale of the text on the polygon, default is 0.5

text_thickness int

The thickness of the text on the polygon, default is 1

text_padding int

The padding around the text on the polygon, default is 10

font int

The font type for the text on the polygon, default is cv2.FONT_HERSHEY_SIMPLEX

center Tuple[int, int]

The center of the polygon for text placement

Source code in supervision/detection/tools/polygon_zone.py
class PolygonZoneAnnotator:\n\"\"\"\n    A class for annotating a polygon-shaped zone within a frame with a count of detected objects.\n\n    Attributes:\n        zone (PolygonZone): The polygon zone to be annotated\n        color (Color): The color to draw the polygon lines\n        thickness (int): The thickness of the polygon lines, default is 2\n        text_color (Color): The color of the text on the polygon, default is black\n        text_scale (float): The scale of the text on the polygon, default is 0.5\n        text_thickness (int): The thickness of the text on the polygon, default is 1\n        text_padding (int): The padding around the text on the polygon, default is 10\n        font (int): The font type for the text on the polygon, default is cv2.FONT_HERSHEY_SIMPLEX\n        center (Tuple[int, int]): The center of the polygon for text placement\n    \"\"\"\n\n    def __init__(\n        self,\n        zone: PolygonZone,\n        color: Color,\n        thickness: int = 2,\n        text_color: Color = Color.black(),\n        text_scale: float = 0.5,\n        text_thickness: int = 1,\n        text_padding: int = 10,\n    ):\n        self.zone = zone\n        self.color = color\n        self.thickness = thickness\n        self.text_color = text_color\n        self.text_scale = text_scale\n        self.text_thickness = text_thickness\n        self.text_padding = text_padding\n        self.font = cv2.FONT_HERSHEY_SIMPLEX\n        self.center = get_polygon_center(polygon=zone.polygon)\n\n    def annotate(self, scene: np.ndarray, label: Optional[str] = None) -> np.ndarray:\n\"\"\"\n        Annotates the polygon zone within a frame with a count of detected objects.\n\n        Parameters:\n            scene (np.ndarray): The image on which the polygon zone will be annotated\n            label (Optional[str]): An optional label for the count of detected objects within the polygon zone (default: None)\n\n        Returns:\n            np.ndarray: The image with the polygon zone and count of detected objects\n        \"\"\"\n        annotated_frame = draw_polygon(\n            scene=scene,\n            polygon=self.zone.polygon,\n            color=self.color,\n            thickness=self.thickness,\n        )\n\n        annotated_frame = draw_text(\n            scene=annotated_frame,\n            text=str(self.zone.current_count) if label is None else label,\n            text_anchor=self.center,\n            background_color=self.color,\n            text_color=self.text_color,\n            text_scale=self.text_scale,\n            text_thickness=self.text_thickness,\n            text_padding=self.text_padding,\n            text_font=self.font,\n        )\n\n        return annotated_frame\n
"},{"location":"detection/tools/polygon_zone/#supervision.detection.tools.polygon_zone.PolygonZoneAnnotator.annotate","title":"annotate(scene, label=None)","text":"

Annotates the polygon zone within a frame with a count of detected objects.

Parameters:

Name Type Description Default scene np.ndarray

The image on which the polygon zone will be annotated

required label Optional[str]

An optional label for the count of detected objects within the polygon zone (default: None)

None

Returns:

Type Description np.ndarray

np.ndarray: The image with the polygon zone and count of detected objects

Source code in supervision/detection/tools/polygon_zone.py
def annotate(self, scene: np.ndarray, label: Optional[str] = None) -> np.ndarray:\n\"\"\"\n    Annotates the polygon zone within a frame with a count of detected objects.\n\n    Parameters:\n        scene (np.ndarray): The image on which the polygon zone will be annotated\n        label (Optional[str]): An optional label for the count of detected objects within the polygon zone (default: None)\n\n    Returns:\n        np.ndarray: The image with the polygon zone and count of detected objects\n    \"\"\"\n    annotated_frame = draw_polygon(\n        scene=scene,\n        polygon=self.zone.polygon,\n        color=self.color,\n        thickness=self.thickness,\n    )\n\n    annotated_frame = draw_text(\n        scene=annotated_frame,\n        text=str(self.zone.current_count) if label is None else label,\n        text_anchor=self.center,\n        background_color=self.color,\n        text_color=self.text_color,\n        text_scale=self.text_scale,\n        text_thickness=self.text_thickness,\n        text_padding=self.text_padding,\n        text_font=self.font,\n    )\n\n    return annotated_frame\n
"},{"location":"draw/utils/","title":"Utils","text":""},{"location":"draw/utils/#draw_line","title":"draw_line","text":"

Draws a line on a given scene.

Parameters:

Name Type Description Default scene np.ndarray

The scene on which the line will be drawn

required start Point

The starting point of the line

required end Point

The end point of the line

required color Color

The color of the line

required thickness int

The thickness of the line

2

Returns:

Type Description np.ndarray

np.ndarray: The scene with the line drawn on it

Source code in supervision/draw/utils.py
def draw_line(\n    scene: np.ndarray, start: Point, end: Point, color: Color, thickness: int = 2\n) -> np.ndarray:\n\"\"\"\n    Draws a line on a given scene.\n\n    Parameters:\n        scene (np.ndarray): The scene on which the line will be drawn\n        start (Point): The starting point of the line\n        end (Point): The end point of the line\n        color (Color): The color of the line\n        thickness (int): The thickness of the line\n\n    Returns:\n        np.ndarray: The scene with the line drawn on it\n    \"\"\"\n    cv2.line(\n        scene,\n        start.as_xy_int_tuple(),\n        end.as_xy_int_tuple(),\n        color.as_bgr(),\n        thickness=thickness,\n    )\n    return scene\n
"},{"location":"draw/utils/#draw_rectangle","title":"draw_rectangle","text":"

Draws a rectangle on an image.

Parameters:

Name Type Description Default scene np.ndarray

The scene on which the rectangle will be drawn

required rect Rect

The rectangle to be drawn

required color Color

The color of the rectangle

required thickness int

The thickness of the rectangle border

2

Returns:

Type Description np.ndarray

np.ndarray: The scene with the rectangle drawn on it

Source code in supervision/draw/utils.py
def draw_rectangle(\n    scene: np.ndarray, rect: Rect, color: Color, thickness: int = 2\n) -> np.ndarray:\n\"\"\"\n    Draws a rectangle on an image.\n\n    Parameters:\n        scene (np.ndarray): The scene on which the rectangle will be drawn\n        rect (Rect): The rectangle to be drawn\n        color (Color): The color of the rectangle\n        thickness (int): The thickness of the rectangle border\n\n    Returns:\n        np.ndarray: The scene with the rectangle drawn on it\n    \"\"\"\n    cv2.rectangle(\n        scene,\n        rect.top_left.as_xy_int_tuple(),\n        rect.bottom_right.as_xy_int_tuple(),\n        color.as_bgr(),\n        thickness=thickness,\n    )\n    return scene\n
"},{"location":"draw/utils/#draw_filled_rectangle","title":"draw_filled_rectangle","text":"

Draws a filled rectangle on an image.

Parameters:

Name Type Description Default scene np.ndarray

The scene on which the rectangle will be drawn

required rect Rect

The rectangle to be drawn

required color Color

The color of the rectangle

required

Returns:

Type Description np.ndarray

np.ndarray: The scene with the rectangle drawn on it

Source code in supervision/draw/utils.py
def draw_filled_rectangle(scene: np.ndarray, rect: Rect, color: Color) -> np.ndarray:\n\"\"\"\n    Draws a filled rectangle on an image.\n\n    Parameters:\n        scene (np.ndarray): The scene on which the rectangle will be drawn\n        rect (Rect): The rectangle to be drawn\n        color (Color): The color of the rectangle\n\n    Returns:\n        np.ndarray: The scene with the rectangle drawn on it\n    \"\"\"\n    cv2.rectangle(\n        scene,\n        rect.top_left.as_xy_int_tuple(),\n        rect.bottom_right.as_xy_int_tuple(),\n        color.as_bgr(),\n        -1,\n    )\n    return scene\n
"},{"location":"draw/utils/#draw_polygon","title":"draw_polygon","text":"

Draw a polygon on a scene.

Parameters:

Name Type Description Default scene np.ndarray

The scene to draw the polygon on.

required polygon np.ndarray

The polygon to be drawn, given as a list of vertices.

required color Color

The color of the polygon.

required thickness int

The thickness of the polygon lines, by default 2.

2

Returns:

Type Description np.ndarray

np.ndarray: The scene with the polygon drawn on it.

Source code in supervision/draw/utils.py
def draw_polygon(\n    scene: np.ndarray, polygon: np.ndarray, color: Color, thickness: int = 2\n) -> np.ndarray:\n\"\"\"Draw a polygon on a scene.\n\n    Parameters:\n        scene (np.ndarray): The scene to draw the polygon on.\n        polygon (np.ndarray): The polygon to be drawn, given as a list of vertices.\n        color (Color): The color of the polygon.\n        thickness (int, optional): The thickness of the polygon lines, by default 2.\n\n    Returns:\n        np.ndarray: The scene with the polygon drawn on it.\n    \"\"\"\n    cv2.polylines(\n        scene, [polygon], isClosed=True, color=color.as_bgr(), thickness=thickness\n    )\n    return scene\n
"},{"location":"draw/utils/#draw_text","title":"draw_text","text":"

Draw text with background on a scene.

Parameters:

Name Type Description Default scene np.ndarray

A 2-dimensional numpy ndarray representing an image or scene.

required text str

The text to be drawn.

required text_anchor Point

The anchor point for the text, represented as a Point object with x and y attributes.

required text_color Color

The color of the text. Defaults to black.

Color.black() text_scale float

The scale of the text. Defaults to 0.5.

0.5 text_thickness int

The thickness of the text. Defaults to 1.

1 text_padding int

The amount of padding to add around the text when drawing a rectangle in the background. Defaults to 10.

10 text_font int

The font to use for the text. Defaults to cv2.FONT_HERSHEY_SIMPLEX.

cv2.FONT_HERSHEY_SIMPLEX background_color Color

The color of the background rectangle, if one is to be drawn. Defaults to None.

None

Returns:

Type Description np.ndarray

np.ndarray: The input scene with the text drawn on it.

Examples:

>>> scene = np.zeros((100, 100, 3), dtype=np.uint8)\n>>> text_anchor = Point(x=50, y=50)\n>>> scene = draw_text(scene=scene, text=\"Hello, world!\", text_anchor=text_anchor)\n
Source code in supervision/draw/utils.py
def draw_text(\n    scene: np.ndarray,\n    text: str,\n    text_anchor: Point,\n    text_color: Color = Color.black(),\n    text_scale: float = 0.5,\n    text_thickness: int = 1,\n    text_padding: int = 10,\n    text_font: int = cv2.FONT_HERSHEY_SIMPLEX,\n    background_color: Optional[Color] = None,\n) -> np.ndarray:\n\"\"\"\n    Draw text with background on a scene.\n\n    Parameters:\n        scene (np.ndarray): A 2-dimensional numpy ndarray representing an image or scene.\n        text (str): The text to be drawn.\n        text_anchor (Point): The anchor point for the text, represented as a Point object with x and y attributes.\n        text_color (Color, optional): The color of the text. Defaults to black.\n        text_scale (float, optional): The scale of the text. Defaults to 0.5.\n        text_thickness (int, optional): The thickness of the text. Defaults to 1.\n        text_padding (int, optional): The amount of padding to add around the text when drawing a rectangle in the background. Defaults to 10.\n        text_font (int, optional): The font to use for the text. Defaults to cv2.FONT_HERSHEY_SIMPLEX.\n        background_color (Color, optional): The color of the background rectangle, if one is to be drawn. Defaults to None.\n\n    Returns:\n        np.ndarray: The input scene with the text drawn on it.\n\n    Examples:\n        ```python\n        >>> scene = np.zeros((100, 100, 3), dtype=np.uint8)\n        >>> text_anchor = Point(x=50, y=50)\n        >>> scene = draw_text(scene=scene, text=\"Hello, world!\", text_anchor=text_anchor)\n        ```\n    \"\"\"\n    text_width, text_height = cv2.getTextSize(\n        text=text,\n        fontFace=text_font,\n        fontScale=text_scale,\n        thickness=text_thickness,\n    )[0]\n    text_rect = Rect(\n        x=text_anchor.x - text_width // 2,\n        y=text_anchor.y - text_height // 2,\n        width=text_width,\n        height=text_height,\n    ).pad(text_padding)\n\n    if background_color is not None:\n        scene = draw_filled_rectangle(\n            scene=scene, rect=text_rect, color=background_color\n        )\n\n    cv2.putText(\n        img=scene,\n        text=text,\n        org=(text_anchor.x - text_width // 2, text_anchor.y + text_height // 2),\n        fontFace=text_font,\n        fontScale=text_scale,\n        color=text_color.as_bgr(),\n        thickness=text_thickness,\n        lineType=cv2.LINE_AA,\n    )\n    return scene\n
"},{"location":"quickstart/detections/","title":"Detections","text":""},{"location":"quickstart/detections/#advanced-filtering","title":"advanced filtering","text":"

The advanced filtering capabilities of the Detections class offer users a versatile and efficient way to narrow down and refine object detections. This section outlines various filtering methods, including filtering by specific class or a set of classes, confidence, object area, bounding box area, relative area, box dimensions, and designated zones. Each method is demonstrated with concise code examples to provide users with a clear understanding of how to implement the filters in their applications.

"},{"location":"quickstart/detections/#by-specific-class","title":"by specific class","text":"

Allows you to select detections that belong only to one selected class.

AfterBefore
import supervision as sv\n\ndetections = sv.Detections(...)\ndetections = detections[detections.class_id == 0]\n

import supervision as sv\n\ndetections = sv.Detections(...)\ndetections = detections[detections.class_id == 0]\n

"},{"location":"quickstart/detections/#by-set-of-classes","title":"by set of classes","text":"

Allows you to select detections that belong only to selected set of classes.

AfterBefore
import numpy as np\nimport supervision as sv\n\nselected_classes = [0, 2, 3]\ndetections = sv.Detections(...)\ndetections = detections[np.isin(detections.class_id, selected_classes)]\n

import numpy as np\nimport supervision as sv\n\nclass_id = [0, 2, 3]\ndetections = sv.Detections(...)\ndetections = detections[np.isin(detections.class_id, class_id)]\n

"},{"location":"quickstart/detections/#by-confidence","title":"by confidence","text":"

Allows you to select detections with specific confidence value, for example higher than selected threshold.

AfterBefore
import supervision as sv\n\ndetections = sv.Detections(...)\ndetections = detections[detections.confidence > 0.5]\n

import supervision as sv\n\ndetections = sv.Detections(...)\ndetections = detections[detections.confidence > 0.5]\n

"},{"location":"quickstart/detections/#by-area","title":"by area","text":"

Allows you to select detections based on their size. We define the area as the number of pixels occupied by the detection in the image. In the example below, we have sifted out the detections that are too small.

AfterBefore
import supervision as sv\n\ndetections = sv.Detections(...)\ndetections = detections[detections.area > 1000]\n

import supervision as sv\n\ndetections = sv.Detections(...)\ndetections = detections[detections.area > 1000]\n

"},{"location":"quickstart/detections/#by-relative-area","title":"by relative area","text":"

Allows you to select detections based on their size in relation to the size of whole image. Sometimes the concept of detection size changes depending on the image. Detection occupying 10000 square px can be large on a 1280x720 image but small on a 3840x2160 image. In such cases, we can filter out detections based on the percentage of the image area occupied by them. In the example below, we remove too large detections.

AfterBefore
import supervision as sv\n\nimage = ...\nheight, width, channels = image.shape\nimage_area = height * width\n\ndetections = sv.Detections(...)\ndetections = detections[(detections.area / image_area) < 0.8]\n

import supervision as sv\n\nimage = ...\nheight, width, channels = image.shape\nimage_area = height * width\n\ndetections = sv.Detections(...)\ndetections = detections[(detections.area / image_area) < 0.8]\n

"},{"location":"quickstart/detections/#by-box-dimensions","title":"by box dimensions","text":"

Allows you to select detections based on their dimensions. The size of the bounding box, as well as its coordinates, can be criteria for rejecting detection. Implementing such filtering requires a bit of custom code but is relatively simple and fast.

AfterBefore
import supervision as sv\n\ndetections = sv.Detections(...)\nw = detections.xyxy[:, 2] - detections.xyxy[:, 0]\nh = detections.xyxy[:, 3] - detections.xyxy[:, 1]\ndetections = detections[(w > 200) & (h > 200)]\n

import supervision as sv\n\ndetections = sv.Detections(...)\nw = detections.xyxy[:, 2] - detections.xyxy[:, 0]\nh = detections.xyxy[:, 3] - detections.xyxy[:, 1]\ndetections = detections[(w > 200) & (h > 200)]\n

"},{"location":"quickstart/detections/#by-polygonzone","title":"by PolygonZone","text":"

Allows you to use Detections in combination with PolygonZone to weed out bounding boxes that are in and out of the zone. In the example below you can see how to filter out all detections located in the lower part of the image.

AfterBefore
import supervision as sv\n\nzone = sv.PolygonZone(...)\ndetections = sv.Detections(...)\nmask = zone.trigger(detections=detections)\ndetections = detections[mask]\n

import supervision as sv\n\nzone = sv.PolygonZone(...)\ndetections = sv.Detections(...)\nmask = zone.trigger(detections=detections)\ndetections = detections[mask]\n

"},{"location":"quickstart/detections/#by-mixed-conditions","title":"by mixed conditions","text":"

Detections' greatest strength, however, is that you can build arbitrarily complex logical conditions by simply combining separate conditions using & or |.

AfterBefore
import supervision as sv\n\nzone = sv.PolygonZone(...)\ndetections = sv.Detections(...)\nmask = zone.trigger(detections=detections)\ndetections = detections[(detections.confidence > 0.7) & mask]\n

import supervision as sv\n\nzone = sv.PolygonZone(...)\ndetections = sv.Detections(...)\nmask = zone.trigger(detections=detections)\ndetections = detections[mask]\n

"},{"location":"utils/file/","title":"File","text":""},{"location":"utils/file/#list_files_with_extensions","title":"list_files_with_extensions","text":"

List files in a directory with specified extensions or all files if no extensions are provided.

Parameters:

Name Type Description Default directory Union[str, Path]

The directory path as a string or Path object.

required extensions Optional[List[str]]

A list of file extensions to filter. Default is None, which lists all files.

None

Returns:

Type Description List[Path]

A list of Path objects for the matching files.

Examples:

>>> import supervision as sv\n\n>>> # List all files in the directory\n>>> files = sv.list_files_with_extensions(directory='my_directory')\n\n>>> # List only files with '.txt' and '.md' extensions\n>>> files = sv.list_files_with_extensions(directory='my_directory', extensions=['txt', 'md'])\n
Source code in supervision/utils/file.py
def list_files_with_extensions(\n    directory: Union[str, Path], extensions: Optional[List[str]] = None\n) -> List[Path]:\n\"\"\"\n    List files in a directory with specified extensions or all files if no extensions are provided.\n\n    Args:\n        directory (Union[str, Path]): The directory path as a string or Path object.\n        extensions (Optional[List[str]]): A list of file extensions to filter. Default is None, which lists all files.\n\n    Returns:\n        (List[Path]): A list of Path objects for the matching files.\n\n    Examples:\n        ```python\n        >>> import supervision as sv\n\n        >>> # List all files in the directory\n        >>> files = sv.list_files_with_extensions(directory='my_directory')\n\n        >>> # List only files with '.txt' and '.md' extensions\n        >>> files = sv.list_files_with_extensions(directory='my_directory', extensions=['txt', 'md'])\n        ```\n    \"\"\"\n    directory = Path(directory)\n    files_with_extensions = []\n\n    if extensions is not None:\n        for ext in extensions:\n            files_with_extensions.extend(directory.glob(f\"*.{ext}\"))\n    else:\n        files_with_extensions.extend(directory.glob(\"*\"))\n\n    return files_with_extensions\n
"},{"location":"utils/image/","title":"Image","text":""},{"location":"utils/image/#imagesink","title":"ImageSink","text":"Source code in supervision/utils/image.py
class ImageSink:\n    def __init__(\n        self,\n        target_dir_path: str,\n        overwrite: bool = False,\n        image_name_pattern: str = \"image_{:05d}.png\",\n    ):\n\"\"\"\n        Initialize a context manager for saving images.\n\n        Args:\n            target_dir_path (str): The target directory where images will be saved.\n            overwrite (bool, optional): Whether to overwrite the existing directory. Defaults to False.\n            image_name_pattern (str, optional): The image file name pattern. Defaults to \"image_{:05d}.png\".\n\n        Examples:\n            ```python\n            >>> import supervision as sv\n\n            >>> with sv.ImageSink(target_dir_path='target/directory/path', overwrite=True) as sink:\n            ...     for image in sv.get_video_frames_generator(source_path='source_video.mp4', stride=2):\n            ...         sink.save_image(image=image)\n            ```\n        \"\"\"\n        self.target_dir_path = target_dir_path\n        self.overwrite = overwrite\n        self.image_name_pattern = image_name_pattern\n        self.image_count = 0\n\n    def __enter__(self):\n        if os.path.exists(self.target_dir_path):\n            if self.overwrite:\n                shutil.rmtree(self.target_dir_path)\n                os.makedirs(self.target_dir_path)\n        else:\n            os.makedirs(self.target_dir_path)\n\n        return self\n\n    def save_image(self, image: np.ndarray, image_name: Optional[str] = None):\n\"\"\"\n        Save a given image in the target directory.\n\n        Args:\n            image (np.ndarray): The image to be saved.\n            image_name (str, optional): The name to use for the saved image. If not provided, a name will be generated using the `image_name_pattern`.\n        \"\"\"\n        if image_name is None:\n            image_name = self.image_name_pattern.format(self.image_count)\n\n        image_path = os.path.join(self.target_dir_path, image_name)\n        cv2.imwrite(image_path, image)\n        self.image_count += 1\n\n    def __exit__(self, exc_type, exc_value, exc_traceback):\n        pass\n
"},{"location":"utils/image/#supervision.utils.image.ImageSink.__init__","title":"__init__(target_dir_path, overwrite=False, image_name_pattern='image_{:05d}.png')","text":"

Initialize a context manager for saving images.

Parameters:

Name Type Description Default target_dir_path str

The target directory where images will be saved.

required overwrite bool

Whether to overwrite the existing directory. Defaults to False.

False image_name_pattern str

The image file name pattern. Defaults to \"image_{:05d}.png\".

'image_{:05d}.png'

Examples:

>>> import supervision as sv\n\n>>> with sv.ImageSink(target_dir_path='target/directory/path', overwrite=True) as sink:\n...     for image in sv.get_video_frames_generator(source_path='source_video.mp4', stride=2):\n...         sink.save_image(image=image)\n
Source code in supervision/utils/image.py
def __init__(\n    self,\n    target_dir_path: str,\n    overwrite: bool = False,\n    image_name_pattern: str = \"image_{:05d}.png\",\n):\n\"\"\"\n    Initialize a context manager for saving images.\n\n    Args:\n        target_dir_path (str): The target directory where images will be saved.\n        overwrite (bool, optional): Whether to overwrite the existing directory. Defaults to False.\n        image_name_pattern (str, optional): The image file name pattern. Defaults to \"image_{:05d}.png\".\n\n    Examples:\n        ```python\n        >>> import supervision as sv\n\n        >>> with sv.ImageSink(target_dir_path='target/directory/path', overwrite=True) as sink:\n        ...     for image in sv.get_video_frames_generator(source_path='source_video.mp4', stride=2):\n        ...         sink.save_image(image=image)\n        ```\n    \"\"\"\n    self.target_dir_path = target_dir_path\n    self.overwrite = overwrite\n    self.image_name_pattern = image_name_pattern\n    self.image_count = 0\n
"},{"location":"utils/image/#supervision.utils.image.ImageSink.save_image","title":"save_image(image, image_name=None)","text":"

Save a given image in the target directory.

Parameters:

Name Type Description Default image np.ndarray

The image to be saved.

required image_name str

The name to use for the saved image. If not provided, a name will be generated using the image_name_pattern.

None Source code in supervision/utils/image.py
def save_image(self, image: np.ndarray, image_name: Optional[str] = None):\n\"\"\"\n    Save a given image in the target directory.\n\n    Args:\n        image (np.ndarray): The image to be saved.\n        image_name (str, optional): The name to use for the saved image. If not provided, a name will be generated using the `image_name_pattern`.\n    \"\"\"\n    if image_name is None:\n        image_name = self.image_name_pattern.format(self.image_count)\n\n    image_path = os.path.join(self.target_dir_path, image_name)\n    cv2.imwrite(image_path, image)\n    self.image_count += 1\n
"},{"location":"utils/image/#crop","title":"crop","text":"

Crops the given image based on the given bounding box.

Parameters:

Name Type Description Default image np.ndarray

The image to be cropped, represented as a numpy array.

required xyxy np.ndarray

A numpy array containing the bounding box coordinates in the format (x1, y1, x2, y2).

required

Returns:

Type Description np.ndarray

The cropped image as a numpy array.

Examples:

>>> import supervision as sv\n\n>>> detection = sv.Detections(...)\n>>> with sv.ImageSink(target_dir_path='target/directory/path') as sink:\n...     for xyxy in detection.xyxy:\n...         cropped_image = sv.crop(image=image, xyxy=xyxy)\n...         sink.save_image(image=image)\n
Source code in supervision/utils/image.py
def crop(image: np.ndarray, xyxy: np.ndarray) -> np.ndarray:\n\"\"\"\n    Crops the given image based on the given bounding box.\n\n    Args:\n        image (np.ndarray): The image to be cropped, represented as a numpy array.\n        xyxy (np.ndarray): A numpy array containing the bounding box coordinates in the format (x1, y1, x2, y2).\n\n    Returns:\n        (np.ndarray): The cropped image as a numpy array.\n\n    Examples:\n        ```python\n        >>> import supervision as sv\n\n        >>> detection = sv.Detections(...)\n        >>> with sv.ImageSink(target_dir_path='target/directory/path') as sink:\n        ...     for xyxy in detection.xyxy:\n        ...         cropped_image = sv.crop(image=image, xyxy=xyxy)\n        ...         sink.save_image(image=image)\n        ```\n    \"\"\"\n\n    xyxy = np.round(xyxy).astype(int)\n    x1, y1, x2, y2 = xyxy\n    cropped_img = image[y1:y2, x1:x2]\n    return cropped_img\n
"},{"location":"utils/notebook/","title":"Notebook","text":""},{"location":"utils/notebook/#plot_image","title":"plot_image","text":"

Plots image using matplotlib.

Parameters:

Name Type Description Default image np.ndarray

The frame to be displayed.

required size Tuple[int, int]

The size of the plot.

(12, 12) cmap str

the colormap to use for single channel images.

'gray'

Examples:

>>> import cv2\n>>> import supervision as sv\n\n>>> image = cv2.imread(\"path/to/image.jpg\")\n\n%matplotlib inline\n>>> sv.plot_image(image=image, size=(16, 16))\n
Source code in supervision/utils/notebook.py
def plot_image(\n    image: np.ndarray, size: Tuple[int, int] = (12, 12), cmap: Optional[str] = \"gray\"\n) -> None:\n\"\"\"\n    Plots image using matplotlib.\n\n    Args:\n        image (np.ndarray): The frame to be displayed.\n        size (Tuple[int, int]): The size of the plot.\n        cmap (str): the colormap to use for single channel images.\n\n    Examples:\n        ```python\n        >>> import cv2\n        >>> import supervision as sv\n\n        >>> image = cv2.imread(\"path/to/image.jpg\")\n\n        %matplotlib inline\n        >>> sv.plot_image(image=image, size=(16, 16))\n        ```\n    \"\"\"\n    plt.figure(figsize=size)\n\n    if image.ndim == 2:\n        plt.imshow(image, cmap=cmap)\n    else:\n        plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))\n\n    plt.axis(\"off\")\n    plt.show()\n
"},{"location":"utils/notebook/#plot_images_grid","title":"plot_images_grid","text":"

Plots images in a grid using matplotlib.

Parameters:

Name Type Description Default images List[np.ndarray]

A list of images as numpy arrays.

required grid_size Tuple[int, int]

A tuple specifying the number of rows and columns for the grid.

required titles Optional[List[str]]

A list of titles for each image. Defaults to None.

None size Tuple[int, int]

A tuple specifying the width and height of the entire plot in inches.

(12, 12) cmap str

the colormap to use for single channel images.

'gray'

Raises:

Type Description ValueError

If the number of images exceeds the grid size.

Examples:

>>> import cv2\n>>> import supervision as sv\n\n>>> image1 = cv2.imread(\"path/to/image1.jpg\")\n>>> image2 = cv2.imread(\"path/to/image2.jpg\")\n>>> image3 = cv2.imread(\"path/to/image3.jpg\")\n\n>>> images = [image1, image2, image3]\n>>> titles = [\"Image 1\", \"Image 2\", \"Image 3\"]\n\n%matplotlib inline\n>>> plot_images_grid(images, grid_size=(2, 2), titles=titles, size=(16, 16))\n
Source code in supervision/utils/notebook.py
def plot_images_grid(\n    images: List[np.ndarray],\n    grid_size: Tuple[int, int],\n    titles: Optional[List[str]] = None,\n    size: Tuple[int, int] = (12, 12),\n    cmap: Optional[str] = \"gray\",\n) -> None:\n\"\"\"\n    Plots images in a grid using matplotlib.\n\n    Args:\n       images (List[np.ndarray]): A list of images as numpy arrays.\n       grid_size (Tuple[int, int]): A tuple specifying the number of rows and columns for the grid.\n       titles (Optional[List[str]]): A list of titles for each image. Defaults to None.\n       size (Tuple[int, int]): A tuple specifying the width and height of the entire plot in inches.\n       cmap (str): the colormap to use for single channel images.\n\n    Raises:\n       ValueError: If the number of images exceeds the grid size.\n\n    Examples:\n        ```python\n        >>> import cv2\n        >>> import supervision as sv\n\n        >>> image1 = cv2.imread(\"path/to/image1.jpg\")\n        >>> image2 = cv2.imread(\"path/to/image2.jpg\")\n        >>> image3 = cv2.imread(\"path/to/image3.jpg\")\n\n        >>> images = [image1, image2, image3]\n        >>> titles = [\"Image 1\", \"Image 2\", \"Image 3\"]\n\n        %matplotlib inline\n        >>> plot_images_grid(images, grid_size=(2, 2), titles=titles, size=(16, 16))\n        ```\n    \"\"\"\n    nrows, ncols = grid_size\n\n    if len(images) > nrows * ncols:\n        raise ValueError(\n            \"The number of images exceeds the grid size. Please increase the grid size or reduce the number of images.\"\n        )\n\n    fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=size)\n\n    for idx, ax in enumerate(axes.flat):\n        if idx < len(images):\n            if images[idx].ndim == 2:\n                ax.imshow(images[idx], cmap=cmap)\n            else:\n                ax.imshow(cv2.cvtColor(images[idx], cv2.COLOR_BGR2RGB))\n\n            if titles is not None and idx < len(titles):\n                ax.set_title(titles[idx])\n\n        ax.axis(\"off\")\n    plt.show()\n
"},{"location":"utils/video/","title":"Video","text":""},{"location":"utils/video/#videoinfo","title":"VideoInfo","text":"

A class to store video information, including width, height, fps and total number of frames.

Attributes:

Name Type Description width int

width of the video in pixels

height int

height of the video in pixels

fps int

frames per second of the video

total_frames int

total number of frames in the video, default is None

Examples:

>>> import supervision as sv\n\n>>> video_info = sv.VideoInfo.from_video_path(video_path='video.mp4')\n\n>>> video_info\nVideoInfo(width=3840, height=2160, fps=25, total_frames=538)\n\n>>> video_info.resolution_wh\n(3840, 2160)\n
Source code in supervision/utils/video.py
@dataclass\nclass VideoInfo:\n\"\"\"\n    A class to store video information, including width, height, fps and total number of frames.\n\n    Attributes:\n        width (int): width of the video in pixels\n        height (int): height of the video in pixels\n        fps (int): frames per second of the video\n        total_frames (int, optional): total number of frames in the video, default is None\n\n    Examples:\n        ```python\n        >>> import supervision as sv\n\n        >>> video_info = sv.VideoInfo.from_video_path(video_path='video.mp4')\n\n        >>> video_info\n        VideoInfo(width=3840, height=2160, fps=25, total_frames=538)\n\n        >>> video_info.resolution_wh\n        (3840, 2160)\n        ```\n    \"\"\"\n\n    width: int\n    height: int\n    fps: int\n    total_frames: Optional[int] = None\n\n    @classmethod\n    def from_video_path(cls, video_path: str) -> VideoInfo:\n        video = cv2.VideoCapture(video_path)\n        if not video.isOpened():\n            raise Exception(f\"Could not open video at {video_path}\")\n\n        width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))\n        height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))\n        fps = int(video.get(cv2.CAP_PROP_FPS))\n        total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))\n        video.release()\n        return VideoInfo(width, height, fps, total_frames)\n\n    @property\n    def resolution_wh(self) -> Tuple[int, int]:\n        return self.width, self.height\n
"},{"location":"utils/video/#videosink","title":"VideoSink","text":"

Context manager that saves video frames to a file using OpenCV.

Attributes:

Name Type Description target_path str

The path to the output file where the video will be saved.

video_info VideoInfo

Information about the video resolution, fps, and total frame count.

Example
>>> import supervision as sv\n\n>>> video_info = sv.VideoInfo.from_video_path(video_path='source_video.mp4')\n\n>>> with sv.VideoSink(target_path='target_video.mp4', video_info=video_info) as sink:\n...     for frame in get_video_frames_generator(source_path='source_video.mp4', stride=2):\n...         sink.write_frame(frame=frame)\n
Source code in supervision/utils/video.py
class VideoSink:\n\"\"\"\n    Context manager that saves video frames to a file using OpenCV.\n\n    Attributes:\n        target_path (str): The path to the output file where the video will be saved.\n        video_info (VideoInfo): Information about the video resolution, fps, and total frame count.\n\n    Example:\n        ```python\n        >>> import supervision as sv\n\n        >>> video_info = sv.VideoInfo.from_video_path(video_path='source_video.mp4')\n\n        >>> with sv.VideoSink(target_path='target_video.mp4', video_info=video_info) as sink:\n        ...     for frame in get_video_frames_generator(source_path='source_video.mp4', stride=2):\n        ...         sink.write_frame(frame=frame)\n        ```\n    \"\"\"\n\n    def __init__(self, target_path: str, video_info: VideoInfo):\n        self.target_path = target_path\n        self.video_info = video_info\n        self.__fourcc = cv2.VideoWriter_fourcc(*\"mp4v\")\n        self.__writer = None\n\n    def __enter__(self):\n        self.__writer = cv2.VideoWriter(\n            self.target_path,\n            self.__fourcc,\n            self.video_info.fps,\n            self.video_info.resolution_wh,\n        )\n        return self\n\n    def write_frame(self, frame: np.ndarray):\n        self.__writer.write(frame)\n\n    def __exit__(self, exc_type, exc_value, exc_traceback):\n        self.__writer.release()\n
"},{"location":"utils/video/#get_video_frames_generator","title":"get_video_frames_generator","text":"

Get a generator that yields the frames of the video.

Parameters:

Name Type Description Default source_path str

The path of the video file.

required stride int

Indicates the interval at which frames are returned, skipping stride - 1 frames between each.

1

Returns:

Type Description Generator[np.ndarray, None, None]

A generator that yields the frames of the video.

Examples:

>>> import supervision as sv\n\n>>> for frame in sv.get_video_frames_generator(source_path='source_video.mp4', stride=2):\n...     ...\n
Source code in supervision/utils/video.py
def get_video_frames_generator(\n    source_path: str, stride: int = 1\n) -> Generator[np.ndarray, None, None]:\n\"\"\"\n    Get a generator that yields the frames of the video.\n\n    Args:\n        source_path (str): The path of the video file.\n        stride (int): Indicates the interval at which frames are returned, skipping stride - 1 frames between each.\n\n    Returns:\n        (Generator[np.ndarray, None, None]): A generator that yields the frames of the video.\n\n    Examples:\n        ```python\n        >>> import supervision as sv\n\n        >>> for frame in sv.get_video_frames_generator(source_path='source_video.mp4', stride=2):\n        ...     ...\n        ```\n    \"\"\"\n    video = cv2.VideoCapture(source_path)\n    if not video.isOpened():\n        raise Exception(f\"Could not open video at {source_path}\")\n\n    frame_count = 0\n    success, frame = video.read()\n    while success:\n        if frame_count % stride == 0:\n            yield frame\n        success, frame = video.read()\n        frame_count += 1\n\n    video.release()\n
"},{"location":"utils/video/#process_video","title":"process_video","text":"

Process a video file by applying a callback function on each frame and saving the result to a target video file.

Parameters:

Name Type Description Default source_path str

The path to the source video file.

required target_path str

The path to the target video file.

required callback Callable[[np.ndarray, int], np.ndarray]

A function that takes in a numpy ndarray representation of a video frame and an int index of the frame and returns a processed numpy ndarray representation of the frame.

required

Examples:

>>> from supervision import process_video\n\n>>> def process_frame(scene: np.ndarray) -> np.ndarray:\n...     ...\n\n>>> process_video(\n...     source_path='source_video.mp4',\n...     target_path='target_video.mp4',\n...     callback=process_frame\n... )\n
Source code in supervision/utils/video.py
def process_video(\n    source_path: str,\n    target_path: str,\n    callback: Callable[[np.ndarray, int], np.ndarray],\n) -> None:\n\"\"\"\n    Process a video file by applying a callback function on each frame and saving the result to a target video file.\n\n    Args:\n        source_path (str): The path to the source video file.\n        target_path (str): The path to the target video file.\n        callback (Callable[[np.ndarray, int], np.ndarray]): A function that takes in a numpy ndarray representation of a video frame and an int index of the frame and returns a processed numpy ndarray representation of the frame.\n\n    Examples:\n        ```python\n        >>> from supervision import process_video\n\n        >>> def process_frame(scene: np.ndarray) -> np.ndarray:\n        ...     ...\n\n        >>> process_video(\n        ...     source_path='source_video.mp4',\n        ...     target_path='target_video.mp4',\n        ...     callback=process_frame\n        ... )\n        ```\n    \"\"\"\n    source_video_info = VideoInfo.from_video_path(video_path=source_path)\n    with VideoSink(target_path=target_path, video_info=source_video_info) as sink:\n        for index, frame in enumerate(\n            get_video_frames_generator(source_path=source_path)\n        ):\n            result_frame = callback(frame, index)\n            sink.write_frame(frame=result_frame)\n
"}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 000000000..ca428ea00 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,73 @@ + + + + https://roboflow.github.io/supervision/ + 2023-06-27 + daily + + + https://roboflow.github.io/supervision/changelog/ + 2023-06-27 + daily + + + https://roboflow.github.io/supervision/classification/core/ + 2023-06-27 + daily + + + https://roboflow.github.io/supervision/dataset/core/ + 2023-06-27 + daily + + + https://roboflow.github.io/supervision/detection/annotate/ + 2023-06-27 + daily + + + https://roboflow.github.io/supervision/detection/core/ + 2023-06-27 + daily + + + https://roboflow.github.io/supervision/detection/utils/ + 2023-06-27 + daily + + + https://roboflow.github.io/supervision/detection/tools/polygon_zone/ + 2023-06-27 + daily + + + https://roboflow.github.io/supervision/draw/utils/ + 2023-06-27 + daily + + + https://roboflow.github.io/supervision/quickstart/detections/ + 2023-06-27 + daily + + + https://roboflow.github.io/supervision/utils/file/ + 2023-06-27 + daily + + + https://roboflow.github.io/supervision/utils/image/ + 2023-06-27 + daily + + + https://roboflow.github.io/supervision/utils/notebook/ + 2023-06-27 + daily + + + https://roboflow.github.io/supervision/utils/video/ + 2023-06-27 + daily + + \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz new file mode 100644 index 000000000..31b33a780 Binary files /dev/null and b/sitemap.xml.gz differ diff --git a/stylesheets/extra.css b/stylesheets/extra.css new file mode 100644 index 000000000..81d3860e0 --- /dev/null +++ b/stylesheets/extra.css @@ -0,0 +1,4 @@ +:root { + --md-primary-fg-color: #8315F9; + --md-accent-fg-color: #00FFCE; +} \ No newline at end of file diff --git a/utils/file/index.html b/utils/file/index.html new file mode 100644 index 000000000..27fa8e003 --- /dev/null +++ b/utils/file/index.html @@ -0,0 +1,1092 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + File - Supervision + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

File

+ +

list_files_with_extensions

+ + +
+ + + +
+ +

List files in a directory with specified extensions or all files if no extensions are provided.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
directory + Union[str, Path] + +
+

The directory path as a string or Path object.

+
+
+ required +
extensions + Optional[List[str]] + +
+

A list of file extensions to filter. Default is None, which lists all files.

+
+
+ None +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ List[Path] + +
+

A list of Path objects for the matching files.

+
+
+ +

Examples:

+
>>> import supervision as sv
+
+>>> # List all files in the directory
+>>> files = sv.list_files_with_extensions(directory='my_directory')
+
+>>> # List only files with '.txt' and '.md' extensions
+>>> files = sv.list_files_with_extensions(directory='my_directory', extensions=['txt', 'md'])
+
+ +
+ Source code in supervision/utils/file.py +
19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
def list_files_with_extensions(
+    directory: Union[str, Path], extensions: Optional[List[str]] = None
+) -> List[Path]:
+    """
+    List files in a directory with specified extensions or all files if no extensions are provided.
+
+    Args:
+        directory (Union[str, Path]): The directory path as a string or Path object.
+        extensions (Optional[List[str]]): A list of file extensions to filter. Default is None, which lists all files.
+
+    Returns:
+        (List[Path]): A list of Path objects for the matching files.
+
+    Examples:
+        ```python
+        >>> import supervision as sv
+
+        >>> # List all files in the directory
+        >>> files = sv.list_files_with_extensions(directory='my_directory')
+
+        >>> # List only files with '.txt' and '.md' extensions
+        >>> files = sv.list_files_with_extensions(directory='my_directory', extensions=['txt', 'md'])
+        ```
+    """
+    directory = Path(directory)
+    files_with_extensions = []
+
+    if extensions is not None:
+        for ext in extensions:
+            files_with_extensions.extend(directory.glob(f"*.{ext}"))
+    else:
+        files_with_extensions.extend(directory.glob("*"))
+
+    return files_with_extensions
+
+
+
+ +
+ + + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/utils/image/index.html b/utils/image/index.html new file mode 100644 index 000000000..65619e448 --- /dev/null +++ b/utils/image/index.html @@ -0,0 +1,1509 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Image - Supervision + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Image

+ +

ImageSink

+ + +
+ + + +
+ + + +
+ Source code in supervision/utils/image.py +
38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
class ImageSink:
+    def __init__(
+        self,
+        target_dir_path: str,
+        overwrite: bool = False,
+        image_name_pattern: str = "image_{:05d}.png",
+    ):
+        """
+        Initialize a context manager for saving images.
+
+        Args:
+            target_dir_path (str): The target directory where images will be saved.
+            overwrite (bool, optional): Whether to overwrite the existing directory. Defaults to False.
+            image_name_pattern (str, optional): The image file name pattern. Defaults to "image_{:05d}.png".
+
+        Examples:
+            ```python
+            >>> import supervision as sv
+
+            >>> with sv.ImageSink(target_dir_path='target/directory/path', overwrite=True) as sink:
+            ...     for image in sv.get_video_frames_generator(source_path='source_video.mp4', stride=2):
+            ...         sink.save_image(image=image)
+            ```
+        """
+        self.target_dir_path = target_dir_path
+        self.overwrite = overwrite
+        self.image_name_pattern = image_name_pattern
+        self.image_count = 0
+
+    def __enter__(self):
+        if os.path.exists(self.target_dir_path):
+            if self.overwrite:
+                shutil.rmtree(self.target_dir_path)
+                os.makedirs(self.target_dir_path)
+        else:
+            os.makedirs(self.target_dir_path)
+
+        return self
+
+    def save_image(self, image: np.ndarray, image_name: Optional[str] = None):
+        """
+        Save a given image in the target directory.
+
+        Args:
+            image (np.ndarray): The image to be saved.
+            image_name (str, optional): The name to use for the saved image. If not provided, a name will be generated using the `image_name_pattern`.
+        """
+        if image_name is None:
+            image_name = self.image_name_pattern.format(self.image_count)
+
+        image_path = os.path.join(self.target_dir_path, image_name)
+        cv2.imwrite(image_path, image)
+        self.image_count += 1
+
+    def __exit__(self, exc_type, exc_value, exc_traceback):
+        pass
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+__init__(target_dir_path, overwrite=False, image_name_pattern='image_{:05d}.png') + +

+ + +
+ +

Initialize a context manager for saving images.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
target_dir_path + str + +
+

The target directory where images will be saved.

+
+
+ required +
overwrite + bool + +
+

Whether to overwrite the existing directory. Defaults to False.

+
+
+ False +
image_name_pattern + str + +
+

The image file name pattern. Defaults to "image_{:05d}.png".

+
+
+ 'image_{:05d}.png' +
+ +

Examples:

+
>>> import supervision as sv
+
+>>> with sv.ImageSink(target_dir_path='target/directory/path', overwrite=True) as sink:
+...     for image in sv.get_video_frames_generator(source_path='source_video.mp4', stride=2):
+...         sink.save_image(image=image)
+
+ +
+ Source code in supervision/utils/image.py +
39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
def __init__(
+    self,
+    target_dir_path: str,
+    overwrite: bool = False,
+    image_name_pattern: str = "image_{:05d}.png",
+):
+    """
+    Initialize a context manager for saving images.
+
+    Args:
+        target_dir_path (str): The target directory where images will be saved.
+        overwrite (bool, optional): Whether to overwrite the existing directory. Defaults to False.
+        image_name_pattern (str, optional): The image file name pattern. Defaults to "image_{:05d}.png".
+
+    Examples:
+        ```python
+        >>> import supervision as sv
+
+        >>> with sv.ImageSink(target_dir_path='target/directory/path', overwrite=True) as sink:
+        ...     for image in sv.get_video_frames_generator(source_path='source_video.mp4', stride=2):
+        ...         sink.save_image(image=image)
+        ```
+    """
+    self.target_dir_path = target_dir_path
+    self.overwrite = overwrite
+    self.image_name_pattern = image_name_pattern
+    self.image_count = 0
+
+
+
+ +
+ +
+ + + +

+save_image(image, image_name=None) + +

+ + +
+ +

Save a given image in the target directory.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
image + np.ndarray + +
+

The image to be saved.

+
+
+ required +
image_name + str + +
+

The name to use for the saved image. If not provided, a name will be generated using the image_name_pattern.

+
+
+ None +
+ +
+ Source code in supervision/utils/image.py +
77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
def save_image(self, image: np.ndarray, image_name: Optional[str] = None):
+    """
+    Save a given image in the target directory.
+
+    Args:
+        image (np.ndarray): The image to be saved.
+        image_name (str, optional): The name to use for the saved image. If not provided, a name will be generated using the `image_name_pattern`.
+    """
+    if image_name is None:
+        image_name = self.image_name_pattern.format(self.image_count)
+
+    image_path = os.path.join(self.target_dir_path, image_name)
+    cv2.imwrite(image_path, image)
+    self.image_count += 1
+
+
+
+ +
+ + + +
+ +
+ +

crop

+ + +
+ + + +
+ +

Crops the given image based on the given bounding box.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
image + np.ndarray + +
+

The image to be cropped, represented as a numpy array.

+
+
+ required +
xyxy + np.ndarray + +
+

A numpy array containing the bounding box coordinates in the format (x1, y1, x2, y2).

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ np.ndarray + +
+

The cropped image as a numpy array.

+
+
+ +

Examples:

+
>>> import supervision as sv
+
+>>> detection = sv.Detections(...)
+>>> with sv.ImageSink(target_dir_path='target/directory/path') as sink:
+...     for xyxy in detection.xyxy:
+...         cropped_image = sv.crop(image=image, xyxy=xyxy)
+...         sink.save_image(image=image)
+
+ +
+ Source code in supervision/utils/image.py +
 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
def crop(image: np.ndarray, xyxy: np.ndarray) -> np.ndarray:
+    """
+    Crops the given image based on the given bounding box.
+
+    Args:
+        image (np.ndarray): The image to be cropped, represented as a numpy array.
+        xyxy (np.ndarray): A numpy array containing the bounding box coordinates in the format (x1, y1, x2, y2).
+
+    Returns:
+        (np.ndarray): The cropped image as a numpy array.
+
+    Examples:
+        ```python
+        >>> import supervision as sv
+
+        >>> detection = sv.Detections(...)
+        >>> with sv.ImageSink(target_dir_path='target/directory/path') as sink:
+        ...     for xyxy in detection.xyxy:
+        ...         cropped_image = sv.crop(image=image, xyxy=xyxy)
+        ...         sink.save_image(image=image)
+        ```
+    """
+
+    xyxy = np.round(xyxy).astype(int)
+    x1, y1, x2, y2 = xyxy
+    cropped_img = image[y1:y2, x1:x2]
+    return cropped_img
+
+
+
+ +
+ + + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/utils/notebook/index.html b/utils/notebook/index.html new file mode 100644 index 000000000..df053e151 --- /dev/null +++ b/utils/notebook/index.html @@ -0,0 +1,1357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Notebook - Supervision + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Notebook

+ +

plot_image

+ + +
+ + + +
+ +

Plots image using matplotlib.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
image + np.ndarray + +
+

The frame to be displayed.

+
+
+ required +
size + Tuple[int, int] + +
+

The size of the plot.

+
+
+ (12, 12) +
cmap + str + +
+

the colormap to use for single channel images.

+
+
+ 'gray' +
+ +

Examples:

+
>>> import cv2
+>>> import supervision as sv
+
+>>> image = cv2.imread("path/to/image.jpg")
+
+%matplotlib inline
+>>> sv.plot_image(image=image, size=(16, 16))
+
+ +
+ Source code in supervision/utils/notebook.py +
 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
def plot_image(
+    image: np.ndarray, size: Tuple[int, int] = (12, 12), cmap: Optional[str] = "gray"
+) -> None:
+    """
+    Plots image using matplotlib.
+
+    Args:
+        image (np.ndarray): The frame to be displayed.
+        size (Tuple[int, int]): The size of the plot.
+        cmap (str): the colormap to use for single channel images.
+
+    Examples:
+        ```python
+        >>> import cv2
+        >>> import supervision as sv
+
+        >>> image = cv2.imread("path/to/image.jpg")
+
+        %matplotlib inline
+        >>> sv.plot_image(image=image, size=(16, 16))
+        ```
+    """
+    plt.figure(figsize=size)
+
+    if image.ndim == 2:
+        plt.imshow(image, cmap=cmap)
+    else:
+        plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
+
+    plt.axis("off")
+    plt.show()
+
+
+
+ +

plot_images_grid

+ + +
+ + + +
+ +

Plots images in a grid using matplotlib.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
images + List[np.ndarray] + +
+

A list of images as numpy arrays.

+
+
+ required +
grid_size + Tuple[int, int] + +
+

A tuple specifying the number of rows and columns for the grid.

+
+
+ required +
titles + Optional[List[str]] + +
+

A list of titles for each image. Defaults to None.

+
+
+ None +
size + Tuple[int, int] + +
+

A tuple specifying the width and height of the entire plot in inches.

+
+
+ (12, 12) +
cmap + str + +
+

the colormap to use for single channel images.

+
+
+ 'gray' +
+ +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

If the number of images exceeds the grid size.

+
+
+ +

Examples:

+
>>> import cv2
+>>> import supervision as sv
+
+>>> image1 = cv2.imread("path/to/image1.jpg")
+>>> image2 = cv2.imread("path/to/image2.jpg")
+>>> image3 = cv2.imread("path/to/image3.jpg")
+
+>>> images = [image1, image2, image3]
+>>> titles = ["Image 1", "Image 2", "Image 3"]
+
+%matplotlib inline
+>>> plot_images_grid(images, grid_size=(2, 2), titles=titles, size=(16, 16))
+
+ +
+ Source code in supervision/utils/notebook.py +
41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
def plot_images_grid(
+    images: List[np.ndarray],
+    grid_size: Tuple[int, int],
+    titles: Optional[List[str]] = None,
+    size: Tuple[int, int] = (12, 12),
+    cmap: Optional[str] = "gray",
+) -> None:
+    """
+    Plots images in a grid using matplotlib.
+
+    Args:
+       images (List[np.ndarray]): A list of images as numpy arrays.
+       grid_size (Tuple[int, int]): A tuple specifying the number of rows and columns for the grid.
+       titles (Optional[List[str]]): A list of titles for each image. Defaults to None.
+       size (Tuple[int, int]): A tuple specifying the width and height of the entire plot in inches.
+       cmap (str): the colormap to use for single channel images.
+
+    Raises:
+       ValueError: If the number of images exceeds the grid size.
+
+    Examples:
+        ```python
+        >>> import cv2
+        >>> import supervision as sv
+
+        >>> image1 = cv2.imread("path/to/image1.jpg")
+        >>> image2 = cv2.imread("path/to/image2.jpg")
+        >>> image3 = cv2.imread("path/to/image3.jpg")
+
+        >>> images = [image1, image2, image3]
+        >>> titles = ["Image 1", "Image 2", "Image 3"]
+
+        %matplotlib inline
+        >>> plot_images_grid(images, grid_size=(2, 2), titles=titles, size=(16, 16))
+        ```
+    """
+    nrows, ncols = grid_size
+
+    if len(images) > nrows * ncols:
+        raise ValueError(
+            "The number of images exceeds the grid size. Please increase the grid size or reduce the number of images."
+        )
+
+    fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=size)
+
+    for idx, ax in enumerate(axes.flat):
+        if idx < len(images):
+            if images[idx].ndim == 2:
+                ax.imshow(images[idx], cmap=cmap)
+            else:
+                ax.imshow(cv2.cvtColor(images[idx], cv2.COLOR_BGR2RGB))
+
+            if titles is not None and idx < len(titles):
+                ax.set_title(titles[idx])
+
+        ax.axis("off")
+    plt.show()
+
+
+
+ +
+ + + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/utils/video/index.html b/utils/video/index.html new file mode 100644 index 000000000..f4d5ba126 --- /dev/null +++ b/utils/video/index.html @@ -0,0 +1,1683 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Video - Supervision + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Video

+ +

VideoInfo

+ + +
+ + + +
+ + +

A class to store video information, including width, height, fps and total number of frames.

+ +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
width + int + +
+

width of the video in pixels

+
+
height + int + +
+

height of the video in pixels

+
+
fps + int + +
+

frames per second of the video

+
+
total_frames + int + +
+

total number of frames in the video, default is None

+
+
+ +

Examples:

+
>>> import supervision as sv
+
+>>> video_info = sv.VideoInfo.from_video_path(video_path='video.mp4')
+
+>>> video_info
+VideoInfo(width=3840, height=2160, fps=25, total_frames=538)
+
+>>> video_info.resolution_wh
+(3840, 2160)
+
+ + +
+ Source code in supervision/utils/video.py +
10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
@dataclass
+class VideoInfo:
+    """
+    A class to store video information, including width, height, fps and total number of frames.
+
+    Attributes:
+        width (int): width of the video in pixels
+        height (int): height of the video in pixels
+        fps (int): frames per second of the video
+        total_frames (int, optional): total number of frames in the video, default is None
+
+    Examples:
+        ```python
+        >>> import supervision as sv
+
+        >>> video_info = sv.VideoInfo.from_video_path(video_path='video.mp4')
+
+        >>> video_info
+        VideoInfo(width=3840, height=2160, fps=25, total_frames=538)
+
+        >>> video_info.resolution_wh
+        (3840, 2160)
+        ```
+    """
+
+    width: int
+    height: int
+    fps: int
+    total_frames: Optional[int] = None
+
+    @classmethod
+    def from_video_path(cls, video_path: str) -> VideoInfo:
+        video = cv2.VideoCapture(video_path)
+        if not video.isOpened():
+            raise Exception(f"Could not open video at {video_path}")
+
+        width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
+        height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
+        fps = int(video.get(cv2.CAP_PROP_FPS))
+        total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
+        video.release()
+        return VideoInfo(width, height, fps, total_frames)
+
+    @property
+    def resolution_wh(self) -> Tuple[int, int]:
+        return self.width, self.height
+
+
+ + + +
+ + + + + + + + + + + +
+ +
+ +

VideoSink

+ + +
+ + + +
+ + +

Context manager that saves video frames to a file using OpenCV.

+ +

Attributes:

+ + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
target_path + str + +
+

The path to the output file where the video will be saved.

+
+
video_info + VideoInfo + +
+

Information about the video resolution, fps, and total frame count.

+
+
+ +
+ Example +
>>> import supervision as sv
+
+>>> video_info = sv.VideoInfo.from_video_path(video_path='source_video.mp4')
+
+>>> with sv.VideoSink(target_path='target_video.mp4', video_info=video_info) as sink:
+...     for frame in get_video_frames_generator(source_path='source_video.mp4', stride=2):
+...         sink.write_frame(frame=frame)
+
+
+ +
+ Source code in supervision/utils/video.py +
58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
class VideoSink:
+    """
+    Context manager that saves video frames to a file using OpenCV.
+
+    Attributes:
+        target_path (str): The path to the output file where the video will be saved.
+        video_info (VideoInfo): Information about the video resolution, fps, and total frame count.
+
+    Example:
+        ```python
+        >>> import supervision as sv
+
+        >>> video_info = sv.VideoInfo.from_video_path(video_path='source_video.mp4')
+
+        >>> with sv.VideoSink(target_path='target_video.mp4', video_info=video_info) as sink:
+        ...     for frame in get_video_frames_generator(source_path='source_video.mp4', stride=2):
+        ...         sink.write_frame(frame=frame)
+        ```
+    """
+
+    def __init__(self, target_path: str, video_info: VideoInfo):
+        self.target_path = target_path
+        self.video_info = video_info
+        self.__fourcc = cv2.VideoWriter_fourcc(*"mp4v")
+        self.__writer = None
+
+    def __enter__(self):
+        self.__writer = cv2.VideoWriter(
+            self.target_path,
+            self.__fourcc,
+            self.video_info.fps,
+            self.video_info.resolution_wh,
+        )
+        return self
+
+    def write_frame(self, frame: np.ndarray):
+        self.__writer.write(frame)
+
+    def __exit__(self, exc_type, exc_value, exc_traceback):
+        self.__writer.release()
+
+
+ + + +
+ + + + + + + + + + + +
+ +
+ +

get_video_frames_generator

+ + +
+ + + +
+ +

Get a generator that yields the frames of the video.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_path + str + +
+

The path of the video file.

+
+
+ required +
stride + int + +
+

Indicates the interval at which frames are returned, skipping stride - 1 frames between each.

+
+
+ 1 +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Generator[np.ndarray, None, None] + +
+

A generator that yields the frames of the video.

+
+
+ +

Examples:

+
>>> import supervision as sv
+
+>>> for frame in sv.get_video_frames_generator(source_path='source_video.mp4', stride=2):
+...     ...
+
+ +
+ Source code in supervision/utils/video.py +
100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
def get_video_frames_generator(
+    source_path: str, stride: int = 1
+) -> Generator[np.ndarray, None, None]:
+    """
+    Get a generator that yields the frames of the video.
+
+    Args:
+        source_path (str): The path of the video file.
+        stride (int): Indicates the interval at which frames are returned, skipping stride - 1 frames between each.
+
+    Returns:
+        (Generator[np.ndarray, None, None]): A generator that yields the frames of the video.
+
+    Examples:
+        ```python
+        >>> import supervision as sv
+
+        >>> for frame in sv.get_video_frames_generator(source_path='source_video.mp4', stride=2):
+        ...     ...
+        ```
+    """
+    video = cv2.VideoCapture(source_path)
+    if not video.isOpened():
+        raise Exception(f"Could not open video at {source_path}")
+
+    frame_count = 0
+    success, frame = video.read()
+    while success:
+        if frame_count % stride == 0:
+            yield frame
+        success, frame = video.read()
+        frame_count += 1
+
+    video.release()
+
+
+
+ +

process_video

+ + +
+ + + +
+ +

Process a video file by applying a callback function on each frame and saving the result to a target video file.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_path + str + +
+

The path to the source video file.

+
+
+ required +
target_path + str + +
+

The path to the target video file.

+
+
+ required +
callback + Callable[[np.ndarray, int], np.ndarray] + +
+

A function that takes in a numpy ndarray representation of a video frame and an int index of the frame and returns a processed numpy ndarray representation of the frame.

+
+
+ required +
+ +

Examples:

+
>>> from supervision import process_video
+
+>>> def process_frame(scene: np.ndarray) -> np.ndarray:
+...     ...
+
+>>> process_video(
+...     source_path='source_video.mp4',
+...     target_path='target_video.mp4',
+...     callback=process_frame
+... )
+
+ +
+ Source code in supervision/utils/video.py +
136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
def process_video(
+    source_path: str,
+    target_path: str,
+    callback: Callable[[np.ndarray, int], np.ndarray],
+) -> None:
+    """
+    Process a video file by applying a callback function on each frame and saving the result to a target video file.
+
+    Args:
+        source_path (str): The path to the source video file.
+        target_path (str): The path to the target video file.
+        callback (Callable[[np.ndarray, int], np.ndarray]): A function that takes in a numpy ndarray representation of a video frame and an int index of the frame and returns a processed numpy ndarray representation of the frame.
+
+    Examples:
+        ```python
+        >>> from supervision import process_video
+
+        >>> def process_frame(scene: np.ndarray) -> np.ndarray:
+        ...     ...
+
+        >>> process_video(
+        ...     source_path='source_video.mp4',
+        ...     target_path='target_video.mp4',
+        ...     callback=process_frame
+        ... )
+        ```
+    """
+    source_video_info = VideoInfo.from_video_path(video_path=source_path)
+    with VideoSink(target_path=target_path, video_info=source_video_info) as sink:
+        for index, frame in enumerate(
+            get_video_frames_generator(source_path=source_path)
+        ):
+            result_frame = callback(frame, index)
+            sink.write_frame(frame=result_frame)
+
+
+
+ +
+ + + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file