Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
{
"editor.formatOnSave": true,
"python.formatting.provider": "yapf",
"python.linting.pylintPath": "venv_sa_conv/bin/pylint",
"python.linting.pylintEnabled": true,
"python.testing.pytestArgs": [
"tests"
],
"python.testing.unittestEnabled": false,
"python.testing.nosetestsEnabled": false,
"python.testing.pytestEnabled": true,
"python.pythonPath": "venv_sa_conv/bin/python"
// "python.linting.pylintPath": "venv_sa_conv/bin/pylint",
// "python.linting.pylintEnabled": true,
// "python.testing.pytestArgs": [
// "tests"
// ],
// "python.testing.unittestEnabled": false,
// "python.testing.nosetestsEnabled": false,
// "python.testing.pytestEnabled": true,
// "python.pythonPath": "venv_sa_conv/bin/python"
}
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ pandas>=1.1.2
plotly>=4.1.0
ffmpeg-python>=0.2.0
google-cloud-storage>=1.33.0
azure-storage-blob>=12.6.0
azure-storage-blob>=12.6.0
mixpanel>=4.8.3
3 changes: 3 additions & 0 deletions superannotate/analytics/class_analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
from plotly.subplots import make_subplots

from .common import aggregate_annotations_as_df
from ..mixp.decorators import Trackable

logger = logging.getLogger("superannotate-python-sdk")


@Trackable
def class_distribution(export_root, project_names, visualize=False):
"""Aggregate distribution of classes across multiple projects.

Expand Down Expand Up @@ -63,6 +65,7 @@ def class_distribution(export_root, project_names, visualize=False):
return df


@Trackable
def attribute_distribution(export_root, project_names, visualize=False):
"""Aggregate distribution of attributes across multiple projects.

Expand Down
3 changes: 3 additions & 0 deletions superannotate/analytics/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
import pandas as pd

from ..exceptions import SABaseException
from ..mixp.decorators import Trackable

logger = logging.getLogger("superannotate-python-sdk")


@Trackable
def df_to_annotations(df, output_dir):
"""Converts and saves pandas DataFrame annotation info (see aggregate_annotations_as_df)
in output_dir.
Expand Down Expand Up @@ -148,6 +150,7 @@ def df_to_annotations(df, output_dir):
)


@Trackable
def aggregate_annotations_as_df(
project_root,
include_classes_wo_annotations=False,
Expand Down
20 changes: 14 additions & 6 deletions superannotate/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from .exceptions import SABaseException
from .version import __version__
from .mixp.app import mp, get_default

logger = logging.getLogger("superannotate-python-sdk")

Expand All @@ -24,10 +25,13 @@ def __init__(self):
self._default_headers = None
self._main_endpoint = None
self.team_id = None
self.user_id = None
self.team_name = None
if API.__instance is not None:
raise SABaseException(0, "API class is a singleton!")
API.__instance = self
self._authenticated = False
self.init()

def init(self, config_location=None):
if config_location is None:
Expand Down Expand Up @@ -78,15 +82,15 @@ def init(self, config_location=None):
self._verify = self._api_config["ssl_verify"]
self._session = None
self._authenticated = True

response = self.send_request(
req_type='GET',
path='/projects',
params={
'team_id': str(self.team_id),
'offset': 0,
'limit': 1
}
path=f'/team/{self.team_id}',
)

self.user_id = response.json()['creator_id']
self.team_name = response.json()['name']

if not self._verify:
urllib3.disable_warnings(
urllib3.exceptions.InsecureRequestWarning
Expand All @@ -102,6 +106,10 @@ def init(self, config_location=None):
raise SABaseException(
0, "Couldn't reach superannotate " + response.text
)
mp.track(
self.user_id, "SDK init",
get_default(self.team_name, self.user_id)
)
except SABaseException:
self._authenticated = False
self._session = None
Expand Down
2 changes: 2 additions & 0 deletions superannotate/consensus_benchmark/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
from .helpers import image_consensus, consensus_plot
from ..db.exports import prepare_export, download_export
from ..analytics.common import aggregate_annotations_as_df
from ..mixp.decorators import Trackable

logger = logging.getLogger("superannotate-python-sdk")


@Trackable
def benchmark(
project,
gt_folder,
Expand Down
2 changes: 2 additions & 0 deletions superannotate/consensus_benchmark/consensus.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
from .helpers import image_consensus, consensus_plot
from ..db.exports import prepare_export, download_export
from ..analytics.common import aggregate_annotations_as_df
from ..mixp.decorators import Trackable

logger = logging.getLogger("superannotate-python-sdk")


@Trackable
def consensus(
project,
folder_names,
Expand Down
19 changes: 15 additions & 4 deletions superannotate/consensus_benchmark/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,32 +80,43 @@ def image_consensus(df, image_name, annot_type):
"Invalid %s instance occured, skipping to the next one.",
annot_type
)
visited_instances = {}
for proj, instances in projects_shaply_objs.items():
visited_instances[proj] = [False] * len(instances)

# match instances
for curr_proj, curr_proj_instances in projects_shaply_objs.items():
for curr_inst_data in curr_proj_instances:
for curr_id, curr_inst_data in enumerate(curr_proj_instances):
curr_inst, curr_class, _, _ = curr_inst_data
if visited_instances[curr_proj][curr_id] == True:
continue
max_instances = []
for other_proj, other_proj_instances in projects_shaply_objs.items(
):
if curr_proj == other_proj:
max_instances.append((curr_proj, *curr_inst_data))
projects_shaply_objs[curr_proj].remove(curr_inst_data)
visited_instances[curr_proj][curr_id] = True
else:
if annot_type in ['polygon', 'bbox']:
max_score = 0
else:
max_score = float('-inf')
max_inst_data = None
for other_inst_data in other_proj_instances:
max_inst_id = -1
for other_id, other_inst_data in enumerate(
other_proj_instances
):
other_inst, other_class, _, _ = other_inst_data
if visited_instances[other_proj][other_id] == True:
continue
score = instance_consensus(curr_inst, other_inst)
if score > max_score and other_class == curr_class:
max_score = score
max_inst_data = other_inst_data
max_inst_id = other_id
if max_inst_data is not None:
max_instances.append((other_proj, *max_inst_data))
projects_shaply_objs[other_proj].remove(max_inst_data)
visited_instances[other_proj][max_inst_id] = True
if len(max_instances) == 1:
image_data["creatorEmail"].append(max_instances[0][3])
image_data["attributes"].append(max_instances[0][4])
Expand Down
4 changes: 4 additions & 0 deletions superannotate/dataframe_filtering.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import pandas as pd
from .mixp.decorators import Trackable


@Trackable
def filter_images_by_comments(
annotations_df,
include_unresolved_comments=True,
Expand Down Expand Up @@ -40,6 +42,7 @@ def filter_images_by_comments(
return list(images)


@Trackable
def filter_images_by_tags(annotations_df, include=None, exclude=None):
"""Filter images on tags

