Skip to content

Commit

Permalink
Merge branch 'develop' into vsaltykovx/add_mmdetection_input_paramete…
Browse files Browse the repository at this point in the history
…rs_validation
  • Loading branch information
saltykox committed Mar 18, 2022
2 parents 1d5e06c + abafaa3 commit 04e5fb8
Show file tree
Hide file tree
Showing 142 changed files with 18,574 additions and 68 deletions.
4 changes: 2 additions & 2 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
path = external/mmdetection
url = ../../openvinotoolkit/mmdetection
[submodule "external/deep-object-reid"]
path = external/deep-object-reid
path = external/deep-object-reid/submodule
url = ../../openvinotoolkit/deep-object-reid
[submodule "external/mmsegmentation"]
path = external/mmsegmentation
path = external/mmsegmentation/submodule
url = ../../openvinotoolkit/mmsegmentation
12 changes: 7 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ repos:
alias: isort_rest
name: "isort (ote_cli|external)"
args: ["--settings", ".isort.cfg"]
files: '^(ote_cli|external)/.*\.py'
files: '^(ote_cli|external/anomaly)/.*\.py'
exclude: "tests/"

- repo: https://github.com/psf/black
rev: 21.7b0
Expand All @@ -26,14 +27,14 @@ repos:
- id: black
name: "black (rest)"
args: [--line-length, "120"]
files: '^external/.*\.py'
files: '^external/anomaly/.*\.py'

- repo: https://github.com/PyCQA/flake8
rev: "3.9.2"
hooks:
- id: flake8
name: "flake8"
files: '^(ote_sdk|ote_cli|external)/.*\.py'
files: '^(ote_sdk|ote_cli|external/anomaly)/.*\.py'
args: ["--config", ".flake8", "--max-complexity", "20"]
exclude: ".*/protobuf"

Expand All @@ -43,6 +44,7 @@ repos:
hooks:
- id: prettier
types: [yaml]
exclude: "external/deep-object-reid"

- repo: https://github.com/pre-commit/mirrors-mypy
rev: "v0.812"
Expand Down Expand Up @@ -71,14 +73,14 @@ repos:
- id: mypy
alias: mypy_rest
name: "mypy (external)"
files: '^external/.*\.py'
files: '^external/anomaly/.*\.py'
args: ["--config-file=ote_sdk/.mypy.ini"]

- repo: local
hooks:
- id: pylint
name: "pylint"
files: '^(ote_sdk|ote_cli|external)/.*\.py'
files: '^(ote_sdk|ote_cli|external/anomaly)/.*\.py'
entry: pylint
language: system
types: [python]
Expand Down
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ ignored-classes=optparse.Values,thread._local,_thread._local
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis). It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=ote_sdk,mmseg,mmdet,torchreid,cv2,anomalib,pytorch_lightning,torch,addict,compression,openvino,pandas
ignored-modules=ote_sdk,mmseg,segmentation_tasks,mmdet,torchreid,torchreid_tasks,cv2,anomalib,pytorch_lightning,torch,addict,compression,openvino,pandas

