Skip to content

Commit 339d734

Browse files
authored
Merge pull request #626 from superannotateai/fix_issues
fix issues
2 parents 0a94e2e + 860cf01 commit 339d734

File tree

16 files changed

+141
-90
lines changed

16 files changed

+141
-90
lines changed

docs/source/api_reference/helpers.rst

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,3 @@ ________________________
2222
.. _ref_aggregate_annotations_as_df:
2323
.. automethod:: superannotate.SAClient.validate_annotations
2424
.. automethod:: superannotate.SAClient.aggregate_annotations_as_df
25-
26-
----------
27-
28-
Utility functions
29-
--------------------------------
30-
31-
.. autofunction:: superannotate.SAClient.consensus

docs/source/userguide/quickstart.rst

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,6 @@ It can be installed on Ubuntu with:
2525
2626
sudo apt-get install ffmpeg
2727
28-
To use the :py:obj:`consensus` function on Windows and Mac platforms, you might also need to install the shapely package
29-
beforehand. The package works well only under the Anaconda distribution with:
30-
31-
.. code-block:: bash
32-
33-
conda install shapely
34-
3528
----------
3629

3730

docs/source/userguide/utilities.rst

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -126,64 +126,3 @@ Example of created DataFrame:
126126

127127
Each row represents annotation information. One full annotation with multiple
128128
attribute groups can be grouped under :code:`instanceId` field.
129-
130-
131-
Working with DICOM files
132-
------------------------
133-
134-
JPEG images with names :file:`<dicom_file_name>_<frame_num>.jpg` will be created
135-
in :file:`<path_to_output_dir>`. Those JPEG images can be uploaded to
136-
SuperAnnotate platform using the regular:
137-
138-
.. code-block:: python
139-
140-
sa.upload_images_from_folder_to_project(project, "<path_to_output_dir>")
141-
142-
Some DICOM files can have image frames that are compressed. To load them, `GDCM :
143-
Grassroots DICOM library <http://gdcm.sourceforge.net/wiki/index.php/Main_Page>`_ needs to be installed:
144-
145-
.. code-block:: bash
146-
147-
# using conda
148-
conda install -c conda-forge gdcm
149-
150-
# or on Ubuntu with versions above 19.04
151-
sudo apt install python3-gdcm
152-
153-
Computing consensus scores for instances between several projects
154-
-----------------------------------------------------------------
155-
156-
157-
Consensus is a tool to compare the quallity of the annotations of the same image that is present in several projects.
158-
To compute the consensus scores:
159-
160-
.. code-block:: python
161-
162-
res_df = sa.consensus([project_names], "<path_to_export_folder>", [image_list], "<annotation_type>")
163-
164-
Here pandas DataFrame with following columns is returned: creatorEmail, imageName, instanceId, className, area, attribute, projectName, score
165-
166-
.. image:: images/consensus_dataframe.png
167-
168-
Besides the pandas DataFrame there is an option to get the following plots by setting the show_plots flag to True:
169-
170-
* Box plot of consensus scores for each annotators
171-
* Box plot of consensus scores for each project
172-
* Scatter plots of consensus score vs instance area for each project
173-
174-
.. code-block:: python
175-
176-
sa.consensus([project_names], "<path_to_export_folder>", [image_list], "<annotation_type>", show_plots=True)
177-
178-
To the left of each box plot the original score points of that annotator is depicted, the box plots are colored by annotator.
179-
180-
.. image:: images/consensus_annotators_box.png
181-
182-
Analogically the box plots of consensus scores for each project are colored according to project name.
183-
184-
.. image:: images/consensus_projects_box.png
185-
186-
Scatter plot of consensus score vs instance area is separated by projects. Hovering on a point reveals its annotator and image name.
187-
The points are colored according to class name. Each annotator is represented with separate symbol.
188-
189-
.. image:: images/consensus_scatter.png

