lightly-studio 0.3.1__py3-none-any.whl → 0.3.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of lightly-studio might be problematic. Click here for more details.

Files changed (133) hide show
  1. lightly_studio/__init__.py +4 -4
  2. lightly_studio/api/app.py +1 -1
  3. lightly_studio/api/routes/api/annotation.py +6 -16
  4. lightly_studio/api/routes/api/annotation_label.py +2 -5
  5. lightly_studio/api/routes/api/annotation_task.py +4 -5
  6. lightly_studio/api/routes/api/classifier.py +2 -5
  7. lightly_studio/api/routes/api/dataset.py +2 -3
  8. lightly_studio/api/routes/api/dataset_tag.py +2 -3
  9. lightly_studio/api/routes/api/metadata.py +2 -4
  10. lightly_studio/api/routes/api/metrics.py +2 -6
  11. lightly_studio/api/routes/api/sample.py +5 -13
  12. lightly_studio/api/routes/api/settings.py +2 -6
  13. lightly_studio/api/routes/images.py +6 -6
  14. lightly_studio/core/add_samples.py +383 -0
  15. lightly_studio/core/dataset.py +250 -362
  16. lightly_studio/core/dataset_query/__init__.py +0 -0
  17. lightly_studio/core/dataset_query/boolean_expression.py +67 -0
  18. lightly_studio/core/dataset_query/dataset_query.py +211 -0
  19. lightly_studio/core/dataset_query/field.py +113 -0
  20. lightly_studio/core/dataset_query/field_expression.py +79 -0
  21. lightly_studio/core/dataset_query/match_expression.py +23 -0
  22. lightly_studio/core/dataset_query/order_by.py +79 -0
  23. lightly_studio/core/dataset_query/sample_field.py +28 -0
  24. lightly_studio/core/dataset_query/tags_expression.py +46 -0
  25. lightly_studio/core/sample.py +159 -32
  26. lightly_studio/core/start_gui.py +35 -0
  27. lightly_studio/dataset/edge_embedding_generator.py +13 -8
  28. lightly_studio/dataset/embedding_generator.py +2 -3
  29. lightly_studio/dataset/embedding_manager.py +74 -6
  30. lightly_studio/dataset/fsspec_lister.py +275 -0
  31. lightly_studio/dataset/loader.py +49 -30
  32. lightly_studio/dataset/mobileclip_embedding_generator.py +6 -4
  33. lightly_studio/db_manager.py +145 -0
  34. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/SelectableSvgGroup.BBm0IWdq.css +1 -0
  35. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/SelectableSvgGroup.BNTuXSAe.css +1 -0
  36. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/2O287xak.js +3 -0
  37. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{O-EABkf9.js → 7YNGEs1C.js} +1 -1
  38. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BBoGk9hq.js +1 -0
  39. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BRnH9v23.js +92 -0
  40. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Bg1Y5eUZ.js +1 -0
  41. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{DOlTMNyt.js → BqBqV92V.js} +1 -1
  42. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/C0JiMuYn.js +1 -0
  43. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{DjfY96ND.js → C98Hk3r5.js} +1 -1
  44. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{r64xT6ao.js → CG0dMCJi.js} +1 -1
  45. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{C8I8rFJQ.js → Ccq4ZD0B.js} +1 -1
  46. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Cpy-nab_.js +1 -0
  47. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{Bu7uvVrG.js → Crk-jcvV.js} +1 -1
  48. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Cs31G8Qn.js +1 -0
  49. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CsKrY2zA.js +1 -0
  50. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{x9G_hzyY.js → Cur71c3O.js} +1 -1
  51. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CzgC3GFB.js +1 -0
  52. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D8GZDMNN.js +1 -0
  53. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DFRh-Spp.js +1 -0
  54. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{BylOuP6i.js → DRZO-E-T.js} +1 -1
  55. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{l7KrR96u.js → DcGCxgpH.js} +1 -1
  56. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{Bsi3UGy5.js → Df3aMO5B.js} +1 -1
  57. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{hQVEETDE.js → DkR_EZ_B.js} +1 -1
  58. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DqUGznj_.js +1 -0
  59. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/KpAtIldw.js +1 -0
  60. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/M1Q1F7bw.js +4 -0
  61. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{CDnpyLsT.js → OH7-C_mc.js} +1 -1
  62. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{D6su9Aln.js → gLNdjSzu.js} +1 -1
  63. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/i0ZZ4z06.js +1 -0
  64. lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/app.BI-EA5gL.js +2 -0
  65. lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/start.CcsRl3cZ.js +1 -0
  66. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/0.BbO4Zc3r.js +1 -0
  67. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{1.B4rNYwVp.js → 1._I9GR805.js} +1 -1
  68. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/10.J2RBFrSr.js +1 -0
  69. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/12.Cmqj25a-.js +1 -0
  70. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/2.C45iKJHA.js +6 -0
  71. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{3.CWHpKonm.js → 3.w9g4AcAx.js} +1 -1
  72. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{4.OUWOLQeV.js → 4.BBI8KwnD.js} +1 -1
  73. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/5.huHuxdiF.js +1 -0
  74. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/6.CrbkRPam.js +1 -0
  75. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/7.FomEdhD6.js +1 -0
  76. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/8.Cb_ADSLk.js +1 -0
  77. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{9.CPu3CiBc.js → 9.CajIG5ce.js} +1 -1
  78. lightly_studio/dist_lightly_studio_view_app/_app/version.json +1 -1
  79. lightly_studio/dist_lightly_studio_view_app/index.html +14 -14
  80. lightly_studio/examples/example.py +13 -12
  81. lightly_studio/examples/example_coco.py +13 -0
  82. lightly_studio/examples/example_metadata.py +83 -98
  83. lightly_studio/examples/example_selection.py +7 -19
  84. lightly_studio/examples/example_split_work.py +12 -36
  85. lightly_studio/examples/{example_v2.py → example_yolo.py} +3 -4
  86. lightly_studio/models/annotation/annotation_base.py +7 -8
  87. lightly_studio/models/annotation/instance_segmentation.py +8 -8
  88. lightly_studio/models/annotation/object_detection.py +4 -4
  89. lightly_studio/models/dataset.py +6 -2
  90. lightly_studio/models/sample.py +10 -3
  91. lightly_studio/resolvers/dataset_resolver.py +10 -0
  92. lightly_studio/resolvers/embedding_model_resolver.py +22 -0
  93. lightly_studio/resolvers/sample_resolver.py +53 -9
  94. lightly_studio/resolvers/tag_resolver.py +23 -0
  95. lightly_studio/selection/select.py +55 -46
  96. lightly_studio/selection/select_via_db.py +23 -19
  97. lightly_studio/selection/selection_config.py +6 -3
  98. lightly_studio/services/annotations_service/__init__.py +4 -0
  99. lightly_studio/services/annotations_service/update_annotation.py +21 -32
  100. lightly_studio/services/annotations_service/update_annotation_bounding_box.py +36 -0
  101. lightly_studio-0.3.2.dist-info/METADATA +689 -0
  102. {lightly_studio-0.3.1.dist-info → lightly_studio-0.3.2.dist-info}/RECORD +104 -91
  103. lightly_studio/api/db.py +0 -133
  104. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/SelectableSvgGroup.OwPEPQZu.css +0 -1
  105. lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/SelectableSvgGroup.b653GmVf.css +0 -1
  106. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/B2FVR0s0.js +0 -1
  107. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/B9zumHo5.js +0 -1
  108. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BJXwVxaE.js +0 -1
  109. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Bx1xMsFy.js +0 -1
  110. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CcaPhhk3.js +0 -1
  111. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CvOmgdoc.js +0 -93
  112. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CxtLVaYz.js +0 -3
  113. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D5-A_Ffd.js +0 -4
  114. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D6RI2Zrd.js +0 -1
  115. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D98V7j6A.js +0 -1
  116. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DIRAtgl0.js +0 -1
  117. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DjUWrjOv.js +0 -1
  118. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/XO7A28GO.js +0 -1
  119. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/nAHhluT7.js +0 -1
  120. lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/vC4nQVEB.js +0 -1
  121. lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/app.CjnvpsmS.js +0 -2
  122. lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/start.0o1H7wM9.js +0 -1
  123. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/0.XRq_TUwu.js +0 -1
  124. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/10.DfBwOEhN.js +0 -1
  125. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/12.CwF2_8mP.js +0 -1
  126. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/2.CS4muRY-.js +0 -6
  127. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/5.Dm6t9F5W.js +0 -1
  128. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/6.Bw5ck4gK.js +0 -1
  129. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/7.CF0EDTR6.js +0 -1
  130. lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/8.Cw30LEcV.js +0 -1
  131. lightly_studio-0.3.1.dist-info/METADATA +0 -520
  132. /lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/{OpenSans- → OpenSans-Medium.DVUZMR_6.ttf} +0 -0
  133. {lightly_studio-0.3.1.dist-info → lightly_studio-0.3.2.dist-info}/WHEEL +0 -0