# Show a hint with possible names when a member name was not found. The aspect
# of finding the hint is based on edit distance.
Expand Down
6 changes: 3 additions & 3 deletions external/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ Custom_Rotated_Detection_via_Instance_Segmentation_MaskRCNN_ResNet50 | MaskRCNN-
## Semantic Segmentaion
ID | Name | Complexity (GFlops) | Model size (MB) | Path
------- | ------- | ------- | ------- | -------
Custom_Semantic_Segmentation_Lite-HRNet-18_OCR | Lite-HRNet-18 OCR | 3.45 | 4.5 | mmsegmentation/configs/ote/custom-sematic-segmentation/ocr-lite-hrnet-18/template.yaml
Custom_Semantic_Segmentation_Lite-HRNet-18-mod2_OCR | Lite-HRNet-18-mod2 OCR | 3.63 | 4.8 | mmsegmentation/configs/ote/custom-sematic-segmentation/ocr-lite-hrnet-18-mod2/template.yaml
Custom_Semantic_Segmentation_Lite-HRNet-x-mod3_OCR | Lite-HRNet-x-mod3 OCR | 13.97 | 6.4 | mmsegmentation/configs/ote/custom-sematic-segmentation/ocr-lite-hrnet-x-mod3/template.yaml
Custom_Semantic_Segmentation_Lite-HRNet-18_OCR | Lite-HRNet-18 OCR | 3.45 | 4.5 | mmsegmentation/configs/custom-sematic-segmentation/ocr-lite-hrnet-18/template.yaml
Custom_Semantic_Segmentation_Lite-HRNet-18-mod2_OCR | Lite-HRNet-18-mod2 OCR | 3.63 | 4.8 | mmsegmentation/configs/custom-sematic-segmentation/ocr-lite-hrnet-18-mod2/template.yaml
Custom_Semantic_Segmentation_Lite-HRNet-x-mod3_OCR | Lite-HRNet-x-mod3 OCR | 13.97 | 6.4 | mmsegmentation/configs/custom-sematic-segmentation/ocr-lite-hrnet-x-mod3/template.yaml
2 changes: 1 addition & 1 deletion external/anomaly/constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ opencv-python==4.5.3.56
openvino-dev==2022.1.0.dev20220302
pillow==9.0.0
pytorch-lightning==1.5.9
requests==2.25.1
requests==2.26.0
scikit-image==0.17.2
scikit-learn==0.24.2
9 changes: 3 additions & 6 deletions external/anomaly/ote_anomalib/callbacks/inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,13 @@ def on_predict_epoch_end(self, _trainer: pl.Trainer, pl_module: AnomalyModule, o
self.ote_dataset, pred_scores, pred_labels, anomaly_maps, pred_masks
):
label = self.anomalous_label if pred_label else self.normal_label
if self.task_type == TaskType.ANOMALY_CLASSIFICATION:
probability = (1 - pred_score) if pred_score < 0.5 else pred_score
dataset_item.append_labels([ScoredLabel(label=label, probability=float(probability))])
elif self.task_type == TaskType.ANOMALY_SEGMENTATION:
probability = (1 - pred_score) if pred_score < 0.5 else pred_score
dataset_item.append_labels([ScoredLabel(label=label, probability=float(probability))])
if self.task_type == TaskType.ANOMALY_SEGMENTATION:
mask = pred_mask.squeeze().astype(np.uint8)
dataset_item.append_annotations(
create_annotation_from_segmentation_map(mask, anomaly_map.squeeze(), self.label_map)
)
else:
raise ValueError(f"Unknown task type: {self.task_type}")