src/superannotate/lib/app/interface/sdk_interface.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,10 +1514,6 @@ def download_export(
15141514
to_s3_bucket=None,
15151515
):
15161516
"""Download prepared export.
1517-
1518-
WARNING: Starting from version 1.9.0 :ref:`download_export <ref_download_export>` additionally
1519-
requires :py:obj:`project` as first argument.
1520-
15211517
:param project: project name
15221518
:type project: str
15231519
:param export: export name

src/superannotate/lib/core/__init__.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,36 @@ def setup_logging(level=DEFAULT_LOGGING_LEVEL, file_path=LOG_FILE_LOCATION):
153153

154154
INVALID_JSON_MESSAGE = "Invalid json"
155155

156+
PROJECT_SETTINGS_VALID_ATTRIBUTES = [
157+
"Brightness",
158+
"Fill",
159+
"Contrast",
160+
"ShowLabels",
161+
"ShowComments",
162+
"Image",
163+
"Lines",
164+
"AnnotatorFinish",
165+
"PointSize",
166+
"FontSize",
167+
"WorkflowEnable",
168+
"ClassChange",
169+
"ShowEntropy",
170+
"UploadImages",
171+
"DeleteImages",
172+
"Download",
173+
"RunPredictions",
174+
"RunSegmentations",
175+
"ImageQuality",
176+
"ImageAutoAssignCount",
177+
"FrameMode",
178+
"FrameRate",
179+
"JumpBackward",
180+
"JumpForward",
181+
"UploadFileType",
182+
"Tokenization",
183+
"ImageAutoAssignEnable",
184+
]
185+
156186
__alL__ = (
157187
FolderStatus,
158188
ProjectStatus,

src/superannotate/lib/core/repositories.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,13 @@ def __init__(
3636
secret_key: str,
3737
session_token: str,
3838
bucket: str,
39+
region: str,
3940
):
4041
self._session = boto3.Session(
4142
aws_access_key_id=access_key,
4243
aws_secret_access_key=secret_key,
4344
aws_session_token=session_token,
45+
region_name=region,
4446
)
4547

4648
self._resource = self._session.resource("s3")

src/superannotate/lib/core/usecases/annotations.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -591,13 +591,20 @@ def prepare_annotation(self, annotation: dict, size) -> dict:
591591
)
592592
return annotation
593593

594+
@staticmethod
595+
def get_mask_path(path: str) -> str:
596+
if path.endswith(constants.PIXEL_ANNOTATION_POSTFIX):
597+
replacement = constants.PIXEL_ANNOTATION_POSTFIX
598+
else:
599+
replacement = ".json"
600+
parts = path.rsplit(replacement, 1)
601+
return constants.ANNOTATION_MASK_POSTFIX.join(parts)
602+
594603
async def get_annotation(
595604
self, path: str
596605
) -> (Optional[Tuple[io.StringIO]], Optional[io.BytesIO]):
597606
mask = None
598-
mask_path = path.replace(
599-
constants.PIXEL_ANNOTATION_POSTFIX, constants.ANNOTATION_MASK_POSTFIX
600-
)
607+
mask_path = self.get_mask_path(path)
601608
if self._client_s3_bucket:
602609
content = self.get_annotation_from_s3(self._client_s3_bucket, path).read()
603610
if self._project.type == constants.ProjectType.PIXEL.value:

src/superannotate/lib/core/usecases/images.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,7 @@ def s3_repo(self):
756756
self._auth_data.data["secretAccessKey"],
757757
self._auth_data.data["sessionToken"],
758758
self._auth_data.data["bucket"],
759+
self._auth_data.data["region"],
759760
)
760761

761762
def validate_project_type(self):
@@ -952,6 +953,7 @@ def s3_repository(self):
952953
self.auth_data["secretAccessKey"],
953954
self.auth_data["sessionToken"],
954955
self.auth_data["bucket"],
956+
self.auth_data["region"],
955957
)
956958
return self._s3_repo_instance
957959

src/superannotate/lib/core/usecases/projects.py

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ def __init__(
177177

178178
def validate_settings(self):
179179
for setting in self._project.settings[:]:
180-
if setting.attribute == "WorkflowType":
180+
if setting.attribute not in constances.PROJECT_SETTINGS_VALID_ATTRIBUTES:
181181
self._project.settings.remove(setting)
182182
if setting.attribute == "ImageQuality" and isinstance(setting.value, str):
183183
setting.value = constances.ImageQuality.get_value(setting.value)
@@ -335,6 +335,39 @@ def __init__(
335335
self._project = project
336336
self._service_provider = service_provider
337337

338+
def validate_settings(self):
339+
for setting in self._project.settings[:]:
340+
if setting.attribute not in constances.PROJECT_SETTINGS_VALID_ATTRIBUTES:
341+
self._project.settings.remove(setting)
342+
if setting.attribute == "ImageQuality" and isinstance(setting.value, str):
343+
setting.value = constances.ImageQuality.get_value(setting.value)
344+
elif setting.attribute == "FrameRate":
345+
if not self._project.type == constances.ProjectType.VIDEO.value:
346+
raise AppValidationException(
347+
"FrameRate is available only for Video projects"
348+
)
349+
try:
350+
setting.value = float(setting.value)
351+
if (
352+
not (0.0001 < setting.value < 120)
353+
or decimal.Decimal(str(setting.value)).as_tuple().exponent < -3
354+
):
355+
raise AppValidationException(
356+
"The FrameRate value range is between 0.001 - 120"
357+
)
358+
frame_mode = next(
359+
filter(
360+
lambda x: x.attribute == "FrameMode", self._project.settings
361+
),
362+
None,
363+
)
364+
if not frame_mode:
365+
self._project.settings.append(
366+
SettingEntity(attribute="FrameMode", value=1)
367+
)
368+
except ValueError:
369+
raise AppValidationException("The FrameRate value should be float")
370+
338371
def validate_project_name(self):
339372
if self._project.name:
340373
if (
@@ -488,13 +521,17 @@ def execute(self):
488521

489522
new_settings_to_update = []
490523
for new_setting in self._to_update:
491-
new_settings_to_update.append(
492-
SettingEntity(
493-
id=attr_id_mapping[new_setting["attribute"]],
494-
attribute=new_setting["attribute"],
495-
value=new_setting["value"],
524+
if (
525+
new_setting["attribute"]
526+
in constances.PROJECT_SETTINGS_VALID_ATTRIBUTES
527+
):
528+
new_settings_to_update.append(
529+
SettingEntity(
530+
id=attr_id_mapping[new_setting["attribute"]],
531+
attribute=new_setting["attribute"],
532+
value=new_setting["value"],
533+
)
496534
)
497-
)
498535

499536
response = self._service_provider.projects.set_settings(
500537
project=self._project,

src/superannotate/lib/infrastructure/controller.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ def _get_s3_repository(self, project: ProjectEntity, folder: FolderEntity):
214214
auth_data["secretAccessKey"],
215215
auth_data["sessionToken"],
216216
auth_data["bucket"],
217+
auth_data["region"],
217218
)
218219

219220
def create(self, project: ProjectEntity, annotation_class: AnnotationClassEntity):

0 commit comments

Comments
 (0)