Skip to content

Commit

Permalink
Merge branch 'main' into rgangire/ood_detection_trigger
Browse files Browse the repository at this point in the history
  • Loading branch information
rajeshgangireddy committed Oct 18, 2024
2 parents 95fcca9 + 7e98a90 commit 7c951c7
Show file tree
Hide file tree
Showing 83 changed files with 831 additions and 379 deletions.
33 changes: 33 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

# Required
version: 2

# Set the OS, Python version and other tools you might need
build:
os: ubuntu-22.04
tools:
python: "3.9"
# You can also specify other tool versions:
# nodejs: "19"
# rust: "1.64"
# golang: "1.19"

# Build documentation in the "docs/" directory with Sphinx
sphinx:
configuration: docs/source/conf.py

# Optionally build your docs in additional formats such as PDF and ePub
# formats:
# - pdf
# - epub

# Optional but recommended, declare the Python requirements required
# to build your documentation
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
python:
install:
- requirements: requirements/requirements.txt
- requirements: requirements/requirements-docs.txt
6 changes: 3 additions & 3 deletions examples/upload_and_predict_from_numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def rotate_image(image: np.ndarray, angle: float) -> np.ndarray:
rotated_image = rotate_image(image=numpy_image, angle=20)

# Make sure that the project exists
ensure_trained_example_project(geti=geti, project_name=PROJECT_NAME)
project = ensure_trained_example_project(geti=geti, project_name=PROJECT_NAME)

