Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
f6b1e00
new assign - initial
dshabin May 24, 2021
2eda4c8
Add folder assign test
dshabin May 25, 2021
a5d71a9
Fix tests - folder assign - unassign
dshabin May 27, 2021
7e808bf
Add tests
dshabin May 31, 2021
bb68f0f
Merge pull request #33 from superannotateai/folder-assign-fix
davitavagyan May 31, 2021
9445e03
Update version.py
dshabin Jun 1, 2021
fa497da
Merge pull request #36 from superannotateai/folder-assign
dshabin Jun 1, 2021
1bc8fd7
Update requirements_extra.txt
dshabin Jun 1, 2021
c300eb5
Add neural networks docs
dshabin Jun 1, 2021
716a4fa
Update version.py
dshabin Jun 1, 2021
2e1a150
Fix neural networks docs
dshabin Jun 1, 2021
03f4e97
Merge branch 'develop' of https://github.com/superannotateai/superann…
dshabin Jun 1, 2021
67d2dac
Update version.py
dshabin Jun 1, 2021
7b6c348
Fix folder assign text
dshabin Jun 1, 2021
599d5d6
Fix multi template coco converter
dshabin Jun 2, 2021
cca132b
Fix template names - coco convert
dshabin Jun 2, 2021
da56c12
Fix SAS-3401 - SAS-2283
dshabin Jun 3, 2021
7b6122c
Merge pull request #37 from superannotateai/SAS-3383
dshabin Jun 3, 2021
3c2fef9
Update version.py
dshabin Jun 3, 2021
17c933d
Fix image unassign
dshabin Jun 7, 2021
fc530ad
Merge pull request #42 from superannotateai/SAS-3432
dshabin Jun 7, 2021
17cccf6
Update version.py
dshabin Jun 7, 2021
de021a3
Add mixp events
dshabin Jun 7, 2021
e7e5ade
Merge pull request #43 from superannotateai/SAS-3444
dshabin Jun 7, 2021
222b78d
Update version.py
dshabin Jun 7, 2021
9ef9f48
assign images by chunk
dshabin Jun 8, 2021
29bdb34
Fix folder-image assign-unassign
dshabin Jun 8, 2021
b46054a
Merge pull request #45 from superannotateai/SAS-3457
dshabin Jun 8, 2021
5c259f6
Update version.py
dshabin Jun 8, 2021
2aa61ab
Fix empty image names
dshabin Jun 8, 2021
6ca38be
Fix checks- remove contniue
dshabin Jun 8, 2021
8a89fa0
Merge pull request #46 from superannotateai/SAS-3457
dshabin Jun 8, 2021
144a23f
Update version.py
dshabin Jun 8, 2021
edbaad2
Fix logger
dshabin Jun 9, 2021
91bbcaf
Merge branch 'develop' of https://github.com/superannotateai/superann…
dshabin Jun 9, 2021
5186dd1
Update version.py
dshabin Jun 9, 2021
36d1070
Update version.py
dshabin Jun 9, 2021
ab972ea
Fix merge
dshabin Jun 16, 2021
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
16 changes: 15 additions & 1 deletion docs/source/superannotate.sdk.rst
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,20 @@ _________________

----------

Neural Network
_______________

.. autofunction:: superannotate.delete_model
.. autofunction:: superannotate.download_model
.. autofunction:: superannotate.plot_model_metrics
.. autofunction:: superannotate.run_prediction
.. autofunction:: superannotate.run_segmentation
.. autofunction:: superannotate.run_training
.. autofunction:: superannotate.stop_model_training
.. autofunction:: superannotate.search_models

----------


.. _ref_metadata:

Expand Down Expand Up @@ -327,4 +341,4 @@ Utility functions

.. autofunction:: superannotate.dicom_to_rgb_sequence
.. autofunction:: superannotate.consensus
.. autofunction:: superannotate.benchmark
.. autofunction:: superannotate.benchmark
3 changes: 2 additions & 1 deletion superannotate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ def consensus(*args, **kwargs):
)
from .db.project_images import (
assign_images, copy_image, copy_images, delete_images, move_image,
move_images, pin_image, upload_image_to_project
move_images, pin_image, upload_image_to_project, assign_folder,
unassign_folder, unassign_images
)
from .db.projects import (
clone_project, create_project, create_project_from_metadata, delete_project,
Expand Down
7 changes: 6 additions & 1 deletion superannotate/db/annotation_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
)
from .project_api import get_project_metadata_bare
from ..mixp.decorators import Trackable
from .utils import get_templates_mapping

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

