kwcoco.category_tree module¶
The category_tree`
module defines the CategoryTree
class, which
is used for maintaining flat or hierarchical category information. The kwcoco
version of this class only contains the datastructure and does not contain any
torch operations. See the ndsampler version for the extension with torch
operations.
-
class
kwcoco.category_tree.
CategoryTree
(graph=None)[source]¶ Bases:
ubelt.util_mixins.NiceRepr
Wrapper that maintains flat or hierarchical category information.
Helps compute softmaxes and probabilities for tree-based categories where a directed edge (A, B) represents that A is a superclass of B.
Notes
There are three basic properties that this object maintains:
- name:
- Alphanumeric string names that should be generally descriptive. Using spaces and special characters in these names is discouraged, but can be done.
- id:
- The integer id of a category should ideally remain consistent. These are often given by a dataset (e.g. a COCO dataset).
- index:
- Contigous zero-based indices that indexes the list of categories. These should be used for the fastest access in backend computation tasks.
Variables: - idx_to_node (List[str]) – a list of class names. Implicitly maps from index to category name.
- id_to_node (Dict[int, str]) – maps integer ids to category names
- node_to_id (Dict[str, int]) – maps category names to ids
- node_to_idx (Dict[str, int]) – maps category names to indexes
- graph (nx.Graph) – a Graph that stores any hierarchy information. For standard mutually exclusive classes, this graph is edgeless. Nodes in this graph can maintain category attributes / properties.
- idx_groups (List[List[int]]) – groups of category indices that share the same parent category.
Example
>>> from kwcoco.category_tree import * >>> graph = nx.from_dict_of_lists({ >>> 'background': [], >>> 'foreground': ['animal'], >>> 'animal': ['mammal', 'fish', 'insect', 'reptile'], >>> 'mammal': ['dog', 'cat', 'human', 'zebra'], >>> 'zebra': ['grevys', 'plains'], >>> 'grevys': ['fred'], >>> 'dog': ['boxer', 'beagle', 'golden'], >>> 'cat': ['maine coon', 'persian', 'sphynx'], >>> 'reptile': ['bearded dragon', 't-rex'], >>> }, nx.DiGraph) >>> self = CategoryTree(graph) >>> print(self) <CategoryTree(nNodes=22, maxDepth=6, maxBreadth=4...)>
Example
>>> # The coerce classmethod is the easiest way to create an instance >>> import kwcoco >>> kwcoco.CategoryTree.coerce(['a', 'b', 'c']) <CategoryTree(nNodes=3, nodes=['a', 'b', 'c']) ... >>> kwcoco.CategoryTree.coerce(4) <CategoryTree(nNodes=4, nodes=['class_1', 'class_2', 'class_3', ... >>> kwcoco.CategoryTree.coerce(4)
-
classmethod
from_mutex
(nodes, bg_hack=True)[source]¶ Parameters: nodes (List[str]) – or a list of class names (in which case they will all be assumed to be mutually exclusive) Example
>>> print(CategoryTree.from_mutex(['a', 'b', 'c'])) <CategoryTree(nNodes=3, ...)>
-
classmethod
from_json
(state)[source]¶ Parameters: state (Dict) – see __getstate__ / __json__ for details
-
classmethod
from_coco
(categories)[source]¶ Create a CategoryTree object from coco categories
Parameters: List[Dict] – list of coco-style categories
-
classmethod
coerce
(data, **kw)[source]¶ Attempt to coerce data as a CategoryTree object.
This is primarily useful for when the software stack depends on categories being represent
This will work if the input data is a specially formatted json dict, a list of mutually exclusive classes, or if it is already a CategoryTree. Otherwise an error will be thrown.
Parameters: - data (object) – a known representation of a category tree.
- **kwargs – input type specific arguments
Returns: self
Return type: Raises: - TypeError - if the input format is unknown
- ValueError - if kwargs are not compatible with the input format
Example
>>> import kwcoco >>> classes1 = kwcoco.CategoryTree.coerce(3) # integer >>> classes2 = kwcoco.CategoryTree.coerce(classes1.__json__()) # graph dict >>> classes3 = kwcoco.CategoryTree.coerce(['class_1', 'class_2', 'class_3']) # mutex list >>> classes4 = kwcoco.CategoryTree.coerce(classes1.graph) # nx Graph >>> classes5 = kwcoco.CategoryTree.coerce(classes1) # cls >>> # xdoctest: +REQUIRES(module:ndsampler) >>> import ndsampler >>> classes6 = ndsampler.CategoryTree.coerce(3) >>> classes7 = ndsampler.CategoryTree.coerce(classes1) >>> classes8 = kwcoco.CategoryTree.coerce(classes6)
-
classmethod
demo
(key='coco', **kwargs)[source]¶ Parameters: key (str) – specify which demo dataset to use. Can be ‘coco’ (which uses the default coco demo data). Can be ‘btree’ which creates a binary tree and accepts kwargs
‘r’ and ‘h’ for branching-factor and height.
- CommandLine:
- xdoctest -m ~/code/kwcoco/kwcoco/category_tree.py CategoryTree.demo
Example
>>> from kwcoco.category_tree import * >>> self = CategoryTree.demo() >>> print('self = {}'.format(self)) self = <CategoryTree(nNodes=10, maxDepth=2, maxBreadth=4...)>
-
id_to_idx
¶ >>> import kwcoco >>> self = kwcoco.CategoryTree.demo() >>> self.id_to_idx[1]
Type: Example
-
idx_to_id
¶ >>> import kwcoco >>> self = kwcoco.CategoryTree.demo() >>> self.idx_to_id[0]
Type: Example
-
idx_to_ancestor_idxs
¶ memoization decorator for a method that respects args and kwargs
References
http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/
Example
>>> import ubelt as ub >>> closure = {'a': 'b', 'c': 'd'} >>> incr = [0] >>> class Foo(object): >>> @memoize_method >>> def foo_memo(self, key): >>> value = closure[key] >>> incr[0] += 1 >>> return value >>> def foo(self, key): >>> value = closure[key] >>> incr[0] += 1 >>> return value >>> self = Foo() >>> assert self.foo('a') == 'b' and self.foo('c') == 'd' >>> assert incr[0] == 2 >>> print('Call memoized version') >>> assert self.foo_memo('a') == 'b' and self.foo_memo('c') == 'd' >>> assert incr[0] == 4 >>> assert self.foo_memo('a') == 'b' and self.foo_memo('c') == 'd' >>> print('Counter should no longer increase') >>> assert incr[0] == 4 >>> print('Closure changes result without memoization') >>> closure = {'a': 0, 'c': 1} >>> assert self.foo('a') == 0 and self.foo('c') == 1 >>> assert incr[0] == 6 >>> assert self.foo_memo('a') == 'b' and self.foo_memo('c') == 'd' >>> print('Constructing a new object should get a new cache') >>> self2 = Foo() >>> self2.foo_memo('a') >>> assert incr[0] == 7 >>> self2.foo_memo('a') >>> assert incr[0] == 7
-
idx_to_descendants_idxs
¶ memoization decorator for a method that respects args and kwargs
References
http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/
Example
>>> import ubelt as ub >>> closure = {'a': 'b', 'c': 'd'} >>> incr = [0] >>> class Foo(object): >>> @memoize_method >>> def foo_memo(self, key): >>> value = closure[key] >>> incr[0] += 1 >>> return value >>> def foo(self, key): >>> value = closure[key] >>> incr[0] += 1 >>> return value >>> self = Foo() >>> assert self.foo('a') == 'b' and self.foo('c') == 'd' >>> assert incr[0] == 2 >>> print('Call memoized version') >>> assert self.foo_memo('a') == 'b' and self.foo_memo('c') == 'd' >>> assert incr[0] == 4 >>> assert self.foo_memo('a') == 'b' and self.foo_memo('c') == 'd' >>> print('Counter should no longer increase') >>> assert incr[0] == 4 >>> print('Closure changes result without memoization') >>> closure = {'a': 0, 'c': 1} >>> assert self.foo('a') == 0 and self.foo('c') == 1 >>> assert incr[0] == 6 >>> assert self.foo_memo('a') == 'b' and self.foo_memo('c') == 'd' >>> print('Constructing a new object should get a new cache') >>> self2 = Foo() >>> self2.foo_memo('a') >>> assert incr[0] == 7 >>> self2.foo_memo('a') >>> assert incr[0] == 7
-
idx_pairwise_distance
¶ memoization decorator for a method that respects args and kwargs
References
http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/
Example
>>> import ubelt as ub >>> closure = {'a': 'b', 'c': 'd'} >>> incr = [0] >>> class Foo(object): >>> @memoize_method >>> def foo_memo(self, key): >>> value = closure[key] >>> incr[0] += 1 >>> return value >>> def foo(self, key): >>> value = closure[key] >>> incr[0] += 1 >>> return value >>> self = Foo() >>> assert self.foo('a') == 'b' and self.foo('c') == 'd' >>> assert incr[0] == 2 >>> print('Call memoized version') >>> assert self.foo_memo('a') == 'b' and self.foo_memo('c') == 'd' >>> assert incr[0] == 4 >>> assert self.foo_memo('a') == 'b' and self.foo_memo('c') == 'd' >>> print('Counter should no longer increase') >>> assert incr[0] == 4 >>> print('Closure changes result without memoization') >>> closure = {'a': 0, 'c': 1} >>> assert self.foo('a') == 0 and self.foo('c') == 1 >>> assert incr[0] == 6 >>> assert self.foo_memo('a') == 'b' and self.foo_memo('c') == 'd' >>> print('Constructing a new object should get a new cache') >>> self2 = Foo() >>> self2.foo_memo('a') >>> assert incr[0] == 7 >>> self2.foo_memo('a') >>> assert incr[0] == 7
-
is_mutex
()[source]¶ Returns True if all categories are mutually exclusive (i.e. flat)
If true, then the classes may be represented as a simple list of class names without any loss of information, otherwise the underlying category graph is necessary to preserve all knowledge.
Todo
- [ ] what happens when we have a dummy root?
-
num_classes
¶
-
class_names
¶
-
category_names
¶
-
cats
¶ Returns a mapping from category names to category attributes.
If this category tree was constructed from a coco-dataset, then this will contain the coco category attributes.
Returns: Dict[str, Dict[str, object]] Example
>>> from kwcoco.category_tree import * >>> self = CategoryTree.demo() >>> print('self.cats = {!r}'.format(self.cats))
-
show
()[source]¶ - Ignore:
>>> import kwplot >>> kwplot.autompl() >>> from kwcoco import category_tree >>> self = category_tree.CategoryTree.demo() >>> self.show()
python -c “import kwplot, kwcoco, graphid; kwplot.autompl(); graphid.util.show_nx(kwcoco.category_tree.CategoryTree.demo().graph); kwplot.show_if_requested()” –show