print(
"Uploading and predicting example image now... The prediction results will be "
Expand All @@ -71,7 +71,7 @@ def rotate_image(image: np.ndarray, angle: float) -> np.ndarray:

# We can upload and predict the resulting array directly:
sc_image, image_prediction = geti.upload_and_predict_image(
project_name=PROJECT_NAME,
project=project,
image=rotated_image,
visualise_output=False,
delete_after_prediction=DELETE_AFTER_PREDICTION,
Expand Down Expand Up @@ -100,7 +100,7 @@ def rotate_image(image: np.ndarray, angle: float) -> np.ndarray:
print("Video generated, retrieving predictions...")
# Create video, upload and predict from the list of frames
sc_video, video_frames, frame_predictions = geti.upload_and_predict_video(
project_name=PROJECT_NAME,
project=project,
video=rotation_video,
frame_stride=1,
visualise_output=False,
Expand Down
4 changes: 2 additions & 2 deletions examples/upload_and_predict_media_from_folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@
# --------------------------------------------------

# Make sure that the specified project exists on the server
ensure_trained_example_project(geti=geti, project_name=PROJECT_NAME)
project = ensure_trained_example_project(geti=geti, project_name=PROJECT_NAME)

# Upload the media in the folder and generate predictions
geti.upload_and_predict_media_folder(
project_name=PROJECT_NAME,
project=project,
media_folder=FOLDER_WITH_MEDIA,
delete_after_prediction=DELETE_AFTER_PREDICTION,
output_folder=OUTPUT_FOLDER,
Expand Down
2 changes: 2 additions & 0 deletions geti_sdk/annotation_readers/base_annotation_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ def __init__(
base_data_folder: str,
annotation_format: str = ".json",
task_type: Union[TaskType, str] = TaskType.DETECTION,
anomaly_reduction: bool = False,
):
if task_type is not None and not isinstance(task_type, TaskType):
task_type = TaskType(task_type)
self.base_folder = base_data_folder
self.annotation_format = annotation_format
self.task_type = task_type
self.anomaly_reduction = anomaly_reduction

self._filepaths: Optional[List[str]] = None

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class DatumAnnotationReader(AnnotationReader):
TaskType.ANOMALY_CLASSIFICATION,
TaskType.ANOMALY_DETECTION,
TaskType.ANOMALY_SEGMENTATION,
TaskType.ANOMALY,
]

def __init__(
Expand Down
23 changes: 23 additions & 0 deletions geti_sdk/annotation_readers/geti_annotation_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

from geti_sdk.data_models import Annotation, TaskType
from geti_sdk.data_models.media import MediaInformation
from geti_sdk.data_models.shapes import Rectangle
from geti_sdk.rest_converters import AnnotationRESTConverter
from geti_sdk.rest_converters.annotation_rest_converter import (
NormalizedAnnotationRESTConverter,
Expand All @@ -41,6 +42,7 @@ def __init__(
annotation_format: str = ".json",
task_type: Optional[Union[TaskType, str]] = None,
label_names_to_include: Optional[List[str]] = None,
anomaly_reduction: bool = False,
):
"""
:param base_data_folder: Path to the folder containing the annotations
Expand All @@ -50,6 +52,10 @@ def __init__(
:param label_names_to_include: Names of the labels that should be included
when reading annotation data. This can be used to filter the annotations
for certain labels.
:param anomaly_reduction: True to reduce all anomaly tasks to the single anomaly task.
This is done in accordance with the Intel Geti 2.5 Anomaly Reduction effort.
All pixel level annotations are converted to full rectangles. All anomaly tasks
are mapped to th new "Anomaly Detection" task wich corresponds to the old "Anomaly Classification".
"""
if annotation_format != ".json":
raise ValueError(
Expand All @@ -60,6 +66,7 @@ def __init__(
base_data_folder=base_data_folder,
annotation_format=annotation_format,
task_type=task_type,
anomaly_reduction=anomaly_reduction,
)
self._label_names_to_include = label_names_to_include
self._normalized_annotations = self._has_normalized_annotations()
Expand Down Expand Up @@ -160,6 +167,22 @@ def get_data(
label_name=label_dict["name"]
)
new_annotations.append(annotation_object)
if (
self.anomaly_reduction
and annotation_object.labels[0].name.lower() == "anomalous"
):
# Part of anomaly task reduction in Intel Geti 2.5 -> all anomaly tasks combined into one.
# Intel Geti now only accepts full rectangles for anomaly tasks.
new_annotations = [
Annotation(
labels=[annotation_object.labels[0]],
shape=Rectangle.generate_full_box(
image_width=media_information.width,
image_height=media_information.height,
),
)
]
break
return new_annotations

def get_all_label_names(self) -> List[str]:
Expand Down
13 changes: 5 additions & 8 deletions geti_sdk/benchmarking/benchmarker.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class Benchmarker:
def __init__(
self,
geti: Geti,
project: Union[str, Project],
project: Project,
precision_levels: Optional[Sequence[str]] = None,
models: Optional[Sequence[Model]] = None,
algorithms: Optional[Sequence[str]] = None,
Expand Down Expand Up @@ -83,7 +83,7 @@ def __init__(
be called after initialization.
:param geti: Geti instance on which the project to use for benchmarking lives
:param project: Project or project name to use for the benchmarking. The
:param project: Project to use for the benchmarking. The
project must exist on the specified Geti instance
:param precision_levels: List of model precision levels to run the
benchmarking for. Throughput will be measured for each precision level
Expand Down Expand Up @@ -111,11 +111,8 @@ def __init__(
on.
"""
self.geti = geti
if isinstance(project, str):
project_name = project
else:
project_name = project.name
self.project = geti.get_project(project_name)
# Update project object to get the latest project details
self.project = self.geti.get_project(project_id=project.id)
logging.info(
f"Setting up Benchmarker for Intel® Geti™ project `{self.project.name}`."
)
Expand Down Expand Up @@ -501,7 +498,7 @@ def prepare_benchmark(self, working_directory: os.PathLike = "."):
output_folder = os.path.join(working_directory, f"deployment_{index}")
with suppress_log_output():
self.geti.deploy_project(
project_name=self.project.name,
project=self.project,
output_folder=output_folder,
models=opt_models,
)
Expand Down
7 changes: 5 additions & 2 deletions geti_sdk/data_models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@
TaskConfiguration,
)
from .credit_system import CreditAccount, CreditBalance, Subscription
from .dataset import Dataset, Subset, TrainingDatasetStatistics
from .enums import AnnotationKind, MediaType, TaskType
from .job import Job
from .label import Label, ScoredLabel
Expand All @@ -185,7 +186,7 @@
from .model_group import ModelGroup, ModelSummary
from .performance import Performance
from .predictions import Prediction
from .project import Dataset, Pipeline, Project
from .project import Pipeline, Project
from .status import ProjectStatus
from .task import Task
from .test_result import Score, TestResult
Expand All @@ -198,6 +199,7 @@
"Label",
"Task",
"Pipeline",
"Dataset",
"Image",
"Video",
"MediaItem",
Expand All @@ -220,11 +222,12 @@
"ProjectStatus",
"Job",
"CodeDeploymentInformation",
"Dataset",
"TestResult",
"Score",
"User",
"CreditAccount",
"CreditBalance",
"Subscription",
"Subset",
"TrainingDatasetStatistics",
]
1 change: 1 addition & 0 deletions geti_sdk/data_models/containers/algorithm_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"anomaly_classification": "ote_anomaly_classification_padim",
"anomaly_detection": "ote_anomaly_classification_padim",
"anomaly_segmentation": "ote_anomaly_segmentation_padim",
"anomaly": "ote_anomaly_classification_padim",
"rotated_detection": "Custom_Rotated_Detection_via_Instance_Segmentation_MaskRCNN_ResNet50",
"instance_segmentation": "Custom_Counting_Instance_Segmentation_MaskRCNN_ResNet50",
}
Expand Down
124 changes: 124 additions & 0 deletions geti_sdk/data_models/dataset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Copyright (C) 2024 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions
# and limitations under the License.
from typing import ClassVar, Dict, List, Optional

import attr

from geti_sdk.data_models.containers import MediaList
from geti_sdk.data_models.enums import SubsetPurpose
from geti_sdk.data_models.media import Image, VideoFrame
from geti_sdk.data_models.utils import (
deidentify,
str_to_datetime,
str_to_enum_converter,
)


@attr.define
class Dataset:
"""
Representation of a dataset for a project in Intel® Geti™.
:var id: Unique database ID of the dataset
:var name: name of the dataset
"""

_identifier_fields: ClassVar[str] = ["id", "creation_time"]
_GET_only_fields: ClassVar[List[str]] = ["use_for_training", "creation_time"]

name: str
id: Optional[str] = None
creation_time: Optional[str] = attr.field(default=None, converter=str_to_datetime)
use_for_training: Optional[bool] = None

def deidentify(self) -> None:
"""
Remove unique database ID from the Dataset.
"""
deidentify(self)

def prepare_for_post(self) -> None:
"""
Set all fields to None that are not valid for making a POST request to the
/projects endpoint.
:return:
"""
for field_name in self._GET_only_fields:
setattr(self, field_name, None)


@attr.define
class TrainingDatasetStatistics:
"""
Statistics for a specific dataset that was used for training a model. Note that
a `dataset` includes both the training, validation and testing set.
"""

id: str
creation_time: str = attr.field(converter=str_to_datetime)
subset_info: Dict[str, int]
dataset_info: Dict[str, int]

@property
def training_size(self) -> int:
"""Return the number of dataset items in the training set"""
return self.subset_info["training"]

@property
def validation_size(self) -> int:
"""Return the number of dataset items in the validation set"""
return self.subset_info["validation"]

@property
def testing_size(self) -> int:
"""Return the number of dataset items in the testing set"""
return self.subset_info["testing"]

@property
def number_of_videos(self) -> int:
"""Return the total number of videos in the dataset"""
return self.dataset_info["videos"]

@property
def number_of_frames(self) -> int:
"""Return the total number of video frames in the dataset"""
return self.dataset_info["frames"]

@property
def number_of_images(self) -> int:
"""Return the total number of images in the dataset"""
return self.dataset_info["images"]


@attr.define
class Subset:
"""
Return the media items for a specific subset (i.e. 'training', 'validation' or
'testing')
:var images: List of images in the subset
:var frames: List of video frames in the subset
:var purpose: string representing the purpose of the subset. Can be either
"""

images: MediaList[Image]
frames: MediaList[VideoFrame]
purpose: str = attr.field(converter=str_to_enum_converter(SubsetPurpose))

@property
def size(self) -> int:
"""Return the total number of items in the subset"""
return len(self.images) + len(self.frames)
2 changes: 2 additions & 0 deletions geti_sdk/data_models/enums/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from .prediction_mode import PredictionMode
from .shape_type import ShapeType
from .subscription_status import SubscriptionStatus
from .subset_purpose import SubsetPurpose
from .task_type import TaskType

__all__ = [
Expand All @@ -44,4 +45,5 @@
"JobType",
"JobState",
"DeploymentState",
"SubsetPurpose",
]
2 changes: 1 addition & 1 deletion geti_sdk/data_models/enums/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ class Domain(Enum):
ANOMALY_CLASSIFICATION = "ANOMALY_CLASSIFICATION"
ANOMALY_DETECTION = "ANOMALY_DETECTION"
ANOMALY_SEGMENTATION = "ANOMALY_SEGMENTATION"
ANOMALY = "ANOMALY"
INSTANCE_SEGMENTATION = "INSTANCE_SEGMENTATION"
ROTATED_DETECTION = "ROTATED_DETECTION"
ANOMALY = "ANOMALY"

def __str__(self) -> str:
"""
Expand Down
Loading

0 comments on commit 7c951c7

Please sign in to comment.