Expand Down Expand Up @@ -344,7 +345,11 @@ def fill_class_and_attribute_ids(annotation_json, annotation_classes_dict):
'attribute_groups': {}
}
annotation_classes_dict = {**annotation_classes_dict, **unknown_classes}

templates_map = get_templates_mapping()
for ann in (
i for i in annotation_json["instances"] if i['type'] == 'template'
):
ann['templateId'] = templates_map.get(ann['templateName'], -1)
for ann in annotation_json["instances"]:
if "className" not in ann:
logger.warning("No className in annotation instance")
Expand Down
211 changes: 155 additions & 56 deletions superannotate/db/project_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,26 @@
)
from .project_api import get_project_and_folder_metadata
from .projects import (
get_project_default_image_quality_in_editor, _get_available_image_counts
get_project_default_image_quality_in_editor, _get_available_image_counts,
get_project_metadata
)
from .teams import get_team_metadata
from ..mixp.decorators import Trackable
from .utils import _get_upload_auth_token, _get_boto_session_by_credentials, upload_image_array_to_s3, get_image_array_to_upload, __create_image, __copy_images, __move_images, get_project_folder_string
from .utils import _unassign_images, _assign_images, _get_upload_auth_token, _get_boto_session_by_credentials, upload_image_array_to_s3, \
get_image_array_to_upload, __create_image, __copy_images, __move_images, get_project_folder_string

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


@Trackable
def upload_image_to_project(
project,
img,
image_name=None,
annotation_status="NotStarted",
from_s3_bucket=None,
image_quality_in_editor=None
project,
img,
image_name=None,
annotation_status="NotStarted",
from_s3_bucket=None,
image_quality_in_editor=None
):
"""Uploads image (io.BytesIO() or filepath to image) to project.
Sets status of the uploaded image to set_status if it is not None.
Expand Down Expand Up @@ -134,8 +137,8 @@ def upload_image_to_project(


def _copy_images(
source_project, destination_project, image_names, include_annotations,
copy_annotation_status, copy_pin
source_project, destination_project, image_names, include_annotations,
copy_annotation_status, copy_pin
):
NUM_TO_SEND = 500
source_project, source_project_folder = source_project
Expand Down Expand Up @@ -164,7 +167,7 @@ def _copy_images(
res['completed'] = []
for start_index in range(0, len(image_names), NUM_TO_SEND):
json_req["image_names"] = image_names[start_index:start_index +
NUM_TO_SEND]
NUM_TO_SEND]
response = _api.send_request(
req_type='POST',
path='/image/copy',
Expand All @@ -190,12 +193,12 @@ def _copy_images(

@Trackable
def copy_images(
source_project,
image_names,
destination_project,
include_annotations=True,
copy_annotation_status=True,
copy_pin=True
source_project,
image_names,
destination_project,
include_annotations=True,
copy_annotation_status=True,
copy_pin=True
):
"""Copy images in bulk between folders in a project

Expand Down Expand Up @@ -305,12 +308,12 @@ def delete_images(project, image_names):

