# -*- coding: utf-8 -*-
import logging
import utool as ut
import itertools as it
from wbia import _wbia_object
from wbia.control.controller_inject import make_ibs_register_decorator
import six
(print, rrr, profile) = ut.inject2(__name__, '[annot]')
logger = logging.getLogger('wbia')
CLASS_INJECT_KEY, register_ibs_method = make_ibs_register_decorator(__name__)
BASE_TYPE = type
[docs]@register_ibs_method
def annots(ibs, aids=None, uuids=None, **kwargs):
"""Makes an Annots object"""
if uuids is not None:
assert aids is None, 'specify one primary key'
aids = ibs.get_annot_aids_from_uuid(uuids)
if aids is None:
aids = ibs.get_valid_aids()
elif aids.__class__.__name__ == 'Annots':
return aids
aids = ut.ensure_iterable(aids)
return Annots(aids, ibs, **kwargs)
[docs]@register_ibs_method
def matches(ibs, ams=None, edges=None, uuid_edges=None, **kwargs):
"""Makes an Annots object"""
if uuid_edges is not None:
assert ams is None, 'specify one primary key'
assert edges is None, 'specify one primary key'
uuids1, uuids2 = list(zip(*uuid_edges))
aids1 = ibs.get_annot_aids_from_uuid(uuids1)
aids2 = ibs.get_annot_aids_from_uuid(uuids2)
ams = ibs.get_annotmatch_rowid_from_undirected_superkey(aids1, aids2)
if edges is not None:
assert ams is None, 'specify one primary key'
assert uuid_edges is None, 'specify one primary key'
aids1, aids2 = list(zip(*edges))
ams = ibs.get_annotmatch_rowid_from_undirected_superkey(aids1, aids2)
if ams is None:
ams = ibs._get_all_annotmatch_rowids()
elif ams.__class__.__name__ == 'AnnotMatches':
return ams
ams = ut.ensure_iterable(ams)
return AnnotMatches(ams, ibs, **kwargs)
@register_ibs_method
def _annot_groups(ibs, aids_list=None, config=None):
annots_list = [ibs.annots(aids, config=config) for aids in aids_list]
return AnnotGroups(annots_list, ibs)
ANNOT_BASE_ATTRS = [
'aid',
'parent_aid',
'multiple',
'age_months_est_max',
'age_months_est_min',
'sex',
'sex_texts',
'uuids',
'hashid_uuid',
'visual_uuids',
'hashid_visual_uuid',
'semantic_uuids',
'hashid_semantic_uuid',
'verts',
'thetas',
'bboxes',
'bbox_area',
'species_uuids',
'species',
'species_rowids',
'species_texts',
'viewpoint_int',
'viewpoint_code',
'qualities',
'quality_texts',
'exemplar_flags',
# DEPRICATE YAW
'yaw_texts',
'yaws',
'yaws_asfloat',
# Images
# 'image_rowids',
'gids',
'image_uuids',
'image_gps',
'image_gps2',
'image_unixtimes_asfloat',
'image_datetime_str',
'image_contributor_tag',
# Names
'nids',
'names',
'name_uuids',
# Inferred from context attrs
'contact_aids',
'num_contact_aids',
'groundfalse',
'groundtruth',
'num_groundtruth',
'has_groundtruth',
'otherimage_aids',
# Image Set
'imgset_uuids',
'imgsetids',
'image_set_texts',
# Occurrence / Encounter
'static_encounter',
'encounter_text',
'occurrence_text',
'primary_imageset',
# Tags
'all_tags',
'case_tags',
'annotmatch_tags',
'notes',
# Processing State
'reviewed',
'reviewed_matching_aids',
'has_reviewed_matching_aids',
'num_reviewed_matching_aids',
'detect_confidence',
]
ANNOT_SETTABLE_ATTRS = [
'age_months_est_max',
'age_months_est_min',
'bboxes',
'thetas',
'verts',
'qualities',
'quality_texts',
'viewpoint_int',
'viewpoint_code',
# DEPRICATE YAW
'yaw_texts',
'yaws',
'sex',
'sex_texts',
'species',
'exemplar_flags',
'static_encounter',
'multiple',
'case_tags',
'detect_confidence',
'reviewed',
'name_texts',
'names',
'notes',
'parent_rowid',
]
class _AnnotPropInjector(BASE_TYPE):
"""
Ignore:
>>> from wbia import _wbia_object
>>> import wbia
>>> ibs = wbia.opendb(defaultdb='testdb1')
>>> objname = 'annot'
>>> blacklist = ['annot_pair']
>>> _wbia_object._find_wbia_attrs(ibs, objname, blacklist)
"""
def __init__(metaself, name, bases, dct):
super(_AnnotPropInjector, metaself).__init__(name, bases, dct)
metaself.rrr = rrr
attrs = ANNOT_BASE_ATTRS
settable_attrs = ANNOT_SETTABLE_ATTRS
configurable_attrs = [
# Chip
'chip_dlensqrd',
'chip_fpath',
'chip_sizes',
'chip_thumbpath',
'chip_thumbtup',
'chips',
# Feat / FeatWeight / Kpts / Desc
'feat_rowids',
'num_feats',
'featweight_rowids',
'fgweights',
'fgweights_subset',
'kpts',
'kpts_distinctiveness',
'vecs',
'vecs_cache',
'vecs_subset',
]
# misc = [
# 'gar_rowids', 'alrids', 'alrids_oftype', 'lblannot_rowids',
# 'lblannot_rowids_oftype', 'lblannot_value_of_lbltype', 'rows',
# 'instancelist', 'lazy_dict', 'lazy_dict2', 'missing_uuid',
# 'been_adjusted', 'class_labels',
# ]
# extra_attrs = [
# # Age / Sex
# 'age_months_est', 'age_months_est_max', 'age_months_est_max_texts',
# 'age_months_est_min', 'age_months_est_min_texts',
# 'age_months_est_texts', 'sex', 'sex_texts',
# # Stats
# 'stats_dict', 'per_name_stats', 'qual_stats', 'info', 'yaw_stats',
# 'intermediate_viewpoint_stats',
# ]
# inverse_attrs = [
# # External lookups via superkeys
# 'aids_from_semantic_uuid',
# 'aids_from_uuid',
# 'aids_from_visual_uuid',
# 'rowids_from_partial_vuuids',
# ]
depcache_attrs = [
('hog', 'hog'),
('probchip', 'img'),
]
aliased_attrs = {
'time': 'image_unixtimes_asfloat',
'gps': 'image_gps2',
'chip_size': 'chip_sizes',
'yaw': 'yaws_asfloat',
'qual': 'qualities',
'name': 'names',
'nid': 'nids',
'unary_tags': 'case_tags',
# DEPRICATE
'rchip': 'chips',
'rchip_fpath': 'chip_fpath',
}
objname = 'annot'
_wbia_object._inject_getter_attrs(
metaself,
objname,
attrs,
configurable_attrs,
'depc_annot',
depcache_attrs,
settable_attrs,
aliased_attrs,
)
# TODO: incorporate dynamic setters
# def set_case_tags(self, tags):
# self._ibs.append_annot_case_tags(self._rowids, tags)
# fget = metaself.case_tags.fget
# fset = set_case_tags
# setattr(metaself, 'case_tags', property(fget, fset))
try:
from wbia import _autogen_annot_base
BASE = _autogen_annot_base._annot_base_class
except ImportError:
BASE = _wbia_object.ObjectList1D
# @ut.reloadable_class
[docs]@six.add_metaclass(_AnnotPropInjector)
class Annots(BASE):
"""
Represents a group of annotations. Efficiently accesses properties from a
database using lazy evaluation.
CommandLine:
python -m wbia.annots Annots
Example:
>>> # ENABLE_DOCTEST
>>> from wbia.annots import * # NOQA
>>> import wbia
>>> ibs = wbia.opendb(defaultdb='testdb1')
>>> aids = ibs.get_valid_aids()
>>> a = self = annots = Annots(aids, ibs)
>>> a.preload('vecs', 'kpts', 'nids')
>>> print(Annots.mro())
>>> print(ut.depth_profile(a.vecs))
>>> print(a)
Example:
>>> # ENABLE_DOCTEST
>>> from wbia.annots import * # NOQA
>>> import wbia
>>> ibs = wbia.opendb(defaultdb='testdb1')
>>> aids = ibs.get_valid_aids()
>>> a = self = annots = Annots(aids, ibs)
>>> a.preload('vecs', 'kpts', 'nids')
>>> a.disconnect()
>>> assert 'vecs' in a._internal_attrs.keys()
>>> assert a._ibs is None
>>> ut.assert_raises(KeyError, a._get_num_feats)
>>> a._ibs = ibs
>>> assert len(a._get_num_feats()) > 0
"""
# def __init__(self, aids, ibs, config=None, caching=False):
# super(Annots, self).__init__(aids, ibs, config, caching)
@property
def aids(self):
return self._rowids
[docs] def get_stats(self, **kwargs):
self._ibs.get_annot_stats_dict(self.aids, **kwargs)
[docs] def print_stats(self, **kwargs):
self._ibs.print_annot_stats(self.aids, **kwargs)
# @property
[docs] def get_speeds(self):
# import vtool as vt
edges = self.get_aidpairs()
speeds = self._ibs.get_annotpair_speeds(edges)
# edges = vt.pdist_indicies(len(annots))
# speeds = self._ibs.get_unflat_annots_speeds_list([self.aids])[0]
edge_to_speed = dict(zip(edges, speeds))
return edge_to_speed
[docs] def get_name_image_closure(self):
ibs = self._ibs
aids = self.aids
old_aids = []
while len(old_aids) != len(aids):
old_aids = aids
gids = ut.unique(ibs.get_annot_gids(aids))
other_aids = list(set(ut.flatten(ibs.get_image_aids(gids))))
other_nids = list(set(ibs.get_annot_nids(other_aids)))
aids = ut.flatten(ibs.get_name_aids(other_nids))
return aids
[docs] def group2(self, by):
"""
self = annots
by = annots.static_encounter
encounters = annots.group2(annots.static_encounter)
"""
annots_list = self.group(by)[1]
return AnnotGroups(annots_list, self._ibs)
[docs] def get_aidpairs(self):
aids = self.aids
aid_pairs = list(it.combinations(aids, 2))
return aid_pairs
[docs] def get_am_rowids(self, internal=True):
"""
if `internal is True` returns am rowids only between
annotations in this Annots object, otherwise returns
any am rowid that contains any aid in this Annots object.
"""
ibs = self._ibs
if internal:
ams = ibs.get_annotmatch_rowids_between(self.aids, self.aids)
else:
ams = ut.flatten(ibs.get_annotmatch_rowids_from_aid(self.aids))
return ams
[docs] def matches(self, internal=True):
ams = self.get_am_rowids(internal)
return self._ibs.matches(ams)
[docs] def get_am_rowids_and_pairs(self):
ibs = self._ibs
ams = self.get_am_rowids()
aid_pairs = ibs.get_annotmatch_aids(ams)
# aid_pairs = self.get_aidpairs()
# aids1 = ut.take_column(aid_pairs, 0)
# aids2 = ut.take_column(aid_pairs, 1)
# ams = ibs.get_annotmatch_rowid_from_undirected_superkey(aids1, aids2)
# flags = ut.not_list(ut.flag_None_items(ams))
# ams = ut.compress(ams, flags)
# aid_pairs = ut.compress(aid_pairs, flags)
return ams, aid_pairs
[docs] def get_am_aidpairs(self):
ibs = self._ibs
ams = self.get_am_rowids()
aids1 = ibs.get_annotmatch_aid1(ams)
aids2 = ibs.get_annotmatch_aid2(ams)
aid_pairs = list(zip(aids1, aids2))
return aid_pairs
@property
def hog_img(self):
from wbia import core_annots
return [core_annots.make_hog_block_image(hog) for hog in self.hog_hog]
def __hash__(self):
return hash(tuple(self.aids))
def __lt__(self, other):
if len(self.aids) == len(other.aids):
if len(self.aids) == 0:
return False
else:
return tuple(self) < tuple(other)
elif len(self.aids) < len(other.aids):
return True
else:
return False
def __eq__(self, other):
if len(self.aids) == len(other.aids):
return all(a == b for a, b in zip(self, other))
return False
[docs] def show(self, *args, **kwargs):
if len(self) != 1:
raise ValueError('Can only show one, got {}'.format(len(self)))
from wbia.viz import viz_chip
for aid in self:
return viz_chip.show_chip(self._ibs, aid, *args, **kwargs)
class _AnnotGroupPropInjector(BASE_TYPE):
def __init__(metaself, name, bases, dct):
super(_AnnotGroupPropInjector, metaself).__init__(name, bases, dct)
metaself.rrr = rrr
# TODO: move to wbia object as a group call
def _make_unflat_getter(objname, attrname):
ibs_funcname = 'get_%s_%s' % (objname, attrname)
def ibs_unflat_getter(self, *args, **kwargs):
ibs_callable = getattr(self._ibs, ibs_funcname)
rowids = self._rowids_list
ibs = self._ibs
return ibs.unflat_map(ibs_callable, rowids, *args, **kwargs)
ut.set_funcname(ibs_unflat_getter, 'unflat_' + ibs_funcname)
return ibs_unflat_getter
for attrname in ANNOT_BASE_ATTRS:
if hasattr(metaself, attrname):
logger.info('Cannot inject annot group attrname = %r' % (attrname,))
continue
ibs_unflat_getter = _make_unflat_getter('annot', attrname)
setattr(metaself, '_unflat_get_' + attrname, ibs_unflat_getter)
setattr(metaself, attrname, property(ibs_unflat_getter))
[docs]@ut.reloadable_class
@six.add_metaclass(_AnnotGroupPropInjector)
class AnnotGroups(ut.NiceRepr):
"""Effciently handle operations on multiple groups of annotations"""
def __init__(self, annots_list, ibs):
self._ibs = ibs
self.annots_list = annots_list
self._rowids_list = [a._rowids for a in self.annots_list]
def __len__(self):
return len(self.annots_list)
def __nice__(self):
import numpy as np
len_list = ut.lmap(len, self.annots_list)
num = len(self.annots_list)
mean = np.mean(len_list)
std = np.std(len_list)
nice = '(n=%r, μ=%.1f, σ=%.1f)' % (num, mean, std)
return nice
def __iter__(self):
return iter(self.annots_list)
def __getitem__(self, index):
return self.annots_list[index]
@property
def aids(self):
return [a.aids for a in self.annots_list]
@property
def images(self, config=None):
return self._ibs.images(self.gids, config)
@property
def match_tags(self):
"""returns pairwise tags within the annotation group"""
ams_list = self._ibs.get_unflat_am_rowids(self.aids)
tags = self._ibs.unflat_map(self._ibs.get_annotmatch_case_tags, ams_list)
return tags
class _AnnotMatchPropInjector(BASE_TYPE):
"""
Example:
>>> # DISABLE_DOCTEST
>>> from wbia import _wbia_object
>>> import wbia
>>> ibs = wbia.opendb(defaultdb='testdb1')
>>> objname = 'annotmatch'
>>> blacklist = []
>>> tup = _wbia_object._find_wbia_attrs(ibs, objname, blacklist)
>>> attrs, settable_attrs = tup
>>> print('attrs = ' + ut.repr4(attrs))
>>> print('settable_attrs = ' + ut.repr4(settable_attrs))
"""
def __init__(metaself, name, bases, dct):
super(_AnnotMatchPropInjector, metaself).__init__(name, bases, dct)
metaself.rrr = rrr
attrs = [
'aid1',
'aid2',
'confidence',
'count',
'evidence_decision',
'meta_decision',
'posixtime_modified',
'reviewer',
'tag_text',
'case_tags',
]
settable_attrs = [
'confidence',
'count',
'evidence_decision',
'meta_decision',
'posixtime_modified',
'reviewer',
'tag_text',
]
configurable_attrs = []
depcache_attrs = []
aliased_attrs = {}
objname = 'annotmatch'
_wbia_object._inject_getter_attrs(
metaself,
objname,
attrs,
configurable_attrs,
None,
depcache_attrs,
settable_attrs,
aliased_attrs,
)
[docs]@six.add_metaclass(_AnnotMatchPropInjector)
class AnnotMatches(BASE):
"""
Represents a group of annotations. Efficiently accesses properties from a
database using lazy evaluation.
CommandLine:
python -m wbia.annots Annots
Example:
>>> # ENABLE_DOCTEST
>>> from wbia.annots import * # NOQA
>>> import wbia
>>> ibs = wbia.opendb(defaultdb='testdb1')
>>> aids = ibs.get_valid_aids()
>>> annots = Annots(aids, ibs)
>>> ams = annots.get_am_rowids()
>>> matches = self = ibs.matches()
>>> ed1 = matches.evidence_decision
>>> md2 = matches.meta_decision
>>> table = ibs.db.get_table_as_pandas('annotmatch')
>>> assert len(table) == len(matches)
"""
@property
def edges(self):
return list(zip(self.aid1, self.aid2))
@property
def confidence_code(self):
INT_TO_CODE = self._ibs.const.CONFIDENCE.INT_TO_CODE
return [INT_TO_CODE[c] for c in self.confidence]
@property
def meta_decision_code(self):
INT_TO_CODE = self._ibs.const.META_DECISION.INT_TO_CODE
return [INT_TO_CODE[c] for c in self.meta_decision]
@property
def evidence_decision_code(self):
INT_TO_CODE = self._ibs.const.EVIDENCE_DECISION.INT_TO_CODE
return [INT_TO_CODE[c] for c in self.evidence_decision]