99import json
1010from collections import defaultdict
1111from pathlib import Path
12+ from typing import List , Tuple
1213
1314import numpy as np
15+ from numpy import float32
1416from openvino .preprocess import PrePostProcessor
1517from openvino .runtime import Model , Type
1618from openvino .runtime import opset10 as opset
1719
1820from model_api .adapters .inference_adapter import InferenceAdapter
19-
20- from .image_model import ImageModel
21- from .result_types import ClassificationResult
22- from .types import BooleanValue , ListValue , NumericalValue , StringValue
21+ from model_api .models .image_model import ImageModel
22+ from model_api .models .result_types import ClassificationResult
23+ from model_api .models .types import BooleanValue , ListValue , NumericalValue , StringValue
2324
2425
2526class ClassificationModel (ImageModel ):
2627 __model__ = "Classification"
2728
28- def __init__ (self , inference_adapter : InferenceAdapter , configuration : dict = {}, preload : bool = False ):
29+ def __init__ (self , inference_adapter : InferenceAdapter , configuration : dict = {}, preload : bool = False ) -> None :
2930 super ().__init__ (inference_adapter , configuration , preload = False )
3031 self .topk : int
3132 self .labels : list [str ]
@@ -102,7 +103,7 @@ def __init__(self, inference_adapter: InferenceAdapter, configuration: dict = {}
102103 if preload :
103104 self .load ()
104105
105- def _load_labels (self , labels_file ) :
106+ def _load_labels (self , labels_file : str ) -> list :
106107 with Path (labels_file ).open () as f :
107108 labels = []
108109 for s in f :
@@ -113,7 +114,7 @@ def _load_labels(self, labels_file):
113114 labels .append (s [(begin_idx + 1 ) : end_idx ])
114115 return labels
115116
116- def _verify_single_output (self ):
117+ def _verify_single_output (self ) -> None :
117118 layer_name = next (iter (self .outputs ))
118119 layer_shape = self .outputs [layer_name ].shape
119120
@@ -137,7 +138,7 @@ def _verify_single_output(self):
137138 )
138139
139140 @classmethod
140- def parameters (cls ):
141+ def parameters (cls ) -> dict :
141142 parameters = super ().parameters ()
142143 parameters .update (
143144 {
@@ -180,7 +181,8 @@ def parameters(cls):
180181 )
181182 return parameters
182183
183- def postprocess (self , outputs , meta ):
184+ def postprocess (self , outputs : dict , meta : dict ) -> ClassificationResult :
185+ del meta # unused
184186 if self .multilabel :
185187 result = self .get_multilabel_predictions (
186188 outputs [self .out_layer_names [0 ]].squeeze (),
@@ -220,7 +222,7 @@ def get_saliency_maps(self, outputs: dict) -> np.ndarray:
220222 reordered_saliency_maps [batch ].append (saliency_maps [batch ][idx ])
221223 return np .array (reordered_saliency_maps )
222224
223- def get_all_probs (self , logits : np .ndarray ):
225+ def get_all_probs (self , logits : np .ndarray ) -> np . ndarray :
224226 if self .multilabel :
225227 probs = sigmoid_numpy (logits .reshape (- 1 ))
226228 elif self .hierarchical :
@@ -266,10 +268,10 @@ def get_hierarchical_predictions(self, logits: np.ndarray):
266268 predicted_labels .append (label_str )
267269 predicted_scores .append (head_logits [i ])
268270
269- predictions = zip (predicted_labels , predicted_scores )
271+ predictions = list ( zip (predicted_labels , predicted_scores ) )
270272 return self .labels_resolver .resolve_labels (predictions )
271273
272- def get_multilabel_predictions (self , logits : np .ndarray ):
274+ def get_multilabel_predictions (self , logits : np .ndarray ) -> List [ Tuple [ int , str , float32 ]] :
273275 logits = sigmoid_numpy (logits )
274276 scores = []
275277 indices = []
@@ -281,7 +283,7 @@ def get_multilabel_predictions(self, logits: np.ndarray):
281283
282284 return list (zip (indices , labels , scores ))
283285
284- def get_multiclass_predictions (self , outputs ) :
286+ def get_multiclass_predictions (self , outputs : dict ) -> list [ tuple [ int , str , float ]] :
285287 if self .embedded_topk :
286288 indicesTensor = outputs [self .out_layer_names [0 ]][0 ]
287289 scoresTensor = outputs [self .out_layer_names [1 ]][0 ]
@@ -293,7 +295,7 @@ def get_multiclass_predictions(self, outputs):
293295 return list (zip (indicesTensor , labels , scoresTensor ))
294296
295297
296- def addOrFindSoftmaxAndTopkOutputs (inference_adapter : InferenceAdapter , topk : int , output_raw_scores : bool ):
298+ def addOrFindSoftmaxAndTopkOutputs (inference_adapter : InferenceAdapter , topk : int , output_raw_scores : bool ) -> None :
297299 softmaxNode = None
298300 for i in range (len (inference_adapter .model .outputs )):
299301 output_node = inference_adapter .model .get_output_op (i ).input (0 ).get_source_output ().get_node ()
@@ -345,17 +347,17 @@ def addOrFindSoftmaxAndTopkOutputs(inference_adapter: InferenceAdapter, topk: in
345347 inference_adapter .model = ppp .build ()
346348
347349
348- def sigmoid_numpy (x : np .ndarray ):
350+ def sigmoid_numpy (x : np .ndarray ) -> np . ndarray :
349351 return 1.0 / (1.0 + np .exp (- x ))
350352
351353
352- def softmax_numpy (x : np .ndarray , eps : float = 1e-9 ):
354+ def softmax_numpy (x : np .ndarray , eps : float = 1e-9 ) -> np . ndarray :
353355 x = np .exp (x - np .max (x ))
354356 return x / (np .sum (x ) + eps )
355357
356358
357359class GreedyLabelsResolver :
358- def __init__ (self , hierarchical_config ) -> None :
360+ def __init__ (self , hierarchical_config : dict ) -> None :
359361 self .label_to_idx = hierarchical_config ["cls_heads_info" ]["label_to_idx" ]
360362 self .label_relations = hierarchical_config ["label_tree_edges" ]
361363 self .label_groups = hierarchical_config ["cls_heads_info" ]["all_groups" ]
@@ -364,7 +366,7 @@ def __init__(self, hierarchical_config) -> None:
364366 for child , parent in self .label_relations :
365367 self .label_tree .add_edge (parent , child )
366368
367- def resolve_labels (self , predictions ) :
369+ def resolve_labels (self , predictions : list [ tuple ]) -> list :
368370 """Resolves hierarchical labels and exclusivity based on a list of ScoredLabels (labels with probability).
369371 The following two steps are taken:
370372 - select the most likely label from each label group
@@ -374,7 +376,7 @@ def resolve_labels(self, predictions):
374376 predictions: a list of tuples (label name, score)
375377 """
376378
377- def get_predecessors (lbl , candidates ) :
379+ def get_predecessors (lbl : str , candidates : list [ str ]) -> list :
378380 """Return all predecessors.
379381
380382 Returns all the predecessors of the input label or an empty list if one of the predecessors is not a
@@ -422,12 +424,12 @@ def get_predecessors(lbl, candidates):
422424
423425
424426class ProbabilisticLabelsResolver (GreedyLabelsResolver ):
425- def __init__ (self , hierarchical_config , warmup_cache = True ) -> None :
427+ def __init__ (self , hierarchical_config : dict , warmup_cache : bool = True ) -> None :
426428 super ().__init__ (hierarchical_config )
427429 if warmup_cache :
428430 self .label_tree .get_labels_in_topological_order ()
429431
430- def resolve_labels (self , predictions ) :
432+ def resolve_labels (self , predictions : list [ tuple [ str , float ]]) -> list [ tuple [ int , str , float ]] :
431433 """Resolves hierarchical labels and exclusivity based on a list of ScoredLabels (labels with probability).
432434
433435 The following two steps are taken:
@@ -446,8 +448,8 @@ def resolve_labels(self, predictions):
446448
447449 def __resolve_labels_probabilistic (
448450 self ,
449- label_to_probability ,
450- ):
451+ label_to_probability : dict [ str , float ] ,
452+ ) -> list [ tuple [ int , str , float ]] :
451453 """Resolves hierarchical labels and exclusivity based on a probabilistic label output.
452454
453455 - selects the most likely (max) label from an exclusive group
@@ -546,24 +548,24 @@ class SimpleLabelsGraph:
546548 like adding edges, getting children and parents.
547549 """
548550
549- def __init__ (self , vertices ) :
551+ def __init__ (self , vertices : list [ str ]) -> None :
550552 self ._v = vertices
551- self ._adj = defaultdict ( list )
552- self ._topological_order_cache = None
553- self ._parents_map = {}
553+ self ._adj : dict [ str , list ] = { v : [] for v in vertices }
554+ self ._topological_order_cache : list | None = None
555+ self ._parents_map : dict [ str , str ] = {}
554556
555- def add_edge (self , parent , child ) :
557+ def add_edge (self , parent : str , child : str ) -> None :
556558 self ._adj [parent ].append (child )
557559 self ._parents_map [child ] = parent
558560 self .clear_topological_cache ()
559561
560- def get_children (self , label ) :
562+ def get_children (self , label : str ) -> list :
561563 return self ._adj [label ]
562564
563- def get_parent (self , label ) :
565+ def get_parent (self , label : str ) -> str | None :
564566 return self ._parents_map .get (label , None )
565567
566- def get_ancestors (self , label ) :
568+ def get_ancestors (self , label : str ) -> list [ str ] :
567569 """Returns all the ancestors of the input label, including self."""
568570 predecessors = [label ]
569571 last_parent = self .get_parent (label )
@@ -576,14 +578,14 @@ def get_ancestors(self, label):
576578
577579 return predecessors
578580
579- def get_labels_in_topological_order (self ):
581+ def get_labels_in_topological_order (self ) -> list :
580582 if self ._topological_order_cache is None :
581583 self ._topological_order_cache = self .topological_sort ()
582584
583585 return self ._topological_order_cache
584586
585- def topological_sort (self ):
586- in_degree = defaultdict ( int )
587+ def topological_sort (self ) -> list :
588+ in_degree : dict [ str , int ] = dict . fromkeys ( self . _v , 0 )
587589
588590 for node_adj in self ._adj .values ():
589591 for j in node_adj :
@@ -610,7 +612,7 @@ def topological_sort(self):
610612
611613 return ordered
612614
613- def clear_topological_cache (self ):
615+ def clear_topological_cache (self ) -> None :
614616 self ._topological_order_cache = None
615617
616618
@@ -619,15 +621,15 @@ def clear_topological_cache(self):
619621_raw_scores_name = "raw_scores"
620622
621623
622- def _get_non_xai_names (output_names ) :
624+ def _get_non_xai_names (output_names : list [ str ]) -> list [ str ] :
623625 return [
624626 output_name
625627 for output_name in output_names
626628 if _saliency_map_name != output_name and _feature_vector_name != output_name
627629 ]
628630
629631
630- def _append_xai_names (outputs , output_names ) :
632+ def _append_xai_names (outputs : dict , output_names : list [ str ]) -> None :
631633 if _saliency_map_name in outputs :
632634 output_names .append (_saliency_map_name )
633635 if _feature_vector_name in outputs :
0 commit comments