@@ -1 +0,0 @@
1
- import{r as e}from"../chunks/H7C68rOM.js";import{l as a}from"../chunks/BJXwVxaE.js";import{r as s}from"../chunks/B90CZVMX.js";const l=async()=>{const{data:t,error:o}=await a(),r=t==null?void 0:t.dataset_id;if(o||!r)throw o;s(307,e.toSamples(r))},p=Object.freeze(Object.defineProperty({__proto__:null,load:l},Symbol.toStringTag,{value:"Module"}));export{p as universal};
@@ -1 +0,0 @@
1
- import"../chunks/CWj6FrbW.js";import{p as f,u as $,a as h,g as o,d as r}from"../chunks/l7KrR96u.js";import{s as S,a as i}from"../chunks/XO7A28GO.js";import{A as T}from"../chunks/CvOmgdoc.js";import{u as _}from"../chunks/vC4nQVEB.js";import"../chunks/CxtLVaYz.js";import"../chunks/D6su9Aln.js";import"../chunks/Bx1xMsFy.js";import"../chunks/Bsi3UGy5.js";import"../chunks/69_IOA4Y.js";import{u as G}from"../chunks/DIRAtgl0.js";import"../chunks/BJXwVxaE.js";function k(p,t){f(t,!0);const[e,m]=S(),n=()=>i(l,"$lastGridType",e),s=()=>i(d,"$sampleSize",e),{datasetId:a,sampleSize:d,selectedAnnotationFilterIds:c}=t.data,{lastGridType:l}=_(),u=r(()=>G({dataset_id:a})),g=r(()=>o(u).clearTagsSelected);$(()=>{n()!=="annotations"&&o(g)()}),T(p,{get itemHeight(){return s().height},get itemWidth(){return s().width},dataset_id:a,selectedAnnotationFilterIds:c}),h(),m()}export{k as component};
@@ -1 +0,0 @@
1
- import{p as T,f as B,n as Et,a as j,w as bt,l as V,$ as ct,g as a,d,c as f,r as m,s as S,t as F,L as Dt,V as Tt,ax as gt}from"../chunks/l7KrR96u.js";import{c as jt}from"../chunks/BJXwVxaE.js";import"../chunks/CWj6FrbW.js";import{c as Z,a as _,t as k,n as Ht}from"../chunks/Bu7uvVrG.js";import{i as O,a as D,s as q}from"../chunks/XO7A28GO.js";import{e as ut}from"../chunks/DjfY96ND.js";import{I as Rt,s as J,K as Ut,L as at,M as Ft,N as Ot,O as zt,P as Gt,p as xt,F as Kt,e as Wt,l as qt,h as Vt,C as It,z as At,E as wt,u as Zt,d as Jt,S as Qt,Q as Yt,a as Xt}from"../chunks/CvOmgdoc.js";import{g as tt}from"../chunks/CxtLVaYz.js";import{u as St}from"../chunks/vC4nQVEB.js";import{f as ht,S as ta}from"../chunks/D5-A_Ffd.js";import{r as U}from"../chunks/H7C68rOM.js";import"../chunks/D6su9Aln.js";import"../chunks/Bx1xMsFy.js";import{p as z}from"../chunks/Bsi3UGy5.js";import"../chunks/69_IOA4Y.js";import{S as aa}from"../chunks/B2FVR0s0.js";import{S as na,B as ea,a as oa,b as X,c as lt,H as sa,d as dt,D as ra,e as ia,Z as la,g as da,u as ca}from"../chunks/nAHhluT7.js";import{S as ua}from"../chunks/BylOuP6i.js";import{s as va,r as ma,a as fa}from"../chunks/D98V7j6A.js";import{s as W}from"../chunks/D6RI2Zrd.js";import{C as _a}from"../chunks/CDnpyLsT.js";import{s as pa}from"../chunks/DIRAtgl0.js";function ga(v,t){T(t,!0);let r=ma(t,["$$slots","$$events","$$legacy"]);Rt(v,va({name:"square-dashed"},()=>r,{iconNode:[["path",{d:"M5 3a2 2 0 0 0-2 2"}],["path",{d:"M19 3a2 2 0 0 1 2 2"}],["path",{d:"M21 19a2 2 0 0 1-2 2"}],["path",{d:"M5 21a2 2 0 0 1-2-2"}],["path",{d:"M9 3h1"}],["path",{d:"M9 21h1"}],["path",{d:"M14 3h1"}],["path",{d:"M14 21h1"}],["path",{d:"M3 9v1"}],["path",{d:"M21 9v1"}],["path",{d:"M3 14v1"}],["path",{d:"M21 14v1"}]],children:(n,o)=>{var s=Z(),p=B(s);pa(p,()=>t.children??Et),_(n,s)},$$slots:{default:!0}})),j()}const ha=async({dataset_id:v,cursor:t=0,limit:r=10,annotation_label_ids:i,tag_ids:n})=>{const o={data:[],error:void 0};try{const s=await jt.GET("/api/datasets/{dataset_id}/annotations",{params:{path:{dataset_id:v},query:{annotation_label_ids:i,tag_ids:n,cursor:t,limit:r}}});if(s.error)throw new Error(JSON.stringify(s.error,null,2));if(!s.data)throw new Error("No data");o.data=s.data.data}catch(s){o.error="Error loading annotations: "+String(s)}return o},ba=({annotationIndex:v,...t})=>{const r=bt({isLoading:!1});return(async()=>{r.update(n=>({...n,isLoading:!0}));try{const n=await ha({cursor:v>0?v-1:0,limit:3,...t}),o=n.data.findIndex(e=>e.annotation_id===t.currentAnnotationId),s=n.data[o+1],p=n.data[o-1];r.update(e=>({...e,isLoading:!1,error:n.error?String(n.error):void 0,data:n.error?void 0:{annotationNext:s,annotationPrevious:p}}))}catch(n){r.update(o=>({...o,isLoading:!1,error:String(n)}))}})(),r},xa=async({parent:v,params:{dataset_id:t,annotationId:r,annotationIndex:i}})=>{const{annotationsSelectedTagsIds:n,annotationsSelectedAnnotationLabelsIds:o,dataset:s}=await v(),p=parseInt(i,10),e=bt();if(!s)throw new Error("Dataset data not found");return ba({annotationIndex:p,dataset_id:t,currentAnnotationId:r,annotation_label_ids:Array.from(V(o)),tag_ids:Array.from(V(n))}).subscribe(({data:L})=>{e.set(L)}),{annotationAdjacents:e,annotationIndex:p,annotationId:r,dataset:s}},gn=Object.freeze(Object.defineProperty({__proto__:null,load:xa},Symbol.toStringTag,{value:"Module"}));function Ia(v,t){T(t,!0);const[r,i]=q(),n=()=>D(a(s),"$annotationAdjacents",r),o=d(()=>z.data.annotationIndex),s=d(()=>z.data.annotationAdjacents),p=()=>{n().annotationNext&&tt(U.toSampleWithAnnotation({datasetId:n().annotationNext.dataset_id,sampleId:n().annotationNext.sample_id,annotationId:n().annotationNext.annotation_id,annotationIndex:a(o)+1}))},e=()=>{n().annotationPrevious&&tt(U.toSampleWithAnnotation({datasetId:n().annotationPrevious.dataset_id,sampleId:n().annotationPrevious.sample_id,annotationId:n().annotationPrevious.annotation_id,annotationIndex:a(o)-1}))},L=g=>{switch(g.key){case"ArrowRight":p();break;case"ArrowLeft":e();break}};var x=Z();ut("keydown",ct,L);var $=B(x);{var I=g=>{const P=d(()=>!!n().annotationPrevious),y=d(()=>!!n().annotationNext);ua(g,{get hasPrevious(){return a(P)},get hasNext(){return a(y)},onPrevious:e,onNext:p})};O($,g=>{n()&&g(I)})}_(v,x),j(),i()}const Aa={object_detection:"Object Detection",bbox:"Bounding Box",instance_segmentation:"Instance Segmentation",semantic_segmentation:"Semantic Segmentation",classification:"Classification"};var wa=k('<span class="text-sm"> </span> <span class="break-all text-sm"> </span>',1);function Sa(v,t){var r=wa(),i=B(r),n=f(i,!0);m(i);var o=S(i,2),s=f(o,!0);m(o),F(()=>{W(n,t.label),J(o,"data-testid",`annotation-metadata-${t.id}`),W(s,t.value)}),_(v,r)}var $a=k('<span class="break-all text-sm"> </span>'),ya=k('<span class="text-sm"> </span> <!>',1);function ka(v,t){T(t,!0);const[r,i]=q(),n=()=>D(s,"$result",r),o=()=>D(t.isEditingMode,"$isEditingMode",r),s=Ut(),{updateAnnotation:p}=at({datasetId:t.datasetId,annotationId:t.annotationId,onUpdate:t.onUpdate}),e=d(()=>zt(n().data||[])),L=d(()=>{const A=a(e).find(c=>c.value===t.value);return A||{value:t.value,label:t.value}});var x=ya(),$=B(x),I=f($,!0);m($);var g=S($,2);{var P=A=>{Ft(A,{get items(){return a(e)},get value(){return a(L)},name:"annotation-label",placeholder:"Select a label",onSelect:l=>{p({annotation_id:t.annotationId,dataset_id:t.datasetId,label_name:l.value})},notFound:(l,u)=>{let h=()=>u==null?void 0:u().inputValue;Ot(l,{get label(){return h()}})},$$slots:{notFound:!0}})},y=A=>{var c=$a();J(c,"data-testid","annotation-metadata-label");var l=f(c,!0);m(c),F(()=>W(l,t.value)),_(A,c)};O(g,A=>{o()?A(P):A(y,!1)})}F(()=>W(I,t.label)),_(v,x),j(),i()}var Pa=k('<div class="flex flex-col gap-4"><div class="grid grid-cols-[6rem_1fr] gap-y-3 text-diffuse-foreground"></div></div>');function Ma(v,t){T(t,!0);const[r,i]=q(),n=()=>D(a(p),"$annotationResp",r),{datasetId:o}=z.data,s=d(()=>at({datasetId:o,annotationId:t.annotationId})),p=d(()=>a(s).annotation);let e=d(()=>n().data);const L=d(qt),x=d(()=>a(L).taskMap),$=d(()=>a(e)&&a(e).annotation_task_id&&V(a(x)).has(a(e).annotation_task_id)?V(a(x)).get(a(e).annotation_task_id).is_prediction:!1),I=d(()=>{var l,u;if(!a(e))return[];let c=[{id:"label",label:"Label:",value:(u=(l=a(e))==null?void 0:l.annotation_label)==null?void 0:u.annotation_label_name},{id:"type",label:"Type:",value:Aa[a(e).annotation_type]||"Unknown"},{id:"status",label:"Status:",value:a($)?"Prediction":"Ground Truth"}];if(a(e)&&!Gt(a(e))){const{height:h,width:M}=xt(a(e));c=[{id:"height",label:"Height:",value:ht(Math.round(h))+"px"},{id:"width",label:"Width:",value:ht(Math.round(M))+"px"},...c]}return c}),{isEditingMode:g}=z.data.globalStorage;var P=Z(),y=B(P);{var A=c=>{Kt(c,{title:"Annotation details",children:(l,u)=>{var h=Pa(),M=f(h);Wt(M,21,()=>a(I),({label:N,value:b,id:C})=>N,(N,b)=>{let C=()=>a(b).label,E=()=>a(b).value,H=()=>a(b).id;var G=Z(),nt=B(G);{var Q=w=>{ka(w,{get onUpdate(){return t.onUpdate},get annotationId(){return t.annotationId},datasetId:o,get label(){return C()},get value(){return E()},isEditingMode:g})},et=w=>{Sa(w,{get label(){return C()},get id(){return H()},get value(){return E()}})};O(nt,w=>{H()==="label"&&g?w(Q):w(et,!1)})}_(N,G)}),m(M),m(h),_(l,h)}})};O(y,c=>{a(I).length>0&&c(A)})}_(v,P),j(),i()}var Na=k('<!> <h2 class="py-2 text-lg font-semibold"><a>Check all sample annotations</a></h2>',1),Ca=k('<div class="flex h-full w-full items-center justify-center"><!></div>'),Ba=k('<div class="flex h-full min-h-0 flex-col space-y-4 overflow-hidden dark:[color-scheme:dark]"><!> <!></div>');function La(v,t){T(t,!0);const{datasetId:r}=z.data;let i=Tt(void 0);at({datasetId:r,annotationId:t.annotationId}).annotation.subscribe(e=>{e.isSuccess&&Dt(i,fa(e.data.sample))});const o=Vt(),[s,p]=o;It(v,{className:"h-full",children:(e,L)=>{At(e,{className:"h-full flex flex-col",children:(x,$)=>{var I=Ba(),g=f(I);Ma(g,{get annotationId(){return t.annotationId},get onUpdate(){return t.onUpdate}});var P=S(g,2);{var y=c=>{var l=Na(),u=B(l);na(u,{get sample(){return a(i)},showCustomMetadata:!1});var h=S(u,2),M=f(h);m(h),F(N=>J(M,"href",N),[()=>s(U.toSample({sampleId:a(i).sample_id,datasetId:a(i).dataset_id}),void 0)]),_(c,l)},A=c=>{var l=Ca(),u=f(l);wt(u,{size:"large",align:"center"}),m(l),_(c,l)};O(P,c=>{a(i)?c(y):c(A,!1)})}m(I),_(x,I)},$$slots:{default:!0}})},$$slots:{default:!0}}),j()}var Ea=k('<!> <span class="hidden sm:inline">Home</span>',1),Da=k('<!> <span class="max-w-[150px] truncate"> </span>',1),Ta=k('<!> <span class="hidden sm:inline">Annotations</span>',1),ja=k('<!> <span class="max-w-[200px] truncate"> </span>',1),Ha=k("<!> <!> <!> <!> <!> <!> <!>",1);function Ra(v,t){T(t,!0);const[r,i]=q(),n=()=>D(o,"$annotationsTotalCount",r),{annotationsTotalCount:o}=St();ea(v,{class:"mb-2",children:(s,p)=>{oa(s,{children:(e,L)=>{var x=Ha(),$=B(x);X($,{children:(l,u)=>{const h=d(()=>U.toHome());lt(l,{get href(){return a(h)},class:"flex items-center gap-2",children:(M,N)=>{var b=Ea(),C=B(b);sa(C,{class:"h-4 w-4"}),gt(2),_(M,b)},$$slots:{default:!0}})},$$slots:{default:!0}});var I=S($,2);dt(I,{});var g=S(I,2);X(g,{children:(l,u)=>{const h=d(()=>U.toAnnotations(t.dataset.dataset_id));lt(l,{get href(){return a(h)},class:"flex items-center gap-2",get title(){return t.dataset.directory},children:(M,N)=>{var b=Da(),C=B(b);ra(C,{class:"h-4 w-4"});var E=S(C,2),H=f(E,!0);m(E),F(()=>{J(E,"title",t.dataset.directory),W(H,t.dataset.name)}),_(M,b)},$$slots:{default:!0}})},$$slots:{default:!0}});var P=S(g,2);dt(P,{});var y=S(P,2);X(y,{children:(l,u)=>{const h=d(()=>U.toAnnotations(t.dataset.dataset_id));lt(l,{get href(){return a(h)},class:"flex items-center gap-2",children:(M,N)=>{var b=Ta(),C=B(b);_a(C,{class:"h-4 w-4"}),gt(2),_(M,b)},$$slots:{default:!0}})},$$slots:{default:!0}});var A=S(y,2);dt(A,{});var c=S(A,2);X(c,{children:(l,u)=>{ia(l,{class:"flex items-center gap-2",children:(h,M)=>{var N=ja(),b=B(N);ga(b,{class:"h-4 w-4"});var C=S(b,2),E=f(C);m(C),F(()=>W(E,`Annotation ${t.annotationIndex+1} of ${n()??""}`)),_(h,N)},$$slots:{default:!0}})},$$slots:{default:!0}}),_(e,x)},$$slots:{default:!0}})},$$slots:{default:!0}}),j(),i()}var Ua=Ht("<image></image><g><!></g>",1),Fa=k('<div class="h-full w-full overflow-hidden"><div class="sample relative h-full w-full"><div class="absolute right-4 top-2 z-30"><!></div> <!> <!></div></div>'),Oa=k('<div class="flex h-full w-full items-center justify-center"><!></div>'),za=k('<div class="flex h-full w-full flex-col space-y-4"><div class="flex w-full items-center"><!></div> <!> <div class="flex min-h-0 flex-1 gap-4"><div class="flex-1"><!></div> <div class="relative w-[375px]"><!></div></div></div>');function Ga(v,t){T(t,!0);const[r,i]=q(),n=()=>D(a(l),"$annotationResp",r),o=()=>D(e,"$selectedSampleAnnotationCropIds",r),s=()=>D(L,"$isHidden",r),{toggleSampleAnnotationCropSelection:p,selectedSampleAnnotationCropIds:e}=St(),{isHidden:L,handleKeyEvent:x}=Zt(),{settingsStore:$}=Jt(),I=" ",g=V($).key_go_back,P=()=>{var w;(w=t.sample)!=null&&w.dataset_id?tt(U.toAnnotations(t.sample.dataset_id)):tt("/")},y=w=>{switch(w.key){case g:P();break;case I:w.preventDefault(),p(t.annotationId);break}x(w)},A=z.data.datasetId,c=d(()=>at({datasetId:A,annotationId:t.annotationId})),l=d(()=>a(c).annotation);let u=d(()=>n().data),h=d(()=>{var w,ot;return da(((ot=(w=a(u))==null?void 0:w.sample)==null?void 0:ot.sample_id)||"")}),M=d(()=>a(u)?xt(a(u)):void 0);var N=za();ut("keydown",ct,y),ut("keyup",ct,x);var b=f(N),C=f(b);Ra(C,{get dataset(){return t.dataset},get annotationIndex(){return t.annotationIndex}}),m(b);var E=S(b,2);ta(E,{class:"mb-4 bg-border-hard"});var H=S(E,2),G=f(H),nt=f(G);It(nt,{className:"h-full",children:(w,ot)=>{At(w,{className:"h-full",children:($t,Wa)=>{var vt=Z(),yt=B(vt);{var kt=K=>{var R=Fa(),Y=f(R),st=f(Y),Mt=f(st);const Nt=d(()=>o().has(t.annotationId));Qt(Mt,{onSelect:()=>p(t.annotationId),get isSelected(){return a(Nt)}}),m(st);var mt=S(st,2);Ia(mt,{});var Ct=S(mt,2);la(Ct,{get width(){return a(u).sample.width},get height(){return a(u).sample.height},get boundingBox(){return a(M)},children:(Bt,qa)=>{var ft=Ua(),_t=B(ft),rt=S(_t);let pt;var Lt=f(rt);Yt(Lt,()=>a(u).annotation_id,it=>{aa(it,{get annotation(){return a(u)},showLabel:!0,get imageWidth(){return a(u).sample.width}})}),m(rt),F(it=>{J(_t,"href",a(h)),pt=Xt(rt,0,"",null,pt,it)},[()=>({invisible:s()})]),_(Bt,ft)},$$slots:{default:!0}}),m(Y),m(R),_(K,R)},Pt=K=>{var R=Oa(),Y=f(R);wt(Y,{size:"large",align:"center"}),m(R),_(K,R)};O(yt,K=>{a(u)?K(kt):K(Pt,!1)})}_($t,vt)},$$slots:{default:!0}})},$$slots:{default:!0}}),m(G);var Q=S(G,2),et=f(Q);La(et,{get annotationId(){return t.annotationId}}),m(Q),m(H),m(N),_(v,N),j(),i()}var Ka=k('<div class="flex h-full w-full space-x-4 px-4 pb-4" data-testid="annotation-details"><div class="h-full w-full space-y-6 rounded-[1vw] bg-card p-4"><!></div></div>');function hn(v,t){T(t,!0);const[r,i]=q(),n=()=>D(a(x),"$sample",r),o=d(()=>t.data.annotationId),s=d(()=>t.data.dataset),p=d(()=>t.data.annotationIndex),{sampleId:e}=z.params,L=d(()=>ca({sampleId:e,datasetId:a(s).dataset_id})),x=d(()=>a(L).sample);var $=Ka(),I=f($),g=f(I);{var P=y=>{Ga(y,{get annotationId(){return a(o)},get annotationIndex(){return a(p)},get dataset(){return a(s)},get sample(){return n().data}})};O(g,y=>{n().data&&a(o)&&a(s)&&y(P)})}m(I),m($),_(v,$),j(),i()}export{hn as component,gn as universal};
@@ -1,520 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: lightly-studio
3
- Version: 0.3.1
4
- Summary: LightlyStudio is a lightweight, fast, and easy-to-use data exploration tool for data scientists and engineers.
5
- Classifier: Operating System :: MacOS :: MacOS X
6
- Classifier: Operating System :: Microsoft :: Windows
7
- Classifier: Operating System :: POSIX :: Linux
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: Programming Language :: Python :: 3.8
10
- Classifier: Programming Language :: Python :: 3.9
11
- Classifier: Programming Language :: Python :: 3.10
12
- Classifier: Programming Language :: Python :: 3.11
13
- Requires-Python: >=3.8
14
- Requires-Dist: annotated-types==0.7.0
15
- Requires-Dist: duckdb-engine>=0.13.5
16
- Requires-Dist: duckdb>=1.1.3
17
- Requires-Dist: environs<12.0.0
18
- Requires-Dist: eval-type-backport>=0.2.2
19
- Requires-Dist: fastapi>=0.115.5
20
- Requires-Dist: faster-coco-eval>=1.6.5
21
- Requires-Dist: labelformat>=0.1.7
22
- Requires-Dist: lightly-mundig==0.1.3
23
- Requires-Dist: open-clip-torch>=2.20.0
24
- Requires-Dist: python-multipart>=0.0.20
25
- Requires-Dist: scikit-learn==1.3.2
26
- Requires-Dist: sqlmodel>=0.0.22
27
- Requires-Dist: torchmetrics>=1.5.2
28
- Requires-Dist: tqdm>=4.65.0
29
- Requires-Dist: typing-extensions>=4.12.2
30
- Requires-Dist: uvicorn>=0.32.1
31
- Requires-Dist: xxhash>=3.5.0
32
- Description-Content-Type: text/markdown
33
-
34
- <div align="center">
35
- <p align="center">
36
-
37
- <!-- prettier-ignore -->
38
- <img src="https://cdn.prod.website-files.com/62cd5ce03261cba217188442/66dac501a8e9a90495970876_Logo%20dark-short-p-800.png" height="50px">
39
-
40
- **The open-source tool curating datasets**
41
-
42
- ---
43
-
44
- [![PyPI python](https://img.shields.io/pypi/pyversions/lightly-studio)](https://pypi.org/project/lightly-studio)
45
- [![PyPI version](https://badge.fury.io/py/lightly-studio.svg)](https://pypi.org/project/lightly-studio)
46
- [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
47
-
48
- </p>
49
- </div>
50
-
51
- # 🚀 Welcome to LightlyStudio!
52
-
53
- We at **[Lightly](https://lightly.ai)** created **LightlyStudio**, an open-source tool
54
- designed to supercharge your data curation workflows for computer vision datasets. Explore
55
- your data, visualize annotations and crops, tag samples, and export curated lists to improve
56
- your machine learning pipelines. And much more!
57
-
58
- LightlyStudio runs entirely locally on your machine, keeping your data private. It consists
59
- of a Python library for indexing your data and a web-based UI for visualization and curation.
60
-
61
- ## ✨ Core Workflow
62
-
63
- Using LightlyStudio typically involves these steps:
64
-
65
- 1. **Index Your Dataset:** Run a Python script using the `lightly_studio` library to process your local dataset (images and annotations) and save metadata into a local `lightly_studio.db` file.
66
- 2. **Launch the UI:** The script then starts a local web server.
67
- 3. **Explore & Curate:** Use the UI to visualize images, annotations, and object crops. Filter and search your data (experimental text search available). Apply tags to interesting samples (e.g., "mislabeled", "review").
68
- 4. **Export Curated Data:** Export information (like filenames) for your tagged samples from the UI to use downstream.
69
- 5. **Stop the Server:** Close the terminal running the script (Ctrl+C) when done.
70
-
71
- <p align="center">
72
- <img alt="LightlyStudio Sample Grid View" src="https://storage.googleapis.com/lightly-public/studio/screenshot_grid_view.jpg" width="70%">
73
- <br/>
74
- <em>Visualize your dataset samples with annotations in the grid view.</em>
75
- </p>
76
- <p align="center">
77
- <img alt="LightlyStudio Annotation Crop View" src="https://storage.googleapis.com/lightly-public/studio/screenshot_annotation_view.jpg" width="70%">
78
- <br/>
79
- <em>Switch to the annotation view to inspect individual object crops easily.</em>
80
- </p>
81
- <p align="center">
82
- <img alt="LightlyStudio Sample Detail View" src="https://storage.googleapis.com/lightly-public/studio/screenshot_detail_view.jpg" width="70%">
83
- <br/>
84
- <em>Inspect individual samples in detail, viewing all annotations and metadata.</em>
85
- </p>
86
-
87
- ## 🎯 Features
88
-
89
- * **Local Web GUI:** Explore and curate your dataset in your browser. Works completelly
90
- offline, your data never leaves your machine.
91
- * **Flexible Input Formats:** Load your image dataset from a folder, or with annotations from
92
- a number of popular formats like e.g. COCO or YOLO.
93
- * **Metadata:** Attach your custom metadata to every sample.
94
- * **Tags:** Mark subsets of your dataset for later use.
95
- * **Embeddings:** Run similarity search queries on your data.
96
- * **Selection:** Run advanced selection algorithms to tag a subset of your data.
97
-
98
- ## 💻 Installation
99
-
100
- Ensure you have **Python 3.8 or higher**. We strongly recommend using a virtual environment.
101
-
102
- The library is OS-independent and works on Windows, Linux, and macOS.
103
-
104
- ```shell
105
- # 1. Create and activate a virtual environment (Recommended)
106
- # On Linux/macOS:
107
- python3 -m venv venv
108
- source venv/bin/activate
109
-
110
- # On Windows:
111
- python -m venv venv
112
- .\venv\Scripts\activate
113
-
114
- # 2. Install LightlyStudio
115
- pip install lightly_studio
116
- ```
117
-
118
- ## **Quickstart**
119
-
120
- Download the dataset and run a quickstart script to load your dataset and launch the app.
121
-
122
- ### YOLO Object Detection
123
-
124
- To run an example using a yolo dataset, clone the example repository and run the example script:
125
-
126
- ```shell
127
- git clone https://github.com/lightly-ai/dataset_examples_studio dataset_examples_studio
128
- python dataset_examples_studio/road_signs_yolo/example_yolo.py
129
- ```
130
-
131
- <details>
132
- <summary>The YOLO format details:</summary>
133
-
134
- ```
135
- road_signs_yolo/
136
- ├── train/
137
- │ ├── images/
138
- │ │ ├── image1.jpg
139
- │ │ ├── image2.jpg
140
- │ │ └── ...
141
- │ └── labels/
142
- │ ├── image1.txt
143
- │ ├── image2.txt
144
- │ └── ...
145
- ├── valid/ (optional)
146
- │ ├── images/
147
- │ │ └── ...
148
- │ └── labels/
149
- │ └── ...
150
- └── data.yaml
151
- ```
152
-
153
- Each label file should contain YOLO format annotations (one per line):
154
-
155
- ```
156
- <class> <x_center> <y_center> <width> <height>
157
- ```
158
-
159
- Where coordinates are normalized between 0 and 1.
160
-
161
- </details>
162
-
163
-
164
- <details>
165
- <summary>Let's break down the `example_yolo.py` script to explore the dataset:</summary>
166
-
167
- ```python
168
- # We import the DatasetLoader class from the lightly_studio module
169
- from lightly_studio import DatasetLoader
170
- from pathlib import Path
171
-
172
- # Create a DatasetLoader instance
173
- loader = DatasetLoader()
174
-
175
- data_yaml_path = Path(__file__).resolve().parent / "data.yaml"
176
- loader.from_yolo(
177
- data_yaml_path=str(data_yaml_path),
178
- input_split="test",
179
- )
180
-
181
- # We start the UI application on the port defined with the LIGHTLY_STUDIO_PORT env variable or 8001 by default.
182
- loader.start_gui()
183
- ```
184
- </details>
185
-
186
- </details>
187
-
188
- ### COCO Instance Segmentation
189
-
190
- To run an instance segmentation example using a COCO dataset, clone the example repository and run the example script:
191
-
192
- ```shell
193
- git clone https://github.com/lightly-ai/dataset_examples_studio dataset_examples_studio
194
- python dataset_examples_studio/coco_subset_128_images/example_coco.py
195
- ```
196
-
197
- <details>
198
- <summary>The COCO format details:</summary>
199
-
200
- ```
201
- coco_subset_128_images/
202
- ├── images/
203
- │ ├── image1.jpg
204
- │ ├── image2.jpg
205
- │ └── ...
206
- └── instances_train2017.json # Single JSON file containing all annotations
207
- ```
208
-
209
- COCO uses a single JSON file containing all annotations. The format consists of three main components:
210
-
211
- - Images: Defines metadata for each image in the dataset.
212
- - Categories: Defines the object classes.
213
- - Annotations: Defines object instances.
214
-
215
- </details>
216
-
217
- <details>
218
- <summary>Let's break down the `example_coco.py` script to explore the dataset:</summary>
219
-
220
- ```python
221
- # We import the DatasetLoader class from the lightly_studio module
222
- from lightly_studio import DatasetLoader
223
- from pathlib import Path
224
-
225
- # Create a DatasetLoader instance
226
- loader = DatasetLoader()
227
-
228
- current_dir = Path(__file__).resolve().parent
229
- loader.from_coco_instance_segmentations(
230
- annotations_json_path=str(current_dir / "instances_train2017.json"),
231
- img_dir=str(current_dir / "images"),
232
- )
233
-
234
- # We start the UI application on the port defined with the LIGHTLY_STUDIO_PORT env variable or 8001 by default.
235
- loader.start_gui()
236
-
237
- ```
238
- </details>
239
-
240
- </details>
241
-
242
- ## 🔍 How It Works
243
-
244
- 1. Your **Python script** uses the `lightly_studio` **Dataset Loader**.
245
- 2. The Loader reads your images and annotations, calculates embeddings, and saves metadata to a local **`lightly_studio.db`** file (using DuckDB).
246
- 3. `loader.start_gui()` starts a **local Backend API** server.
247
- 4. This server reads from `lightly_studio.db` and serves data to the **UI Application** running in your browser (`http://localhost:8001`).
248
- 5. Images are streamed directly from your disk for display in the UI.
249
-
250
- ## 🎯 Python Interface
251
-
252
- ### Load Image Dataset From A Folder
253
-
254
- ```py
255
- from lightly_studio import DatasetLoader
256
-
257
- loader = DatasetLoader()
258
- loader.from_directory(
259
- dataset_name="my-dataset",
260
- img_dir="/path/to/image_dataset",
261
- )
262
- loader.start_gui()
263
- ```
264
-
265
- ### Load Image Dataset With Annotations
266
-
267
- The `DatasetLoader` currently supports:
268
-
269
- - **YOLOv8 Object Detection:** Reads `.yaml` file. Supports bounding boxes.
270
- - **COCO Object Detection:** Reads `.json` annotations. Supports bounding boxes.
271
- - **COCO Instance Segmentation:** Reads `.json` annotations. Supports instance masks in RLE (Run-Length Encoding) format.
272
-
273
- ```py
274
- from lightly_studio import DatasetLoader
275
-
276
- # Create a DatasetLoader instance
277
- loader = DatasetLoader()
278
-
279
- # Load a dataset in YOLO format
280
- dataset = loader.from_yolo(
281
- data_yaml_path="my_yolo_dataset/data.yaml",
282
- input_split="test",
283
- )
284
-
285
- # Load an object detection dataset in COCO format
286
- dataset = loader.from_coco_object_detections(
287
- annotations_json_path="my_coco_dataset/detections_train.json",
288
- img_dir="my_coco_dataset/images",
289
- )
290
-
291
- # Load an instance segmentation dataset in COCO format
292
- dataset = loader.from_coco_instance_segmentations(
293
- annotations_json_path="my_coco_dataset/instances_train.json",
294
- img_dir="my_coco_dataset/images",
295
- )
296
-
297
- # Launch the GUI
298
- loader.start_gui()
299
- ```
300
-
301
- ### Samples
302
-
303
- The dataset consists of samples. Every sample corresponds to an image.
304
- Dataset samples can be fetched and accessed as follows,
305
- for a full list of attributes see [src/lightly_studio/models/sample.py]().
306
-
307
- ```py
308
- # Get all dataset samples
309
- samples = dataset.get_samples()
310
-
311
- # Access sample attributes
312
- s = samples[0]
313
- s.sample_id # Sample ID
314
- s.file_name # Image file name
315
- s.file_path_abs # Full image file path
316
- s.tags # The list of sample tags
317
- ...
318
- ```
319
-
320
- ### Sample Filtering
321
-
322
- You can efficiently fetch filtered dataset samples with the `get_samples()` method.
323
- All arguments are optional. A database object UUID is needed for filtering by
324
- annotation labels, tags and samples. They would be obtained by other function calls.
325
-
326
- ```py
327
- from lightly_studio.resolvers.samples_filter import FilterDimensions, SampleFilter
328
- from lightly_studio.resolvers.metadata_resolver.metadata_filter import Metadata
329
-
330
- sample1 = ...
331
- sample2 = ...
332
- ann1 = ...
333
- tag1 = ...
334
-
335
- # Example with available filters, all arguments are optional.
336
- samples = dataset.get_samples(
337
- # Set offset and limit to the list of returned samples.
338
- # Samples are ordered by their creation date.
339
- offset=0,
340
- limit=10,
341
-
342
- filters=SampleFilter(
343
- # Filter by width and/or height.
344
- width=FilterDimensions(min=10, max=200),
345
- height=FilterDimensions(min=10, max=None),
346
-
347
- # Filter by annotations.
348
- annotation_label_ids=[ann1.annotation_label_id],
349
-
350
- # Filter by tags.
351
- tag_ids=[tag1.tag_id],
352
-
353
- # Filter by metadata.
354
- metadata_filters=[
355
- Metadata("temperature") > 25.0,
356
- Metadata("nested.bool_key") == True,
357
- ]
358
- ),
359
-
360
- # Fetch only specific samples by IDs.
361
- sample_ids=[sample1.sample_id, sample2.sample_id],
362
- )
363
- ```
364
-
365
- Alternatively, samples can be filtered directly in python code:
366
-
367
- ```py
368
- my_samples = [
369
- sample in dataset.get_samples()
370
- if sample.file_name in ["image1.jpg", "image2.jpg"]
371
- ]
372
- ```
373
-
374
- ### Add Custom Metadata
375
-
376
- Attach values to custom fields for every sample.
377
-
378
- ```py
379
- from lightly_studio import DatasetLoader
380
-
381
- # Load your dataset
382
- loader = DatasetLoader()
383
- dataset = loader.from_directory(
384
- dataset_name="my-dataset",
385
- img_dir="/path/to/image_dataset",
386
- )
387
-
388
- # Attach metadata
389
- for sample in dataset.get_samples():
390
- sample["my_metadata"] = f"Example metadata field for {sample.file_name}"
391
- sample["my_dict"] = {"my_int_key": 10, "my_bool_key": True}
392
-
393
- # View metadata in GUI
394
- loader.start_gui()
395
- ```
396
-
397
- ### Tags
398
-
399
- You can easily mark subsets of your data with tags.
400
-
401
- ```py
402
- from lightly_studio.resolvers import tag_resolver
403
- from lightly_studio.models.tag import TagCreate
404
-
405
- # Load your dataset
406
- loader = DatasetLoader()
407
- dataset = ...
408
-
409
- # Create a tag
410
- my_tag = tag_resolver.create(
411
- session=loader.session,
412
- tag=TagCreate(dataset_id=dataset.dataset_id, name="my-tag"),
413
- )
414
-
415
- # Tag some samples, for example the first 10 samples:
416
- for sample in dataset.get_samples()[:10]:
417
- tag_resolver.add_tag_to_sample(
418
- session=loader.session,
419
- tag_id=my_tag.tag_id,
420
- sample=sample,
421
- )
422
- ```
423
-
424
- Find existing tags and tagged samples as follows.
425
-
426
- ```py
427
- from lightly_studio.resolvers import tag_resolver
428
- from lightly_studio.resolvers.samples_filter import SampleFilter
429
-
430
- # Get all tags
431
- all_tags = tag_resolver.get_all_by_dataset_id(
432
- session=loader.session,
433
- dataset_id=dataset.dataset_id,
434
- )
435
-
436
- # Get a tag by name
437
- my_tag = tag_resolver.get_by_name(
438
- session=loader.session,
439
- tag_name="my-tag"
440
- )
441
-
442
- # Get tagged samples
443
- tagged_samples = dataset.get_samples(filters=SampleFilter(tag_ids=[my_tag.tag_id]))
444
- ```
445
-
446
- ### Selection
447
-
448
- LightlyStudio offers as a premium feature advanced methods for subselecting dataset
449
- samples.
450
-
451
- **Prerequisites:** The selection functionality requires a valid LightlyStudio license key.
452
- Set the `LIGHTLY_STUDIO_LICENSE_KEY` environment variable before using selection features:
453
-
454
- ```bash
455
- export LIGHTLY_STUDIO_LICENSE_KEY="license_key_here"
456
- ```
457
- Alternatively, set it inside your Python script:
458
-
459
- ```py
460
- import os
461
- os.environ["LIGHTLY_STUDIO_LICENSE_KEY"] = "license_key_here"
462
- ```
463
-
464
- The selection can be configured with a rich interface of the `Selection` object. The
465
- example below showcases a simple case of selecting diverse samples.
466
-
467
- ```py
468
- import os
469
- from lightly_studio import DatasetLoader
470
- from lightly_studio.selection.select import Selection
471
-
472
- # Load your dataset
473
- loader = DatasetLoader()
474
- dataset = loader.from_directory(
475
- dataset_name="my-dataset",
476
- img_dir="/path/to/image_dataset",
477
- )
478
-
479
- # Select a diverse subset of 10 samples.
480
- select = Selection(dataset_id=dataset.dataset_id, session=loader.session)
481
- select.diverse(
482
- n_samples_to_select=10,
483
- selection_result_tag_name="diverse_selection",
484
- )
485
-
486
- loader.start_gui()
487
- ```
488
-
489
- The selected sample paths can be exported via the GUI, or by a script:
490
-
491
- ```py
492
- from lightly_studio.resolvers import tag_resolver
493
- from lightly_studio.resolvers.samples_filter import SampleFilter
494
-
495
- tag = tag_resolver.get_by_name(session=loader.session, tag_name="diverse_selection")
496
- selected_samples = dataset.get_samples(filters=SampleFilter(tag_ids=[tag.tag_id]))
497
-
498
- with open("export.txt", "w") as f:
499
- for sample in selected_samples:
500
- f.write(f"{sample.file_name}\n")
501
- ```
502
-
503
- ## 📚 **FAQ**
504
-
505
- ### Are the datasets persistent?
506
-
507
- Yes, the information about datasets is persistent and stored in the db file. You can see it after the dataset is processed.
508
- If you rerun the loader it will create a new dataset representing the same dataset, keeping the previous dataset information untouched.
509
-
510
- ### Can I change the database path?
511
-
512
- Not yet. The database is stored in the working directory by default.
513
-
514
- ### Can I launch in another Python script or do I have to do it in the same script?
515
-
516
- It is possible to use only one script at the same time because we lock the db file for the duration of the script.
517
-
518
- ### Can I change the API backend port?
519
-
520
- Yes. To change the port set the LIGHTLY_STUDIO_PORT variable to your preffered value. If at runtime the port is unavailable it will try to set it to a random value.