Source code for ibeis.algo.hots.name_scoring

# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals
from six.moves import zip, range, map  # NOQA
import numpy as np
import vtool as vt
import utool as ut
import itertools
from ibeis.algo.hots import hstypes
from ibeis.algo.hots import _pipeline_helpers as plh  # NOQA
from collections import namedtuple
(print, rrr, profile) = ut.inject2(__name__, '[nscoring]')

NameScoreTup = namedtuple('NameScoreTup', ('sorted_nids', 'sorted_nscore',
                                           'sorted_aids', 'sorted_scores'))


[docs]def testdata_chipmatch(): from ibeis.algo.hots import chip_match # only the first indicies will matter in these test # feature matches fm_list = [ np.array([(0, 9), (1, 9), (2, 9), (3, 9)], dtype=np.int32), np.array([(0, 9), (1, 9), (2, 9), (3, 9)], dtype=np.int32), np.array([(0, 9), (1, 9), (2, 9), (3, 9)], dtype=np.int32), np.array([(4, 9), (5, 9), (6, 9), (3, 9)], dtype=np.int32), np.array([(0, 9), (1, 9), (2, 9), (3, 9), (4, 9)], dtype=np.int32) ] # score each feature match as 1 fsv_list = [ np.array([(1,), (1,), (1,), (1,)], dtype=hstypes.FS_DTYPE), np.array([(1,), (1,), (1,), (1,)], dtype=hstypes.FS_DTYPE), np.array([(1,), (1,), (1,), (1,)], dtype=hstypes.FS_DTYPE), np.array([(1,), (1,), (1,), (1,)], dtype=hstypes.FS_DTYPE), np.array([(1,), (1,), (1,), (1,), (1, )], dtype=hstypes.FS_DTYPE), ] cm = chip_match.ChipMatch( qaid=1, daid_list=np.array([1, 2, 3, 4, 5], dtype=np.int32), fm_list=fm_list, fsv_list=fsv_list, dnid_list=np.array([1, 1, 2, 2, 3], dtype=np.int32), fsv_col_lbls=['count'], ) #print(cm.get_rawinfostr()) #if False: # # DEBUG # cm.rrr() # print(cm.get_rawinfostr()) # print(cm.get_cvs_str(ibs=qreq_.ibs, numtop=None)) return cm
@profile
[docs]def compute_nsum_score(cm, qreq_=None): r""" nsum Args: cm (ibeis.ChipMatch): Returns: tuple: (unique_nids, nsum_score_list) CommandLine: python -m ibeis.algo.hots.name_scoring --test-compute_nsum_score python -m ibeis.algo.hots.name_scoring --test-compute_nsum_score:0 python -m ibeis.algo.hots.name_scoring --test-compute_nsum_score:2 utprof.py -m ibeis.algo.hots.name_scoring --test-compute_nsum_score:2 utprof.py -m ibeis.algo.hots.pipeline --test-request_ibeis_query_L0:0 --db PZ_Master1 -a timectrl:qindex=0:256 Example0: >>> # ENABLE_DOCTEST >>> from ibeis.algo.hots.name_scoring import * # NOQA >>> # build test data >>> cm = testdata_chipmatch() >>> # execute function >>> (unique_nids, nsum_score_list) = compute_nsum_score(cm) >>> result = ut.list_str((unique_nids, nsum_score_list), label_list=['unique_nids', 'nsum_score_list'], with_dtype=False) >>> print(result) unique_nids = np.array([1, 2, 3]) nsum_score_list = np.array([ 4., 7., 5.]) Example1: >>> # ENABLE_DOCTEST >>> from ibeis.algo.hots.name_scoring import * # NOQA >>> #ibs, qreq_, cm_list = plh.testdata_pre_sver('testdb1', qaid_list=[1]) >>> ibs, qreq_, cm_list = plh.testdata_post_sver('PZ_MTEST', qaid_list=[18]) >>> cm = cm_list[0] >>> cm.evaluate_dnids(qreq_.ibs) >>> cm._cast_scores() >>> #cm.qnid = 1 # Hack for testdb1 names >>> nsum_nid_list, nsum_score_list = compute_nsum_score(cm, qreq_) >>> assert np.all(nsum_nid_list == cm.unique_nids), 'nids out of alignment' >>> flags = (nsum_nid_list == cm.qnid) >>> max_true = nsum_score_list[flags].max() >>> max_false = nsum_score_list[~flags].max() >>> assert max_true > max_false, 'is this truely a hard case?' >>> assert max_true > 1.2, 'score=%r should be higher for aid=18' % (max_true,) >>> nsum_nid_list2, nsum_score_list2, _ = compute_nsum_score2(cm, qreq_) >>> assert np.allclose(nsum_score_list2, nsum_score_list), 'something is very wrong' >>> #assert np.all(nsum_score_list2 == nsum_score_list), 'could be a percision issue' Example2: >>> # ENABLE_DOCTEST >>> from ibeis.algo.hots.name_scoring import * # NOQA >>> #ibs, qreq_, cm_list = plh.testdata_pre_sver('testdb1', qaid_list=[1]) >>> ibs, qreq_, cm_list = plh.testdata_post_sver('PZ_MTEST', qaid_list=[18], cfgdict=dict(augment_queryside_hack=True)) >>> cm = cm_list[0] >>> cm.score_nsum(qreq_) >>> #cm.evaluate_dnids(qreq_.ibs) >>> #cm.qnid = 1 # Hack for testdb1 names >>> #nsum_nid_list, nsum_score_list = compute_nsum_score(cm, qreq_=qreq_) >>> ut.quit_if_noshow() >>> cm.show_ranked_matches(qreq_, ori=True) Example3: >>> # DISABLE_DOCTEST >>> from ibeis.algo.hots.name_scoring import * # NOQA >>> #ibs, qreq_, cm_list = plh.testdata_pre_sver('testdb1', qaid_list=[1]) >>> ibs, qreq_, cm_list = plh.testdata_post_sver('testdb1', qaid_list=[1], cfgdict=dict(augment_queryside_hack=True)) >>> cm = cm_list[0] >>> cm.score_nsum(qreq_) >>> #cm.evaluate_dnids(qreq_.ibs) >>> #cm.qnid = 1 # Hack for testdb1 names >>> #nsum_nid_list, nsum_score_list = compute_nsum_score(cm, qreq_=qreq_) >>> ut.quit_if_noshow() >>> cm.show_ranked_matches(qreq_, ori=True) Example4: >>> # ENABLE_DOCTEST >>> # FIXME: breaks when fg_on=True >>> from ibeis.algo.hots.name_scoring import * # NOQA >>> from ibeis.algo.hots import name_scoring >>> from ibeis.algo.hots import scoring >>> import ibeis >>> # Test to make sure name score and chips score are equal when per_name=1 >>> qreq_, args = plh.testdata_pre( >>> 'spatial_verification', defaultdb='PZ_MTEST', >>> a=['default:dpername=1,qsize=1,dsize=10'], >>> p=['default:K=1,fg_on=True,sqrd_dist_on=True']) >>> cm = args.cm_list_FILT[0] >>> ibs = qreq_.ibs >>> # Ensure there is only one aid per database name >>> assert isinstance(ibs, ibeis.control.IBEISControl.IBEISController) >>> #stats_dict = ibs.get_annot_stats_dict(qreq_.get_external_daids(), prefix='d') >>> #stats = stats_dict['dper_name'] >>> stats = ibs.get_annot_per_name_stats(qreq_.get_external_daids()) >>> print('per_name_stats = %s' % (ut.dict_str(stats, nl=False),)) >>> assert stats['mean'] == 1 and stats['std'] == 0, 'this test requires one annot per name in the database' >>> cm.evaluate_dnids(qreq_.ibs) >>> cm.assert_self(qreq_) >>> cm._cast_scores() >>> # cm.fs_list = cm.fs_list.astype(np.float) >>> nsum_nid_list, nsum_score_list = name_scoring.compute_nsum_score(cm, qreq_) >>> nsum_nid_list2, nsum_score_list2, _ = name_scoring.compute_nsum_score2(cm, qreq_) >>> csum_score_list = scoring.compute_csum_score(cm) >>> vt.asserteq(nsum_score_list, csum_score_list) >>> vt.asserteq(nsum_score_list, csum_score_list, thresh=0, iswarning=True) >>> vt.asserteq(nsum_score_list2, csum_score_list, thresh=0, iswarning=True) >>> #assert np.allclose(nsum_score_list, csum_score_list), 'should be the same when K=1 and per_name=1' >>> #assert all(nsum_score_list == csum_score_list), 'should be the same when K=1 and per_name=1' >>> #assert all(nsum_score_list2 == csum_score_list), 'should be the same when K=1 and per_name=1' >>> # Evaluate parts of the sourcecode Ignore: assert all(nsum_score_list3 == csum_score_list), 'should be the same when K=1 and per_name=1' fm_list = fm_list[0:1] fs_list = fs_list[0:1] featflag_list2 = featflag_list2[0:1] dnid_list = dnid_list[0:1] name_groupxs2 = name_groupxs2[0:1] nsum_nid_list2 = nsum_nid_list2[0:1] """ #assert qreq_ is not None try: HACK_SINGLE_ORI = qreq_ is not None and (qreq_.qparams.augment_queryside_hack or qreq_.qparams.rotation_invariance) except AttributeError: HACK_SINGLE_ORI = qreq_ is not None and (qreq_.config.augment_queryside_hack or qreq_.config.feat_cfg.rotation_invariance) pass # The core for each feature match # # The query feature index for each feature match fm_list = cm.fm_list fs_list = cm.get_fsv_prod_list() dnid_list = cm.dnid_list #-- fx1_list = [fm.T[0] for fm in fm_list] """ # Try a rebase? fx1_list = list(map(vt.compute_unique_data_ids_, fx1_list)) """ # Group annotation matches by name nsum_nid_list, name_groupxs = vt.group_indices(dnid_list) name_grouped_fx1_list = vt.apply_grouping_(fx1_list, name_groupxs) name_grouped_fs_list = vt.apply_grouping_(fs_list, name_groupxs) # Stack up all matches to a particular name name_grouped_fx1_flat = list(map(np.hstack, name_grouped_fx1_list)) name_grouped_fs_flat = list(map(np.hstack, name_grouped_fs_list)) """ assert np.all(name_grouped_fs_list[0][0] == fs_list[0]) assert np.all(name_grouped_fs_flat[0] == fs_list[0]) """ if HACK_SINGLE_ORI: # keypoints with the same xy can only have one of them vote kpts1 = qreq_.ibs.get_annot_kpts(cm.qaid, config2_=qreq_.get_external_query_config2()) xys1_ = vt.get_xys(kpts1).T kpts_xyid_list = vt.compute_unique_arr_dataids(xys1_) # Make nested group for every name by query feature index (accounting for duplicate orientation) name_grouped_xyid_flat = [kpts_xyid_list.take(fx1) for fx1 in name_grouped_fx1_flat] feat_groupxs_list = [vt.group_indices(xyid_flat)[1] for xyid_flat in name_grouped_xyid_flat] else: # make unique indicies using feature indexes feat_groupxs_list = [vt.group_indices(fx1_flat)[1] for fx1_flat in name_grouped_fx1_flat] # Make nested group for every name by unique query feature index feat_grouped_fs_list = [[fs_flat.take(xs, axis=0) for xs in feat_groupxs] for fs_flat, feat_groupxs in zip(name_grouped_fs_flat, feat_groupxs_list)] """ np.array(feat_grouped_fs_list)[0].T[0] == fs_list """ if False: valid_fs_list = [ np.array([group.max() for group in grouped_fs]) #np.array([group[group.argmax()] for group in grouped_fs]) for grouped_fs in feat_grouped_fs_list ] nsum_score_list4 = np.array([valid_fs.sum() for valid_fs in valid_fs_list]) # NOQA # Prevent a feature from voting twice: # take only the max score that a query feature produced #name_grouped_valid_fs_list1 =[np.array([fs_group.max() for fs_group in feat_grouped_fs]) # for feat_grouped_fs in feat_grouped_fs_list] nsum_score_list = np.array([np.sum([fs_group.max() for fs_group in feat_grouped_fs]) for feat_grouped_fs in feat_grouped_fs_list]) return nsum_nid_list, nsum_score_list
@profile
[docs]def compute_nsum_score2(cm, qreq_=None): r""" Example3: >>> # DISABLE_DOCTEST >>> from ibeis.algo.hots.name_scoring import * # NOQA >>> #ibs, qreq_, cm_list = plh.testdata_pre_sver('testdb1', qaid_list=[1]) >>> ibs, qreq_, cm_list = plh.testdata_post_sver('testdb1', qaid_list=[1], cfgdict=dict(fg_on=False, augment_queryside_hack=True)) >>> cm = cm_list[0] >>> cm.evaluate_dnids(qreq_.ibs) >>> nsum_nid_list1, nsum_score_list1, featflag_list1 = compute_nsum_score2(cm, qreq_) >>> nsum_nid_list2, nsum_score_list2 = compute_nsum_score(cm, qreq_) >>> ut.quit_if_noshow() >>> cm.show_ranked_matches(qreq_, ori=True) """ featflag_list2 = get_chipmatch_namescore_nonvoting_feature_flags(cm, qreq_) fs_list = cm.get_fsv_prod_list() name_groupxs2 = cm.name_groupxs nsum_nid_list2 = cm.unique_nids #-- valid_fs_list2 = vt.zipcompress(fs_list, featflag_list2) name_grouped_valid_fs_list2 = vt.apply_grouping_(valid_fs_list2, name_groupxs2) nsum_score_list2 = np.array([sum(list(map(np.sum, valid_fs_group))) for valid_fs_group in name_grouped_valid_fs_list2]) if False: nsum_score_list3 = np.array([ # NOQA np.sum([fs_group.sum() for fs_group in valid_fs_group]) for valid_fs_group in name_grouped_valid_fs_list2]) return nsum_nid_list2, nsum_score_list2, featflag_list2
@profile
[docs]def get_chipmatch_namescore_nonvoting_feature_flags(cm, qreq_=None): """ Computes flags to desribe which features can or can not vote CommandLine: python -m ibeis.algo.hots.name_scoring --exec-get_chipmatch_namescore_nonvoting_feature_flags Example: >>> # ENABLE_DOCTEST >>> # FIXME: breaks when fg_on=True >>> from ibeis.algo.hots.name_scoring import * # NOQA >>> from ibeis.algo.hots import name_scoring >>> # Test to make sure name score and chips score are equal when per_name=1 >>> qreq_, args = plh.testdata_pre('spatial_verification', defaultdb='PZ_MTEST', a=['default:dpername=1,qsize=1,dsize=10'], p=['default:K=1,fg_on=True']) >>> cm_list = args.cm_list_FILT >>> ibs = qreq_.ibs >>> cm = cm_list[0] >>> cm.evaluate_dnids(qreq_.ibs) >>> featflat_list = get_chipmatch_namescore_nonvoting_feature_flags(cm, qreq_) >>> assert all(list(map(np.all, featflat_list))), 'all features should be able to vote in K=1, per_name=1 case' """ try: HACK_SINGLE_ORI = qreq_ is not None and (qreq_.qparams.augment_queryside_hack or qreq_.qparams.rotation_invariance) except AttributeError: HACK_SINGLE_ORI = qreq_ is not None and (qreq_.config.augment_queryside_hack or qreq_.config.feat_cfg.rotation_invariance) pass # The core for each feature match fs_list = cm.get_fsv_prod_list() # The query feature index for each feature match fm_list = cm.fm_list kpts1 = None if not HACK_SINGLE_ORI else qreq_.ibs.get_annot_kpts(cm.qaid, config2_=qreq_.get_external_query_config2()) dnid_list = cm.dnid_list name_groupxs = cm.name_groupxs featflag_list = get_namescore_nonvoting_feature_flags(fm_list, fs_list, dnid_list, name_groupxs, kpts1=kpts1) return featflag_list
@profile
[docs]def get_namescore_nonvoting_feature_flags(fm_list, fs_list, dnid_list, name_groupxs, kpts1=None): r""" fm_list = [fm[:min(len(fm), 10)] for fm in fm_list] fs_list = [fs[:min(len(fs), 10)] for fs in fs_list] """ fx1_list = [fm.T[0] for fm in fm_list] # Group annotation matches by name name_grouped_fx1_list = vt.apply_grouping_(fx1_list, name_groupxs) name_grouped_fs_list = vt.apply_grouping_(fs_list, name_groupxs) # Stack up all matches to a particular name, keep track of original indicies via offets name_invertable_flat_fx1_list = list(map(ut.invertible_flatten2_numpy, name_grouped_fx1_list)) name_grouped_fx1_flat = ut.get_list_column(name_invertable_flat_fx1_list, 0) name_grouped_invertable_cumsum_list = ut.get_list_column(name_invertable_flat_fx1_list, 1) name_grouped_fs_flat = list(map(np.hstack, name_grouped_fs_list)) if kpts1 is not None: xys1_ = vt.get_xys(kpts1).T kpts_xyid_list = vt.compute_unique_data_ids(xys1_) # Make nested group for every name by query feature index (accounting for duplicate orientation) name_grouped_xyid_flat = list(kpts_xyid_list.take(fx1) for fx1 in name_grouped_fx1_flat) xyid_groupxs_list = list(vt.group_indices(xyid_flat)[1] for xyid_flat in name_grouped_xyid_flat) name_group_fx1_groupxs_list = xyid_groupxs_list else: # Make nested group for every name by query feature index fx1_groupxs_list = [vt.group_indices(fx1_flat)[1] for fx1_flat in name_grouped_fx1_flat] name_group_fx1_groupxs_list = fx1_groupxs_list name_grouped_fid_grouped_fs_list = [ vt.apply_grouping(fs_flat, fid_groupxs) for fs_flat, fid_groupxs in zip(name_grouped_fs_flat, name_group_fx1_groupxs_list) ] # Flag which features are valid in this grouped space. Only one keypoint should be able to vote # for each group name_grouped_fid_grouped_isvalid_list = [ np.array([fs_group.max() == fs_group for fs_group in fid_grouped_fs_list]) for fid_grouped_fs_list in name_grouped_fid_grouped_fs_list ] # Go back to being grouped only in name space #dtype = np.bool name_grouped_isvalid_flat_list = [ vt.invert_apply_grouping2(fid_grouped_isvalid_list, fid_groupxs, dtype=np.bool) for fid_grouped_isvalid_list, fid_groupxs in zip(name_grouped_fid_grouped_isvalid_list, name_group_fx1_groupxs_list) ] name_grouped_isvalid_unflat_list = [ ut.unflatten2(isvalid_flat, invertable_cumsum_list) for isvalid_flat, invertable_cumsum_list in zip(name_grouped_isvalid_flat_list, name_grouped_invertable_cumsum_list) ] # Reports which features were valid in name scoring for every annotation featflag_list = vt.invert_apply_grouping(name_grouped_isvalid_unflat_list, name_groupxs) return featflag_list
@profile
[docs]def align_name_scores_with_annots(annot_score_list, annot_aid_list, daid2_idx, name_groupxs, name_score_list): r""" takes name scores and gives them to the best annotation Returns: score_list: list of scores aligned with cm.daid_list and cm.dnid_list Args: annot_score_list (list): score associated with each annot name_groupxs (list): groups annot_score lists into groups compatible with name_score_list name_score_list (list): score assocated with name nid2_nidx (dict): mapping from nids to index in name score list CommandLine: python -m ibeis.algo.hots.name_scoring --test-align_name_scores_with_annots python -m ibeis.algo.hots.name_scoring --test-align_name_scores_with_annots --show Example: >>> # ENABLE_DOCTEST >>> from ibeis.algo.hots.name_scoring import * # NOQA >>> #ibs, qreq_, cm_list = plh.testdata_pre_sver('PZ_MTEST', qaid_list=[18]) >>> ibs, qreq_, cm_list = plh.testdata_post_sver('PZ_MTEST', qaid_list=[18]) >>> cm = cm_list[0] >>> cm.evaluate_csum_score(qreq_) >>> cm.evaluate_nsum_score(qreq_) >>> # Annot aligned lists >>> annot_score_list = cm.algo_annot_scores['csum'] >>> annot_aid_list = cm.daid_list >>> daid2_idx = cm.daid2_idx >>> # Name aligned lists >>> name_score_list = cm.algo_name_scores['nsum'] >>> name_groupxs = cm.name_groupxs >>> # Execute Function >>> score_list = align_name_scores_with_annots(annot_score_list, annot_aid_list, daid2_idx, name_groupxs, name_score_list) >>> # Check that the correct name gets the highest score >>> target = name_score_list[cm.nid2_nidx[cm.qnid]] >>> test_index = np.where(score_list == target)[0][0] >>> cm.score_list = score_list >>> ut.assert_eq(ibs.get_annot_name_rowids(cm.daid_list[test_index]), cm.qnid) >>> assert ut.isunique(cm.dnid_list[score_list > 0]), 'bad name score' >>> assert cm.get_top_nids()[0] == cm.unique_nids[cm.nsum_score_list.argmax()], 'bug in alignment' >>> ut.quit_if_noshow() >>> cm.show_ranked_matches(qreq_) >>> ut.show_if_requested() Example: >>> from ibeis.algo.hots.name_scoring import * # NOQA >>> annot_score_list = [] >>> annot_aid_list = [] >>> daid2_idx = {} >>> # Name aligned lists >>> name_score_list = np.array([], dtype=np.float32) >>> name_groupxs = [] >>> # Execute Function >>> score_list = align_name_scores_with_annots(annot_score_list, annot_aid_list, daid2_idx, name_groupxs, name_score_list) Ignore: dict(zip(cm.dnid_list, cm.score_list)) dict(zip(cm.unique_nids, cm.nsum_score_list)) np.all(nid_list == cm.unique_nids) """ if len(name_groupxs) == 0: score_list = np.empty(0, dtype=name_score_list.dtype) return score_list else: # Group annot aligned indicies by nid annot_aid_list = np.array(annot_aid_list) #nid_list, groupxs = vt.group_indices(annot_nid_list) grouped_scores = vt.apply_grouping(annot_score_list, name_groupxs) grouped_annot_aids = vt.apply_grouping(annot_aid_list, name_groupxs) flat_grouped_aids = np.hstack(grouped_annot_aids) #flat_groupxs = np.hstack(name_groupxs) #if __debug__: # sum_scores = np.array([scores.sum() for scores in grouped_scores]) # max_scores = np.array([scores.max() for scores in grouped_scores]) # assert np.all(name_score_list <= sum_scores) # assert np.all(name_score_list > max_scores) # +------------ # Find the position of the highest name_scoring annotation for each name # IN THE FLATTENED GROUPED ANNOT_AID_LIST (this was the bug) offset_list = np.array([annot_scores.argmax() for annot_scores in grouped_scores]) # Find the starting position of eatch group use chain to start offsets with 0 _padded_scores = itertools.chain([[]], grouped_scores[:-1]) sizeoffset_list = np.array([len(annot_scores) for annot_scores in _padded_scores]) baseindex_list = sizeoffset_list.cumsum() # Augment starting position with offset index annot_idx_list = np.add(baseindex_list, offset_list) # L______________ best_aid_list = flat_grouped_aids[annot_idx_list] best_idx_list = ut.dict_take(daid2_idx, best_aid_list) # give the annotation domain a name score #score_list = np.zeros(len(annot_score_list), dtype=name_score_list.dtype) score_list = np.full(len(annot_score_list), fill_value=-np.inf, dtype=name_score_list.dtype) #score_list = np.full(len(annot_score_list), fill_value=np.nan, dtype=name_score_list.dtype) #score_list = np.nan(len(annot_score_list), dtype=name_score_list.dtype) # HACK: we need to set these to 'low' values and we also have to respect negatives #score_list[:] = -np.inf # make sure that the nid_list from group_indicies and the nids belonging to # name_score_list (cm.unique_nids) are in alignment #nidx_list = np.array(ut.dict_take(nid2_nidx, nid_list)) # THIS ASSUMES name_score_list IS IN ALIGNMENT WITH BOTH cm.unique_nids and # nid_list (which should be == cm.unique_nids) score_list[best_idx_list] = name_score_list return score_list #def get_best_annot_per_name_indices(cm): # grouped_scores = vt.apply_grouping(cm.annot_score_list, cm.name_groupxs) # # Find the position of the highest name_scoring annotation for each name # offset_list = np.array([annot_scores.argmax() for annot_scores in grouped_scores]) # # Find the starting position of eatch group use chain to start offsets with 0 # _padded_scores = itertools.chain([[]], grouped_scores[:-1]) # sizeoffset_list = np.array([len(annot_scores) for annot_scores in _padded_scores]) # baseindex_list = sizeoffset_list.cumsum() # # Augment starting position with offset index # annot_idx_list = np.add(baseindex_list, offset_list)
@profile
[docs]def group_scores_by_name(ibs, aid_list, score_list): r""" Converts annotation scores to name scores. Over multiple annotations finds keypoints best match and uses that score. CommandLine: python -m ibeis.algo.hots.name_scoring --test-group_scores_by_name Example: >>> # ENABLE_DOCTEST >>> from ibeis.algo.hots.name_scoring import * # NOQA >>> import ibeis >>> cm, qreq_ = ibeis.testdata_cm('PZ_MTEST') >>> ibs = qreq_.ibs >>> #print(cm.get_inspect_str(qreq_)) >>> aid_list = cm.daid_list >>> score_list = cm.annot_score_list >>> nscoretup = group_scores_by_name(ibs, aid_list, score_list) >>> (sorted_nids, sorted_nscore, sorted_aids, sorted_scores) = nscoretup >>> ut.assert_eq(sorted_nids[0], cm.qnid) TODO: # TODO: this code needs a really good test case #>>> result = np.array_repr(sorted_nids[0:2]) #>>> print(result) #array([1, 5]) Ignore:: # hack in dict of Nones prob for testing import six qres.aid2_prob = {aid:None for aid in six.iterkeys(qres.aid2_score)} array([ 1, 5, 26]) [2 6 5] Timeit:: import ibeis ibs = ibeis.opendb('PZ_MTEST') aid_list = ibs.get_valid_aids() aid_arr = np.array(aid_list) %timeit ibs.get_annot_name_rowids(aid_list) %timeit ibs.get_annot_name_rowids(aid_arr) """ assert len(score_list) == len(aid_list), 'scores and aids must be associated' score_arr = np.array(score_list) nid_list = np.array(ibs.get_annot_name_rowids(aid_list)) aid_list = np.array(aid_list) # Group scores by name unique_nids, groupxs = vt.group_indices(nid_list) grouped_scores = np.array(vt.apply_grouping(score_arr, groupxs)) grouped_aids = np.array(vt.apply_grouping(aid_list, groupxs)) # Build representative score per group # (find each keypoints best match per annotation within the name) group_nscore = np.array([scores.max() for scores in grouped_scores]) group_sortx = group_nscore.argsort()[::-1] # Top nids sorted_nids = unique_nids.take(group_sortx, axis=0) sorted_nscore = group_nscore.take(group_sortx, axis=0) # Initial sort of aids _sorted_aids = grouped_aids.take(group_sortx, axis=0) _sorted_scores = grouped_scores.take(group_sortx, axis=0) # Secondary sort of aids sorted_sortx = [scores.argsort()[::-1] for scores in _sorted_scores] sorted_scores = [scores.take(sortx) for scores, sortx in zip(_sorted_scores, sorted_sortx)] sorted_aids = [aids.take(sortx) for aids, sortx in zip(_sorted_aids, sorted_sortx)] nscoretup = NameScoreTup(sorted_nids, sorted_nscore, sorted_aids, sorted_scores) return nscoretup
if __name__ == '__main__': """ CommandLine: python -m ibeis.algo.hots.name_scoring python -m ibeis.algo.hots.name_scoring --allexamples python -m ibeis.algo.hots.name_scoring --allexamples --noface --nosrc """ import multiprocessing multiprocessing.freeze_support() # for win32 import utool as ut # NOQA ut.doctest_funcs()