dataset_item.append_metadata_item(
ResultMediaEntity(
Expand Down
16 changes: 13 additions & 3 deletions external/anomaly/ote_anomalib/data/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
import numpy as np
from anomalib.pre_processing import PreProcessor
from omegaconf import DictConfig, ListConfig
from ote_anomalib.data.utils import (
contains_anomalous_images,
split_local_global_dataset,
)
from ote_anomalib.logging import get_logger
from ote_sdk.entities.datasets import DatasetEntity
from ote_sdk.entities.model_template import TaskType
Expand Down Expand Up @@ -182,7 +186,6 @@ def train_dataloader(
"""
Train Dataloader
"""

dataset = OTEAnomalyDataset(self.config, self.train_ote_dataset, self.task_type)
return DataLoader(
dataset,
Expand All @@ -195,8 +198,15 @@ def val_dataloader(self) -> Union[DataLoader, List[DataLoader]]:
"""
Validation Dataloader
"""

dataset = OTEAnomalyDataset(self.config, self.val_ote_dataset, self.task_type)
global_dataset, local_dataset = split_local_global_dataset(self.val_ote_dataset)
logger.info(f"Global annotations: {len(global_dataset)}")
logger.info(f"Local annotations: {len(local_dataset)}")
if contains_anomalous_images(local_dataset):
logger.info("Dataset contains polygon annotations. Passing masks to anomalib.")
dataset = OTEAnomalyDataset(self.config, local_dataset, TaskType.ANOMALY_SEGMENTATION)
else:
logger.info("Dataset does not contain polygon annotations. Not passing masks to anomalib.")
dataset = OTEAnomalyDataset(self.config, global_dataset, TaskType.ANOMALY_CLASSIFICATION)
return DataLoader(
dataset,
shuffle=False,
Expand Down
169 changes: 169 additions & 0 deletions external/anomaly/ote_anomalib/data/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
"""
Dataset utils for OTE Anomaly
"""

# Copyright (C) 2021 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 Tuple

from ote_sdk.entities.annotation import AnnotationSceneEntity, AnnotationSceneKind
from ote_sdk.entities.dataset_item import DatasetItemEntity
from ote_sdk.entities.datasets import DatasetEntity
from ote_sdk.entities.resultset import ResultSetEntity
from ote_sdk.entities.shapes.rectangle import Rectangle


def split_local_global_dataset(dataset) -> Tuple[DatasetEntity, DatasetEntity]:
"""Split a dataset into globally and locally annotated items."""
globally_annotated = []
locally_annotated = []
for gt_item in dataset:

annotations = gt_item.get_annotations()
global_annotations = [annotation for annotation in annotations if Rectangle.is_full_box(annotation.shape)]
local_annotations = [annotation for annotation in annotations if not Rectangle.is_full_box(annotation.shape)]

if not any(label.is_anomalous for label in gt_item.get_shapes_labels()):
# normal images get added to both datasets
globally_annotated.append(gt_item)
locally_annotated.append(gt_item)
else: # image is abnormal
globally_annotated.append(
DatasetItemEntity(
media=gt_item.media,
annotation_scene=AnnotationSceneEntity(global_annotations, kind=AnnotationSceneKind.ANNOTATION),
metadata=gt_item.metadata,
subset=gt_item.subset,
ignored_labels=gt_item.ignored_labels,
)
)
# add locally annotated dataset items
if len(local_annotations) > 0:
locally_annotated.append(
DatasetItemEntity(
media=gt_item.media,
annotation_scene=AnnotationSceneEntity(local_annotations, kind=AnnotationSceneKind.ANNOTATION),
metadata=gt_item.metadata,
subset=gt_item.subset,
ignored_labels=gt_item.ignored_labels,
)
)
global_gt_dataset = DatasetEntity(globally_annotated, purpose=dataset.purpose)
local_gt_dataset = DatasetEntity(locally_annotated, purpose=dataset.purpose)
return global_gt_dataset, local_gt_dataset


def split_local_global_resultset(resultset) -> Tuple[ResultSetEntity, ResultSetEntity]:
"""Split resultset based on the type of available annotations."""
# splits the dataset
globally_annotated = []
locally_annotated = []
globally_predicted = []
locally_predicted = []
for gt_item, pred_item in zip(resultset.ground_truth_dataset, resultset.prediction_dataset):

annotations = gt_item.get_annotations()
global_annotations = [annotation for annotation in annotations if Rectangle.is_full_box(annotation.shape)]
local_annotations = [annotation for annotation in annotations if not Rectangle.is_full_box(annotation.shape)]

predictions = gt_item.get_annotations()
global_predictions = [predictions for predictions in predictions if Rectangle.is_full_box(predictions.shape)]
local_predictions = [predictions for predictions in predictions if not Rectangle.is_full_box(predictions.shape)]

if not any(label.is_anomalous for label in gt_item.get_shapes_labels()):
# normal images get added to both datasets
globally_annotated.append(gt_item)
locally_annotated.append(gt_item)
globally_predicted.append(
DatasetItemEntity(
media=pred_item.media,
annotation_scene=AnnotationSceneEntity(global_predictions, kind=AnnotationSceneKind.PREDICTION),
metadata=pred_item.metadata,
subset=pred_item.subset,
ignored_labels=pred_item.ignored_labels,
)
)
locally_predicted.append(
DatasetItemEntity(
media=pred_item.media,
annotation_scene=AnnotationSceneEntity(local_predictions, kind=AnnotationSceneKind.PREDICTION),
metadata=pred_item.metadata,
subset=pred_item.subset,
ignored_labels=pred_item.ignored_labels,
)
)
else: # image is abnormal
globally_annotated.append(
DatasetItemEntity(
media=gt_item.media,
annotation_scene=AnnotationSceneEntity(global_annotations, kind=AnnotationSceneKind.ANNOTATION),
metadata=gt_item.metadata,
subset=gt_item.subset,
ignored_labels=gt_item.ignored_labels,
)
)
globally_predicted.append(
DatasetItemEntity(
media=pred_item.media,
annotation_scene=AnnotationSceneEntity(global_predictions, kind=AnnotationSceneKind.PREDICTION),
metadata=pred_item.metadata,
subset=pred_item.subset,
ignored_labels=pred_item.ignored_labels,
)
)
# add locally annotated dataset items
if len(local_annotations) > 0:
locally_annotated.append(
DatasetItemEntity(
media=gt_item.media,
annotation_scene=AnnotationSceneEntity(local_annotations, kind=AnnotationSceneKind.ANNOTATION),
metadata=gt_item.metadata,
subset=gt_item.subset,
ignored_labels=gt_item.ignored_labels,
)
)
locally_predicted.append(
DatasetItemEntity(
media=pred_item.media,
annotation_scene=AnnotationSceneEntity(local_predictions, kind=AnnotationSceneKind.PREDICTION),
metadata=pred_item.metadata,
subset=pred_item.subset,
ignored_labels=pred_item.ignored_labels,
)
)

global_resultset = ResultSetEntity(
model=resultset.model,
ground_truth_dataset=DatasetEntity(globally_annotated, purpose=resultset.ground_truth_dataset.purpose),
prediction_dataset=DatasetEntity(globally_predicted, purpose=resultset.prediction_dataset.purpose),
purpose=resultset.purpose,
)
local_resultset = ResultSetEntity(
model=resultset.model,
ground_truth_dataset=DatasetEntity(locally_annotated, purpose=resultset.ground_truth_dataset.purpose),
prediction_dataset=DatasetEntity(locally_predicted, purpose=resultset.prediction_dataset.purpose),
purpose=resultset.purpose,
)

return global_resultset, local_resultset


def contains_anomalous_images(dataset: DatasetEntity) -> bool:
"""Find the number of local annotations in a resultset."""
for item in dataset:
labels = item.get_shapes_labels()
if any(label.is_anomalous for label in labels):
return True
return False
16 changes: 15 additions & 1 deletion external/anomaly/ote_anomalib/openvino.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
from compression.pipeline.initializer import create_pipeline
from omegaconf import OmegaConf
from ote_anomalib.configs import get_anomalib_config
from ote_anomalib.data.utils import (
contains_anomalous_images,
split_local_global_resultset,
)
from ote_anomalib.exportable_code import (
AnomalyBase,
AnomalyClassification,
Expand Down Expand Up @@ -215,7 +219,17 @@ def evaluate(self, output_resultset: ResultSetEntity, evaluation_metric: Optiona
if self.task_type == TaskType.ANOMALY_CLASSIFICATION:
metric = MetricsHelper.compute_f_measure(output_resultset)
elif self.task_type == TaskType.ANOMALY_SEGMENTATION:
metric = MetricsHelper.compute_dice_averaged_over_pixels(output_resultset, MetricAverageMethod.MICRO)
global_resultset, local_resultset = split_local_global_resultset(output_resultset)
logger.info(f"Global annotations: {len(global_resultset.ground_truth_dataset)}")
logger.info(f"Local annotations: {len(local_resultset.ground_truth_dataset)}")
logger.info(f"Global predictions: {len(global_resultset.prediction_dataset)}")
logger.info(f"Local predictions: {len(local_resultset.prediction_dataset)}")
if contains_anomalous_images(local_resultset.ground_truth_dataset):
logger.info("Dataset contains polygon annotations. Using pixel-level evaluation metric.")
metric = MetricsHelper.compute_dice_averaged_over_pixels(local_resultset, MetricAverageMethod.MICRO)
else:
logger.info("Dataset does not contain polygon annotations. Using image-level evaluation metric.")
metric = MetricsHelper.compute_f_measure(global_resultset)
else:
raise ValueError(f"Unknown task type: {self.task_type}")
output_resultset.performance = metric.get_performance()
Expand Down
24 changes: 16 additions & 8 deletions external/anomaly/ote_anomalib/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
from ote_anomalib.callbacks import AnomalyInferenceCallback, ProgressCallback
from ote_anomalib.configs import get_anomalib_config
from ote_anomalib.data import OTEAnomalyDataModule
from ote_anomalib.data.utils import (
contains_anomalous_images,
split_local_global_resultset,
)
from ote_anomalib.logging import get_logger
from ote_sdk.entities.datasets import DatasetEntity
from ote_sdk.entities.inference_parameters import InferenceParameters
Expand Down Expand Up @@ -87,13 +91,7 @@ def get_config(self) -> Union[DictConfig, ListConfig]:
config = get_anomalib_config(task_name=self.model_name, ote_config=hyper_parameters)
config.project.path = self.project_path

# set task type
if self.task_type == TaskType.ANOMALY_CLASSIFICATION:
config.dataset.task = "classification"
elif self.task_type == TaskType.ANOMALY_SEGMENTATION:
config.dataset.task = "segmentation"
else:
raise ValueError(f"Unknown task type: {self.task_type}")
config.dataset.task = "classification"

return config

Expand Down Expand Up @@ -232,7 +230,17 @@ def evaluate(self, output_resultset: ResultSetEntity, evaluation_metric: Optiona
if self.task_type == TaskType.ANOMALY_CLASSIFICATION:
metric = MetricsHelper.compute_f_measure(output_resultset)
elif self.task_type == TaskType.ANOMALY_SEGMENTATION:
metric = MetricsHelper.compute_dice_averaged_over_pixels(output_resultset, MetricAverageMethod.MICRO)
global_resultset, local_resultset = split_local_global_resultset(output_resultset)
logger.info(f"Global annotations: {len(global_resultset.ground_truth_dataset)}")
logger.info(f"Local annotations: {len(local_resultset.ground_truth_dataset)}")
logger.info(f"Global predictions: {len(global_resultset.prediction_dataset)}")
logger.info(f"Local predictions: {len(local_resultset.prediction_dataset)}")
if contains_anomalous_images(local_resultset.ground_truth_dataset):
logger.info("Dataset contains polygon annotations. Using pixel-level evaluation metric.")
metric = MetricsHelper.compute_dice_averaged_over_pixels(local_resultset, MetricAverageMethod.MICRO)
else:
logger.info("Dataset does not contain polygon annotations. Using image-level evaluation metric.")
metric = MetricsHelper.compute_f_measure(global_resultset)
else:
raise ValueError(f"Unknown task type: {self.task_type}")
output_resultset.performance = metric.get_performance()
Expand Down
1 change: 0 additions & 1 deletion external/deep-object-reid
Submodule deep-object-reid deleted from d5a9f2
Loading

0 comments on commit 04e5fb8

Please sign in to comment.