@Trackable
def move_images(
source_project,
image_names,
destination_project,
include_annotations=True,
copy_annotation_status=True,
copy_pin=True,
source_project,
image_names,
destination_project,
include_annotations=True,
copy_annotation_status=True,
copy_pin=True,
):
"""Move images in bulk between folders in a project

Expand Down Expand Up @@ -370,12 +373,12 @@ def move_images(

@Trackable
def copy_image(
source_project,
image_name,
destination_project,
include_annotations=False,
copy_annotation_status=False,
copy_pin=False
source_project,
image_name,
destination_project,
include_annotations=False,
copy_annotation_status=False,
copy_pin=False
):
"""Copy image to a project. The image's project is the same as destination
project then the name will be changed to <image_name>_(<num>).<image_ext>,
Expand Down Expand Up @@ -415,10 +418,10 @@ def copy_image(
else:
for m in p.finditer(new_name):
if m.start() + len(m.group()
) + len(extension) - 1 == len(new_name):
) + len(extension) - 1 == len(new_name):
num = int(m.group()[2:-2])
new_name = new_name[:m.start() +
2] + str(num + 1) + ")" + extension
2] + str(num + 1) + ")" + extension
break
else:
new_name = Path(new_name).stem + "_(1)" + extension
Expand All @@ -438,9 +441,9 @@ def copy_image(


def _copy_annotations_and_metadata(
source_project, source_project_folder, image_name, destination_project,
destination_project_folder, new_name, include_annotations,
copy_annotation_status, copy_pin
source_project, source_project_folder, image_name, destination_project,
destination_project_folder, new_name, include_annotations,
copy_annotation_status, copy_pin
):
if include_annotations:
annotations = get_image_annotations(
Expand Down Expand Up @@ -477,12 +480,12 @@ def _copy_annotations_and_metadata(

@Trackable
def move_image(
source_project,
image_name,
destination_project,
include_annotations=True,
copy_annotation_status=True,
copy_pin=True
source_project,
image_name,
destination_project,
include_annotations=True,
copy_annotation_status=True,
copy_pin=True
):
"""Move image from source_project to destination_project. source_project
and destination_project cannot be the same.
Expand Down Expand Up @@ -563,30 +566,126 @@ def assign_images(project, image_names, user):
:param user: user email
:type user: str
"""
logger.info("Assign %s images to user %s", len(image_names), user)
if len(image_names) == 0:
return
project, project_folder = get_project_and_folder_metadata(project)
images = search_images((project, project_folder), return_metadata=True)
image_dict = {}
for image in images:
image_dict[image["name"]] = image["id"]

image_ids = []
for image_name in image_names:
image_ids.append(image_dict[image_name])
team_id, project_id = project["team_id"], project["id"]
params = {"team_id": team_id, "project_id": project_id}
if project_folder is not None:
params['folder_id'] = project_folder['id']
json_req = {"user_id": user, "image_ids": image_ids}
project, folder = get_project_and_folder_metadata(project)
project_meta = get_project_metadata(project, include_contributors=True)
verified_users = project_meta["contributors"]
verified_users = [i['user_id'] for i in verified_users]
if user not in verified_users:
logger.warn(
f'Skipping {user}. {user} is not a verified contributor for the {project["name"]}'
)
return

folder_name = 'root'
if folder:
folder_name = folder['name']

logs = _assign_images(folder_name=folder_name, image_names=image_names, user=user, project_id=project['id'],
team_id=project['team_id'])
for log in logs:
logger.warn(log)
logger.info("Assign images to user %s", user)

@Trackable
def assign_folder(project, folder_name, users):
"""Assigns folder to users. With SDK, the user can be
assigned to a role in the project with the share_project function.

:param project: project name or metadata of the project
:type project: str or dict
:param folder_name: folder name to assign
:type folder_name: str
:param users: list of user emails
:type user: list of str
"""

project_meta = get_project_metadata(project, include_contributors=True)
verified_users = project_meta["contributors"]
verified_users = [i['user_id'] for i in verified_users]
project_name = project_meta['name']
verified_users = set(users).intersection(set(verified_users))
unverified_contributor = set(users) - verified_users

for user in unverified_contributor:
logger.warn(
f'Skipping {user} from assignees. {user} is not a verified contributor for the {project_name}'
)

if not verified_users:
return

params = {
"project_id": project_meta['id'],
"team_id": project_meta["team_id"]
}
json_req = {
"assign_user_ids": list(verified_users),
"folder_name": folder_name
}
response = _api.send_request(
req_type='POST',
path='/images/assign',
path='/folder/editAssignment',
params=params,
json_req=json_req
)

if not response.ok:
raise SABaseException(
response.status_code, "Couldn't assign images " + response.text
response.status_code, "Couldn't assign folder " + response.text
)
logger.info(f'Assigned {folder_name} to users: {list(verified_users)}')

@Trackable
def unassign_folder(project, folder_name):
"""Removes assignment of given folder for all assignees.
With SDK, the user can be assigned to a role in the project
with the share_project function.

:param project: project name or folder path (e.g., "project1/folder1")
:type project: str
:param folder_name: folder name to remove assignees
:type folder_name: str
"""

project_meta = get_project_metadata(project)
params = {
"project_id": project_meta['id'],
"team_id": project_meta["team_id"]
}
json_req = {"folder_name": folder_name, "remove_user_ids": ["all"]}
response = _api.send_request(
req_type='POST',
path='/folder/editAssignment',
params=params,
json_req=json_req
)

if not response.ok:
raise SABaseException(
response.status_code, "Couldn't unassign folder " + response.text
)

@Trackable
def unassign_images(project, image_names):
"""Removes assignment of given images for all assignees.With SDK,
the user can be assigned to a role in the project with the share_project
function.

:param project: project name or folder path (e.g., "project1/folder1")
:type project: str
:param image_names: list of image unassign
:type image_names: list of str
"""
if not image_names:
return
project, folder = get_project_and_folder_metadata(project)

folder_name = 'root'
if folder:
folder_name = folder['name']
logs = _unassign_images(folder_name=folder_name,image_names=image_names,project_id=project['id'],team_id=project['team_id'])
for log in logs:
logger.warn(log)
Loading