Expand Down Expand Up @@ -74,6 +77,7 @@ def filter_images_by_tags(annotations_df, include=None, exclude=None):
return list(images)


@Trackable
def filter_annotation_instances(annotations_df, include=None, exclude=None):
"""Filter annotation instances from project annotations pandas DataFrame.

Expand Down
7 changes: 7 additions & 0 deletions superannotate/db/annotation_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
SANonExistingAnnotationClassNameException
)
from .project_api import get_project_metadata_bare
from ..mixp.decorators import Trackable

logger = logging.getLogger("superannotate-python-sdk")

_api = API.get_instance()


@Trackable
def create_annotation_class(project, name, color, attribute_groups=None):
"""Create annotation class in project

Expand Down Expand Up @@ -79,6 +81,7 @@ def create_annotation_class(project, name, color, attribute_groups=None):
return new_class


@Trackable
def delete_annotation_class(project, annotation_class):
"""Deletes annotation class from project

Expand Down Expand Up @@ -113,6 +116,7 @@ def delete_annotation_class(project, annotation_class):
)


@Trackable
def create_annotation_classes_from_classes_json(
project, classes_json, from_s3_bucket=None
):
Expand Down Expand Up @@ -200,6 +204,7 @@ def del_unn(d):
return res


@Trackable
def search_annotation_classes(project, name_prefix=None):
"""Searches annotation classes by name_prefix (case-insensitive)

Expand Down Expand Up @@ -239,6 +244,7 @@ def search_annotation_classes(project, name_prefix=None):
return result_list


@Trackable
def get_annotation_class_metadata(project, annotation_class_name):
"""Returns annotation class metadata

Expand Down Expand Up @@ -272,6 +278,7 @@ def get_annotation_class_metadata(project, annotation_class_name):
)


@Trackable
def download_annotation_classes_json(project, folder):
"""Downloads project classes.json to folder

Expand Down
4 changes: 4 additions & 0 deletions superannotate/db/exports.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
SANonExistingExportNameException
)
from .project_api import get_project_metadata_bare
from ..mixp.decorators import Trackable

logger = logging.getLogger("superannotate-python-sdk")

Expand Down Expand Up @@ -56,6 +57,7 @@ def get_export_metadata(project, export_name):
)


@Trackable
def get_exports(project, return_metadata=False):
"""Get all prepared exports of the project.

Expand Down Expand Up @@ -97,6 +99,7 @@ def _get_export(export):
return response.json()


@Trackable
def prepare_export(
project,
folder_names=None,
Expand Down Expand Up @@ -219,6 +222,7 @@ def _download_file(url, local_filename):
return local_filename


@Trackable
def download_export(
project, export, folder_path, extract_zip_contents=True, to_s3_bucket=None
):
Expand Down
Loading