# -*- coding: utf-8 -*-
"""
This module controls the GUI backend. It is the layer between the GUI frontend
(newgui.py) and the IBEIS controller. All the functionality of the nonvisual
gui components is written or called from here
TODO:
open_database should not allow you to open subfolders
python -m utool.util_inspect check_module_usage --pat="guiback.py"
Notes:
LAYOUT TERMS;
Margins / Content Margins;
- space around the widgets in the layout
Spacing
- space between widges in the layout
Stretch
- relative size ratio vector (1 component for each widget)
"""
from __future__ import absolute_import, division, print_function, unicode_literals
import six # NOQA
import sys
import functools
import traceback # NOQA
import utool as ut
import ubelt as ub
import wbia.guitool as gt
from wbia.guitool import slot_, signal_, cast_from_qt
from wbia.guitool.__PYQT__ import QtCore, QtGui, QtWidgets
from wbia import constants as const
from wbia.other import ibsfuncs
from wbia import sysres
from wbia import viz
from wbia.control import IBEISControl
from wbia.gui import clock_offset_gui
from wbia.gui import guiexcept
from wbia.gui import guiheaders as gh
from wbia.gui import newgui
from wbia.viz import interact
from os.path import exists, join, dirname, normpath
from wbia.plottool import fig_presenter
from six.moves import zip
(print, rrr, profile) = ut.inject2(__name__, '[back]')
VERBOSE = ut.VERBOSE
[docs]def backreport(func):
"""
reports errors on backend functions
should be around every function by default
"""
def backreport_wrapper(back, *args, **kwargs):
try:
result = func(back, *args, **kwargs)
except guiexcept.UserCancel:
print('handling user cancel')
return None
except Exception as ex:
# error_msg = "Error caught while performing function. \n %r" % ex
error_msg = 'Error: %s' % (ex,)
import traceback # NOQA
detailed_msg = traceback.format_exc()
gt.msgbox(title='Error Catch!', msg=error_msg, detailed_msg=detailed_msg)
raise
return result
backreport_wrapper = ut.preserve_sig(backreport_wrapper, func)
return backreport_wrapper
[docs]def backblock(func):
""" BLOCKING DECORATOR
TODO: This decorator has to be specific to either front or back. Is there a
way to make it more general?
"""
@functools.wraps(func)
# @gt.checks_qt_error
@backreport
def bacblock_wrapper(back, *args, **kwargs):
_wasBlocked_ = back.front.blockSignals(True)
try:
result = func(back, *args, **kwargs)
except Exception:
# error_msg = "Error caught while performing function. \n %r" % ex
# gt.msgbox(title="Error Catch!", msg=error_msg)
raise
finally:
back.front.blockSignals(_wasBlocked_)
return result
bacblock_wrapper = ut.preserve_sig(bacblock_wrapper, func)
return bacblock_wrapper
[docs]def blocking_slot(*types_):
"""
A blocking slot accepts the types which are passed to QtCore.pyqtSlot.
In addition it also causes the gui frontend to block signals while
the decorated function is processing.
"""
def wrap_bslot(func):
# @slot_(*types_)
@QtCore.pyqtSlot(*types_)
@backblock
@functools.wraps(func)
def wrapped_bslot(*args, **kwargs):
result = func(*args, **kwargs)
sys.stdout.flush()
return result
wrapped_bslot = ut.preserve_sig(wrapped_bslot, func)
return wrapped_bslot
return wrap_bslot
[docs]class CustomAnnotCfgSelector(gt.GuitoolWidget):
"""
CommandLine:
python -m wbia.gui.guiback CustomAnnotCfgSelector --show
python -m wbia.gui.guiback CustomAnnotCfgSelector --show --db PZ_MTEST
python -m wbia.gui.guiback CustomAnnotCfgSelector --show --debugwidget
python -m wbia.gui.guiback show_advanced_id_interface --show
Example:
>>> # DISABLE_DOCTEST
>>> from wbia.gui.guiback import * # NOQA
>>> import wbia
>>> gt.ensure_qtapp()
>>> ibs = wbia.opendb(defaultdb='testdb1')
>>> self = CustomAnnotCfgSelector(ibs)
>>> rect = gt.QtWidgets.QDesktopWidget().availableGeometry(screen=0)
>>> self.move(rect.x(), rect.y())
>>> self.show()
>>> self.apply_new_config()
>>> ut.quit_if_noshow()
>>> gt.qtapp_loop(qwin=self, freq=10)
"""
def __init__(self, ibs):
from wbia.expt import annotation_configs
from wbia import dtool
from wbia.guitool import PrefWidget2
from wbia.guitool.__PYQT__.QtCore import Qt
super(CustomAnnotCfgSelector, self).__init__()
self.ibs = ibs
self.qaids = None
self.accept_flag = False
class TmpAnnotConfig(dtool.Config):
_param_info_list = (
annotation_configs.INDEPENDENT_DEFAULTS_PARAM_INFO
+ annotation_configs.INTRAGROUP_DEFAULTS_PARAM_INFO
+ annotation_configs.SAMPLE_DEFAULTS_PARAM_INFO
+ annotation_configs.SUBINDEX_DEFAULTS_PARAM_INFO
)
class TmpPipelineConfig(dtool.Config):
_param_info_list = [
ut.ParamInfo('K', ibs.cfg.query_cfg.nn_cfg.K, min_=1, none_ok=False),
ut.ParamInfo(
'Knorm', ibs.cfg.query_cfg.nn_cfg.Knorm, min_=1, none_ok=False
),
# ibs.cfg.query_cfg.nn_cfg.lookup_paraminfo('Knorm'),
ibs.cfg.query_cfg.nnweight_cfg.lookup_paraminfo('normalizer_rule'),
ut.ParamInfo(
'fgw_thresh',
ibs.cfg.query_cfg.flann_cfg.fgw_thresh,
type_=float,
min_=0,
max_=1,
),
ut.ParamInfo(
'query_rotation_heuristic', ibs.cfg.query_cfg.query_rotation_heuristic
),
ut.ParamInfo(
'minscale_thresh',
ibs.cfg.query_cfg.flann_cfg.minscale_thresh,
type_=float,
),
ut.ParamInfo(
'maxscale_thresh',
ibs.cfg.query_cfg.flann_cfg.maxscale_thresh,
type_=float,
),
ibs.cfg.query_cfg.nnweight_cfg.lookup_paraminfo('can_match_samename'),
# ut.ParamInfo('normalizer_rule', ibs.cfg.query_cfg.nnweight_cfg.normalizer_rule),
# ut.ParamInfo('AI', True),
]
self.qcfg = TmpAnnotConfig()
self.dcfg = TmpAnnotConfig()
self.pcfg = TmpPipelineConfig()
self.review_cfg = dtool.Config.from_dict(
{'filter_reviewed': True, 'ranks_top': 1, 'filter_true_matches': True,}
)
self.info_cfg = dtool.Config.from_dict(
{key: False for key in ibs.parse_annot_config_stats_filter_kws()}
)
self.exemplar_cfg = dtool.Config.from_dict(
{
#'imgsetid': None,
'exemplars_per_view': ibs.cfg.other_cfg.exemplars_per_view,
}
)
self.info_cfg['species_hist'] = True
self.info_cfg['per_vp'] = True
self.info_cfg['per_qual'] = True
self.info_cfg['hashid'] = True
self.info_cfg['per_name'] = True
self.info_cfg['hashid_visual'] = True
self.info_cfg['hashid_uuid'] = True
self.info_cfg['per_multiple'] = True
for cfg in [self.qcfg, self.dcfg]:
cfg['minqual'] = 'good'
cfg['reviewed'] = True
cfg['multiple'] = False
# cfg['min_pername'] = 0
# from wbia.other import ibsfuncs
cfg['species'] = self.ibs.get_primary_database_species()
cfg['require_viewpoint'] = True
cfg['view'] = ibsfuncs.get_primary_species_viewpoint(cfg['species'])
#'right,frontright,backright'
self.setWindowTitle('Custom Annot Selector')
# cfg_size_policy = (QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
def new_confg_widget(cfg, changed=None):
user_mode = 0
cfg_widget = PrefWidget2.EditConfigWidget(
config=cfg, user_mode=user_mode, parent=self, changed=changed
)
# cfg_widget.setSizePolicy(*cfg_size_policy)
return cfg_widget
self.editQueryConfig = new_confg_widget(self.qcfg, changed=self.on_cfg_changed)
self.editDataConfig = new_confg_widget(self.dcfg, changed=self.on_cfg_changed)
self.editPipeConfig = new_confg_widget(self.pcfg, changed=self.on_cfg_changed)
self.editReviewConfig = new_confg_widget(self.review_cfg)
self.editInfoConfig = new_confg_widget(self.info_cfg)
# self.editExemplarConfig = new_confg_widget(self.exemplar_cfg, changed=self.on_cfg_changed)
tabwgt = self.addNewTabWidget(verticalStretch=1)
tab1 = tabwgt.addNewTab('Custom Query')
tab2 = tabwgt.addNewTab('Saved Queries')
table = self.saved_queries = QtWidgets.QTableWidget()
table.doubleClicked.connect(self.on_table_doubleclick)
tab2.addWidget(self.saved_queries)
splitter = tab1.addNewSplitter(orientation=Qt.Vertical)
acfg_hframe = splitter.newWidget(orientation=Qt.Horizontal)
query_vframe = acfg_hframe.addNewVWidget()
query_vframe.addWidget(QtWidgets.QLabel('Query Config'))
query_vframe.addWidget(self.editQueryConfig)
data_vframe = acfg_hframe.addNewVWidget()
data_vframe.addWidget(QtWidgets.QLabel('Data Config'))
data_vframe.addWidget(self.editDataConfig)
# data_vframe.setVisible(False)
info_vframe = acfg_hframe.addNewVWidget()
info_vframe.addNewLabel('Exemplar Config')
self.editExemplarConfig = info_vframe.addNewEditConfigWidget(
config=self.exemplar_cfg, changed=self.on_cfg_changed
)
info_vframe.addNewButton('Set Exemplars', pressed=self.set_exemplars)
pcfg_hframe = splitter.newWidget(orientation=Qt.Horizontal)
pipe_vframe = pcfg_hframe.addNewVWidget()
pipe_vframe.addNewLabel('Pipeline Config')
pipe_vframe.addWidget(self.editPipeConfig)
review_vframe = pcfg_hframe.addNewVWidget()
review_vframe.addNewLabel('Review Config')
review_vframe.addWidget(self.editReviewConfig)
info_vframe = pcfg_hframe.addNewVWidget()
info_vframe.addNewLabel('Info Config')
info_vframe.addWidget(self.editInfoConfig)
# stats_vwidget = splitter.newWidget(orientation=Qt.Vertical)
stats_vwidget = splitter.newWidget(orientation=Qt.Vertical, verticalStretch=1)
stats_vwidget.addNewLabel('Expanded Annot Info (Info Config Changes Display)')
self.qstats = QtWidgets.QTextEdit()
self.qstats.setReadOnly(True)
self.qstats.setWordWrapMode(QtGui.QTextOption.WrapAnywhere)
stats_vwidget.addWidget(self.qstats)
# Hack a copy for tab2
self.qstats2 = QtWidgets.QTextEdit()
self.qstats2.setReadOnly(True)
self.qstats2.setWordWrapMode(QtGui.QTextOption.WrapAnywhere)
tab2.addWidget(self.qstats2)
# self.layout().addWidget(self.qstats)
button_bar = self.addNewHWidget()
self.update_button = button_bar.addNewButton(
'Apply New Config', pressed=self.apply_new_config
)
self.execute_button = button_bar.addNewButton(
'Execute New Query', pressed=self.execute_query
)
self.load_bc_button = button_bar.addNewButton(
'Load Cached Query', pressed=self.load_bc
)
# button_bar.addNewButton('TestLog', pressed=self.log_query)
button_bar.addNewButton('Embed', pressed=self.embed)
# testlog = QtWidgets.QPushButton('TestLog')
# testlog.pressed.connect(self.log_query)
# button_bar.addWidget(testlog)
self.prog_bar = self.addNewProgressBar(visible=False)
gt.fix_child_attr_heirarchy(self, 'setSpacing', 0)
gt.fix_child_attr_heirarchy(self, 'setMargin', 2)
self.cfg_needs_update = True
self.bc_info = None
self.load_bc_button.setEnabled(self.bc_info is not None)
self.load_bc_button.setDisabled(self.bc_info is None)
# layout.addWidget(self.update_button, 3, 2, 1, 1)
# layout.addWidget(self.editQueryConfig)
# layout.addWidget(self.editDataConfig)
self.populate_table()
[docs] def set_exemplars(self):
print('set exemplars')
ibs = self.ibs
print('self.exemplar_cfg = %r' % (self.exemplar_cfg,))
with gt.GuiProgContext('Querying', self.prog_bar) as ctx: # NOQA
ibs.set_exemplars_from_quality_and_viewpoint(
prog_hook=ctx.prog_hook, **self.exemplar_cfg
)
[docs] def onstart(self):
if self.saved_queries.rowCount() > 0:
self.load_previous_query(self.saved_queries.rowCount() - 1)
[docs] def sizeHint(self):
return QtCore.QSize(900, 960)
[docs] def embed(self):
import utool
utool.embed()
[docs] def make_commandline_str(self):
from wbia.expt import experiment_helpers
from wbia.expt import annotation_configs
cfgdict_list, pipecfg_list = experiment_helpers.get_pipecfg_list(
['default:'], ibs=self.ibs
)
default_pcfg = dict(pipecfg_list[0].parse_items())
# Hackish ways to get cmdline string
p = ut.get_cfg_lbl(self.pcfg.asdict(), name='default', default_cfg=default_pcfg)
a = (
'default'
+ annotation_configs.get_varied_acfg_labels(
[self.acfg, annotation_configs.default]
)[0]
)
dbdir = self.ibs.get_dbdir()
wbia_part = ['python', '-m', 'wbia']
data_part = ['--dbdir', dbdir, '-a', a, '-t', p]
cmd_parts = wbia_part + ['draw_rank_cmc'] + data_part + ['--show']
cmdstr = ' '.join(cmd_parts)
return cmdstr
[docs] def populate_table(self):
# data = {'col1': ['1','2','3'], 'col2':['4','5','6'], 'col3':['7','8','9']}
print('Updating saved query table')
from wbia.guitool.__PYQT__.QtCore import Qt
self.table_data = self.get_saved_queries()
horHeaders = ['fname', 'num_qaids', 'num_daids', 'has_bc']
data = self.table_data
table = self.saved_queries
self.saved_queries.setColumnCount(len(horHeaders))
print('Populating table')
for n, key in ut.ProgIter(enumerate(horHeaders), lbl='pop table'):
if n == 0:
self.saved_queries.setRowCount(len(data[key]))
for m, item in enumerate(data[key]):
newitem = QtWidgets.QTableWidgetItem(str(item))
table.setItem(m, n, newitem)
newitem.setFlags(newitem.flags() ^ Qt.ItemIsEditable)
table.setHorizontalHeaderLabels(horHeaders)
table.resizeColumnsToContents()
table.resizeRowsToContents()
print('Finished populating table')
[docs] def on_cfg_changed(self):
self.cfg_needs_update = True
# print('detected change')
@property
def acfg(self):
acfg = {'qcfg': self.qcfg.asdict(), 'dcfg': self.dcfg.asdict()}
return acfg
[docs] def expt_query_dir(self):
# dbdir = self.ibs.get_dbdir()
dbdir = self.ibs.get_dbdir()
expt_dir = ut.ensuredir(ut.unixjoin(dbdir, 'SPECIAL_GGR_EXPT_LOGS'))
expt_query_dir = ut.ensuredir(ut.unixjoin(expt_dir, 'saved_queries'))
return expt_query_dir
[docs] def apply_new_config(self):
print('apply_new_config')
with gt.GuiProgContext('Updating', self.prog_bar) as ctx: # NOQA
ctx.set_total(5)
ibs = self.ibs
# Discard the loaded query info
self.query_info = None
ctx.set_progress(1)
self.qaids = ibs.sample_annots_general(**self.qcfg)
ctx.set_progress(2)
self.daids = ibs.sample_annots_general(**self.dcfg)
ctx.set_progress(3)
ctx.set_progress(4)
self.update_config_info()
self.execute_button.setText('Execute New Query')
ctx.set_progress(5)
print('apply_new_config is done')
self.cfg_needs_update = False
self.bc_info = None
self.load_bc_button.setEnabled(self.bc_info is not None)
self.load_bc_button.setDisabled(self.bc_info is None)
[docs] def update_config_info(self, extra=None):
ibs = self.ibs
from wbia.algo.Config import QueryConfig
# use defaults instead of back's ibs.cfg
query_cfg = QueryConfig()
self.qreq_ = self.ibs.new_query_request(
self.qaids, self.daids, cfgdict=self.pcfg, query_cfg=query_cfg
)
qreq_ = self.qreq_
stats_dict = ibs.get_annotconfig_stats(qreq_.qaids, qreq_.daids, **self.info_cfg)
cmdstr = self.make_commandline_str()
stat_parts = ut.filter_Nones(
[
cmdstr,
extra,
'pipe_cfgstr=%s' % (self.qreq_.get_cfgstr(with_data=False)),
ut.repr2(stats_dict, strvals=True),
]
)
stats_str = '\n'.join(stat_parts)
print(stats_str)
self.qstats.setPlainText(stats_str)
self.qstats2.setPlainText(stats_str)
pass
@property
def expanded_aids(self):
return (self.qaids, self.daids)
[docs] def get_saved_queries(self):
print('Reading saved queries')
expt_query_dir = self.expt_query_dir()
prev_queries = ut.glob(expt_query_dir, 'long_*.json')
data = ut.ddict(list)
from os.path import basename
for long_fpath in sorted(prev_queries):
short_fpath = long_fpath.replace('long', 'short')
data['long_path'].append(long_fpath)
data['short_path'].append(short_fpath)
data['fname'].append(basename(long_fpath))
short_info = ut.load_json(short_fpath)
# Fix old formats
# if 'expanded_uuids' in short_info:
# quuids, duuids = short_info['expanded_uuids']
# short_info['num_qaids'] = len(quuids)
# short_info['num_daids'] = len(duuids)
# del short_info['expanded_uuids']
# if 'expanded_aids' in short_info:
# del short_info['expanded_aids']
# ut.save_json(short_fpath, short_info, pretty=True)
# if 'num_daids' not in short_info:
# long_info = ut.load_json(long_fpath)
# quuids, duuids = long_info['expanded_uuids']
# short_info['num_qaids'] = len(quuids)
# short_info['num_daids'] = len(duuids)
# ut.save_json(short_fpath, short_info, pretty=True)
data['num_qaids'].append(short_info.get('num_qaids', '?'))
data['num_daids'].append(short_info.get('num_daids', '?'))
data['has_bc'].append('bc_info' in short_info)
print('Finished reading saved queries')
return data
[docs] def load_previous_query(self, row):
print('loading previous query')
print('apply_new_config')
with gt.GuiProgContext('Loading', self.prog_bar) as ctx: # NOQA
ctx.set_total(5)
ctx.set_progress(0)
long_fpath = self.table_data['long_path'][row]
short_fpath = self.table_data['short_path'][row]
query_info = ut.load_json(long_fpath)
query_info_short_text = ut.load_text(short_fpath)
ctx.set_progress(1)
self.bc_info = query_info.get('bc_info')
self.load_bc_button.setEnabled(self.bc_info is not None)
self.load_bc_button.setDisabled(self.bc_info is None)
self.query_info = query_info
quuids, duuids = query_info['expanded_uuids']
ibs = self.ibs
self.qaids = ibs.get_annot_aids_from_uuid(quuids)
ctx.set_progress(2)
self.daids = ibs.get_annot_aids_from_uuid(duuids)
ctx.set_progress(3)
self.editPipeConfig.set_to_external(self.query_info['pcfg'])
self.editQueryConfig.set_to_external(self.query_info['acfg']['qcfg'])
self.editDataConfig.set_to_external(self.query_info['acfg']['dcfg'])
# self.pcfg.update(**)
# TODO: Update acfg as well.
self.update_config_info('SAVED MANIFEST INFO:' + query_info_short_text)
print('...loaded previous query')
self.execute_button.setText('Re-Execute Saved Query')
# need to do this or block signals from editPipeConfig
self.cfg_needs_update = False
ctx.set_progress(5)
[docs] def log_query(self, qreq_=None, test=True):
""" DEPRICATE """
expt_query_dir = self.expt_query_dir()
# ut.vd(expt_query_dir)
ibs = self.ibs
# TODO: Save the BIGCACHE file to the log, this allows
# us to re-load that query even if its slightly invalid
if qreq_ is None and test:
from wbia.algo.Config import QueryConfig
query_cfg = QueryConfig()
qreq_ = self.ibs.new_query_request(
self.qaids, self.daids, cfgdict=self.pcfg, query_cfg=query_cfg
)
ts = ut.get_timestamp(isutc=True, timezone=True)
expt_long_fpath = ut.unixjoin(
expt_query_dir, 'long_expt_%s_%s.json' % (self.ibs.dbname, ts)
)
expt_short_fpath = ut.unixjoin(
expt_query_dir, 'short_expt_%s_%s.json' % (self.ibs.dbname, ts)
)
bc_info = qreq_.get_bigcache_info()
query_info = ut.odict(
[
('computer', ut.get_computer_name()),
('timestamp', ts),
('num_qaids', len(self.qaids)),
('num_daids', len(self.daids)),
('pcfg', self.pcfg.asdict()),
('acfg', self.acfg),
('review_cfg', self.review_cfg.asdict()),
('expanded_aids', (self.qaids, self.daids)),
(
'expanded_uuids',
(ibs.get_annot_uuids(self.qaids), ibs.get_annot_uuids(self.daids)),
),
('qreq_cfgstr', qreq_.get_cfgstr(with_input=True)),
('qparams', qreq_.qparams),
(
'qvuuid_hash',
qreq_.ibs.get_annot_hashid_visual_uuid(self.qaids, prefix='Q'),
),
(
'dvuuid_hash',
qreq_.ibs.get_annot_hashid_visual_uuid(self.daids, prefix='D'),
),
('bc_info', bc_info),
]
)
if test:
del query_info['expanded_aids']
del query_info['expanded_uuids']
del query_info['qparams']
print('expt_long_fpath = %r' % (expt_long_fpath,))
print('expt_short_fpath = %r' % (expt_short_fpath,))
print('query_info = %s' % (ut.to_json(query_info, pretty=1),))
else:
ut.save_json(expt_long_fpath, query_info)
del query_info['expanded_uuids']
del query_info['expanded_aids']
del query_info['qparams']
ut.save_json(expt_short_fpath, query_info, pretty=1)
[docs] @backreport
@slot_()
def execute_query(self):
print('accept')
# assert not self.cfg_needs_update, 'NEED TO APPLY ACFG/PCFG BEFORE EXECUTING'
if self.cfg_needs_update:
options = ['Apply now and continue', 'Apply now and wait']
reply = gt.user_option(
msg=ut.codeblock(
'''
Information display is out of date. You should apply the
modified configuration before you continue.
'''
),
options=options,
)
if reply == options[0]:
self.apply_new_config()
elif reply == options[1]:
self.apply_new_config()
raise guiexcept.UserCancel()
else:
raise guiexcept.UserCancel()
self.accept_flag = True
from wbia.gui import inspect_gui
review_cfg = self.review_cfg.asdict().copy()
ibs = self.ibs
qreq_ = self.qreq_
if self.query_info is None:
# Dont log on a re-executed query
self.log_query(qreq_, test=False)
with gt.GuiProgContext('Querying', self.prog_bar) as ctx: # NOQA
cm_list = qreq_.execute(prog_hook=ctx.prog_hook)
qres_wgt = inspect_gui.QueryResultsWidget(
ibs, cm_list, qreq_=qreq_, review_cfg=review_cfg
)
self.qres_wgt = qres_wgt
qres_wgt.show()
qres_wgt.raise_()
print('Showing query results')
if self.query_info is None:
self.populate_table()
[docs] def load_bc(self):
from wbia.gui import inspect_gui
review_cfg = self.review_cfg.asdict().copy()
bc_dpath, bc_fname, bc_cfgstr = self.bc_info
qaid2_cm = ut.load_cache(bc_dpath, bc_fname, bc_cfgstr)
qreq_ = self.qreq_
ibs = self.ibs
cm_list = [qaid2_cm[qaid] for qaid in qreq_.qaids]
qres_wgt = inspect_gui.QueryResultsWidget(
ibs, cm_list, qreq_=qreq_, review_cfg=review_cfg
)
self.qres_wgt = qres_wgt
qres_wgt.show()
qres_wgt.raise_()
[docs] def on_table_doubleclick(self, index):
# print('index = %r' % (index,))
row = index.row()
self.load_previous_query(row)
# ------------------------
# Backend MainWindow Class
# ------------------------
# QtReloadingMetaClass = ut.reloading_meta_metaclass_factory(gt.QtCore.pyqtWrapperType)
GUIBACK_BASE = QtCore.QObject
# @six.add_metaclass(QtReloadingMetaClass) # cant do this quit yet
[docs]class MainWindowBackend(GUIBACK_BASE):
"""
Sends and recieves signals to and from the frontend
Args:
ibs (wbia.IBEISController): image analysis api(default = None)
CommandLine:
python -m wbia.gui.guiback MainWindowBackend --show
Example:
>>> # DISABLE_DOCTEST
>>> from wbia.gui.guiback import * # NOQA
>>> import wbia
>>> back = testdata_guiback(defaultdb=None)
>>> ut.quit_if_noshow()
>>> ut.quit_if_noshow()
>>> gt.qtapp_loop(qwin=back.front, freq=10)
"""
# Backend Signals
updateWindowTitleSignal = signal_(str)
# changeSpeciesSignal = signal_(str)
# incQuerySignal = signal_(int)
# ------------------------
# Constructor
# ------------------------
def __init__(back, ibs=None):
""" Creates GUIBackend object """
# GUIBACK_BASE.__init__(back)
super(MainWindowBackend, back).__init__()
if ut.VERBOSE:
print('[back] MainWindowBackend.__init__(ibs=%r)' % (ibs,))
back.ibs = None
back.cfg = None
back.edit_prefs_wgt = None
# State variables
back.sel_aids = []
back.sel_nids = []
back.sel_gids = []
back.sel_cm = []
# if ut.is_developer():
back.daids_mode = None
# else:
# back.daids_mode = const.VS_EXEMPLARS_KEY
# back.imageset_query_results = ut.ddict(dict)
# used to store partials defined in the frontend
back.special_query_funcs = {}
# Create GUIFrontend object
back.mainwin = newgui.IBEISMainWindow(back=back, ibs=ibs)
back.front = back.mainwin.ibswgt
back.web_ibs = None
back.wb_server_running = None
back.ibswgt = back.front # Alias
# connect signals and other objects
fig_presenter.register_qt4_win(back.mainwin)
# register self with the wbia controller
back.register_self()
# back.changeSpeciesSignal.connect(back.ibswgt.species_combo.setItemText)
# back.incQuerySignal.connect(back.incremental_query_slot)
# def __del__(back):
# back.cleanup()
[docs] def set_daids_mode(back, new_mode):
if new_mode == 'toggle':
if back.daids_mode == const.VS_EXEMPLARS_KEY:
back.daids_mode = const.INTRA_OCCUR_KEY
else:
back.daids_mode = const.VS_EXEMPLARS_KEY
else:
back.daids_mode = new_mode
try:
back.mainwin.actionToggleQueryMode.setText(
'Toggle Query Mode currently: %s' % back.daids_mode
)
except Exception as ex:
ut.printex(ex)
# back.front.menuActions.
[docs] def cleanup(back):
back.kill_web_server_parallel()
if back.wb_server_running:
back.shutdown_wildbook()
try:
if back.ibs is not None:
back.ibs.remove_observer(back)
except Exception as ex:
ut.printex(ex, '[back] observer fail')
# @ut.indent_func
[docs] def notify(back):
""" Observer's notify function. """
back.refresh_state()
# @ut.indent_func
[docs] def notify_controller_killed(back):
""" Observer's notify function that the wbia controller has been killed. """
back.ibs = None
[docs] def register_self(back):
if back.ibs is not None:
back.ibs.register_observer(back)
# ------------------------
# Draw Functions
# ------------------------
[docs] def show(back):
back.mainwin.show()
[docs] def get_background_web_domain(back, **kwargs):
web_url = '127.0.0.1'
web_port = back.ibs.get_web_port_via_scan()
if web_port is None:
raise ValueError('IA web server is not running on any expected port')
web_domain = '%s:%s' % (web_url, web_port,)
return web_domain
[docs] def show_imgsetid_list_in_web(back, imgsetid_list, **kwargs):
import webbrowser
back.start_web_server_parallel(browser=False)
imgsetid_list = ut.ensure_iterable(imgsetid_list)
imgsetid_str = ','.join(map(str, imgsetid_list))
web_domain = back.get_background_web_domain()
url = 'http://%s/view/images/?imgsetid=%s' % (web_domain, imgsetid_str,)
webbrowser.open(url)
[docs] def show_imgsetid_detection_turk_in_web(back, imgsetid_list, **kwargs):
import webbrowser
back.start_web_server_parallel(browser=False)
imgsetid_list = ut.ensure_iterable(imgsetid_list)
imgsetid_str = ','.join(map(str, imgsetid_list))
web_domain = back.get_background_web_domain()
url = 'http://%s/turk/detection/?imgsetid=%s' % (web_domain, imgsetid_str,)
webbrowser.open(url)
[docs] def show_imgsetid_annotation_turk_in_web(back, imgsetid_list, **kwargs):
import webbrowser
back.start_web_server_parallel(browser=False)
imgsetid_list = ut.ensure_iterable(imgsetid_list)
imgsetid_str = ','.join(map(str, imgsetid_list))
web_domain = back.get_background_web_domain()
url = 'http://%s/turk/annotation/?imgsetid=%s' % (web_domain, imgsetid_str,)
webbrowser.open(url)
[docs] def show_image(back, gid, sel_aids=[], web=False, **kwargs):
if web:
back.show_images_in_web(gid)
else:
kwargs.update(
{'sel_aids': sel_aids, 'select_callback': back.select_gid,}
)
interact.ishow_image(back.ibs, gid, **kwargs)
[docs] def show_images_in_web(back, gid_list, **kwargs):
import webbrowser
back.start_web_server_parallel(browser=False)
gid_list = ut.ensure_iterable(gid_list)
gid_text = ','.join(map(str, gid_list))
web_domain = back.get_background_web_domain()
if len(gid_list) == 1:
url = 'http://%s/view/detection?gid=%s' % (web_domain, gid_text,)
else:
url = 'http://%s/view/images?gid=%s' % (web_domain, gid_text,)
webbrowser.open(url)
[docs] def show_annotation(back, aid, show_image=False, web=False, **kwargs):
if web:
import webbrowser
back.start_web_server_parallel(browser=False)
web_domain = back.get_background_web_domain()
url = 'http://%s/view/annotations?aid=%s' % (web_domain, aid,)
webbrowser.open(url)
else:
interact.ishow_chip(back.ibs, aid, **kwargs)
if show_image:
gid = back.ibs.get_annot_gids(aid)
# interact.ishow_image(back.ibs, gid, sel_aids=[aid])
back.show_image(gid, sel_aids=[aid], web=web, **kwargs)
[docs] def show_aid_list_in_web(back, aid_list, **kwargs):
import webbrowser
back.start_web_server_parallel(browser=False)
if not isinstance(aid_list, (tuple, list)):
aid_list = [aid_list]
if len(aid_list) > 0:
aid_list = ','.join(map(str, aid_list))
else:
aid_list = ''
web_domain = back.get_background_web_domain()
url = 'http://%s/view/annotations?aid=%s' % (web_domain, aid_list,)
webbrowser.open(url)
[docs] def show_name(back, nid, sel_aids=[], **kwargs):
kwargs.update(
{'sel_aids': sel_aids, 'select_aid_callback': back.select_aid,}
)
# nid = back.ibs.get_name_rowids_from_text(name)
interact.ishow_name(back.ibs, nid, **kwargs)
pass
[docs] def show_nid_list_in_web(back, nid_list, **kwargs):
import webbrowser
back.start_web_server_parallel(browser=False)
if not isinstance(nid_list, (tuple, list)):
nid_list = [nid_list]
aids_list = back.ibs.get_name_aids(nid_list)
aid_list = []
for aids in aids_list:
if len(aids) > 0:
aid_list.append(aids[0])
if len(aid_list) > 0:
aid_str = ','.join(map(str, aid_list))
else:
aid_str = ''
web_domain = back.get_background_web_domain()
url = 'http://%s/view/names?aid=%s' % (web_domain, aid_str,)
webbrowser.open(url)
[docs] def show_hough_image_(back, gid, **kwargs):
species = back.get_selected_species()
viz.show_hough_image(back.ibs, gid, species=species, **kwargs)
viz.draw()
[docs] def run_detection_on_imageset(back, imgsetid_list, refresh=True, **kwargs):
gid_list = ut.flatten(back.ibs.get_imageset_gids(imgsetid_list))
back.run_detection_on_images(gid_list, refresh=refresh, **kwargs)
[docs] def run_detection_on_images(back, gid_list, refresh=True, **kwargs):
ibs = back.ibs
detector = back.ibs.cfg.detect_cfg.detector
if detector in ['cnn_yolo', 'yolo', 'cnn']:
ibs.detect_cnn_yolo(gid_list)
elif detector in ['random_forest', 'rf']:
species = back.ibs.cfg.detect_cfg.species_text
ibs.detect_random_forest(gid_list, species)
else:
raise ValueError('Detector not recognized')
if refresh:
back.front.update_tables([gh.IMAGE_TABLE])
[docs] def show_probability_chip(back, aid, **kwargs):
viz.show_probability_chip(back.ibs, aid, **kwargs)
viz.draw()
[docs] @blocking_slot()
def review_queries(back, cm_list, qreq_=None, review_cfg={}, query_title=''):
# Qt QueryResults Interaction
from wbia.gui import inspect_gui
ibs = back.ibs
def finished_review_callback():
try:
# TODO: only call this if connected to wildbook
# TODO: probably need to remove verboseity as well
if back.wb_server_running:
back.ibs.wildbook_signal_annot_name_changes()
except Exception as ex:
ut.printex(ex, 'Wildbook call did not work. Maybe not connected?')
back.front.update_tables()
# Overwrite
review_cfg = review_cfg.copy()
filter_reviewed = review_cfg.pop('filter_reviewed', None)
if filter_reviewed is None:
filter_reviewed = ibs.cfg.other_cfg.ensure_attr('filter_reviewed', True)
if filter_reviewed is None:
# only filter big queries if not specified
filter_reviewed = len(cm_list) > 6
print('EVIDENCE_DECISION QUERIES')
print('review_cfg = %s' % (ut.repr3(review_cfg),))
print('filter_reviewed = %s' % (filter_reviewed,))
review_cfg['filter_reviewed'] = filter_reviewed
review_cfg['ranks_top'] = review_cfg.get('ranks_top', ibs.cfg.other_cfg.ranks_top)
back.qres_wgt = inspect_gui.QueryResultsWidget(
ibs,
cm_list,
callback=finished_review_callback,
qreq_=qreq_,
query_title=query_title,
review_cfg=review_cfg,
)
back.qres_wgt.show()
back.qres_wgt.raise_()
# ----------------------
# State Management Functions
# ----------------------
[docs] def refresh_state(back):
""" Blanket refresh function. Try not to call this """
back.front.update_tables()
back.ibswgt.update_species_available(reselect=True)
[docs] def connect_wbia_control(back, ibs):
if ut.VERBOSE:
print('[back] connect_wbia(ibs=%r)' % (ibs,))
if ibs is None:
return None
back.ibs = ibs
# register self with the wbia controller
back.register_self()
# deselect
back._set_selection(sel_gids=[], sel_aids=[], sel_nids=[], sel_imgsetids=[None])
back.front.connect_wbia_control(ibs)
exemplar_gsid = ibs.get_imageset_imgsetids_from_text(const.EXEMPLAR_IMAGESETTEXT)
num_exemplars = len(ibsfuncs._get_gids_in_imgsetid(ibs, exemplar_gsid))
if num_exemplars == 0:
back.daids_mode = const.INTRA_OCCUR_KEY
else:
back.daids_mode = const.VS_EXEMPLARS_KEY
back.set_daids_mode(back.daids_mode)
[docs] @blocking_slot()
def default_config(back):
""" Button Click -> Preferences Defaults """
print('[back] default preferences')
back.ibs._default_config()
back.edit_prefs_wgt.refresh_layout()
back.edit_prefs_wgt.pref_model.rootPref.save()
# due to weirdness of Preferences structs
# we have to close the widget otherwise we will
# be looking at an outated object
back.edit_prefs_wgt.close()
[docs] @ut.indent_func
def get_selected_gid(back):
""" selected image id """
if len(back.sel_gids) == 0:
if len(back.sel_aids) == 0:
sel_gids = back.ibs.get_annot_gids(back.sel_aids)
if len(sel_gids) == 0:
raise guiexcept.InvalidRequest('There are no selected images')
gid = sel_gids[0]
return gid
raise guiexcept.InvalidRequest('There are no selected images')
gid = back.sel_gids[0]
return gid
[docs] @ut.indent_func
def get_selected_aids(back):
""" selected annotation id """
if len(back.sel_aids) == 0:
raise guiexcept.InvalidRequest('There are no selected ANNOTATIONs')
# aid = back.sel_aids[0]
return back.sel_aids
[docs] @ut.indent_func
def get_selected_imgsetid(back):
""" selected imageset id """
if len(back.sel_imgsetids) == 0:
raise guiexcept.InvalidRequest('There are no selected ImageSets')
imgsetid = back.sel_imgsetids[0]
return imgsetid
# --------------------------------------------------------------------------
# Selection Functions
# --------------------------------------------------------------------------
def _set_selection2(back, tablename, id_list, mode='set'):
# here tablename is a backend const tablename
def set_collections(old, aug):
return ut.ensure_iterable(aug)
def add_collections(old, aug):
return list(set(old) | set(ut.ensure_iterable(aug)))
def diff_collections(old, aug):
return list(set(old) - set(ut.ensure_iterable(aug)))
modify_collections = {
'set': set_collections,
'add': add_collections,
'diff': diff_collections,
}[mode]
attr_map = {
const.ANNOTATION_TABLE: 'sel_aids',
const.IMAGE_TABLE: 'sel_gids',
const.NAME_TABLE: 'sel_nids',
}
attr = attr_map[tablename]
new_id_list = modify_collections(getattr(back, attr), id_list)
setattr(back, attr, new_id_list)
def _set_selection3(back, tablename, id_list, mode='set'):
"""
text = '51e10019-968b-5f2e-2287-8432464d7547 '
"""
def ensure_uuids_are_ids(id_list, uuid_to_id_fn):
import uuid
if len(id_list) > 0 and isinstance(id_list[0], uuid.UUID):
id_list = uuid_to_id_fn(id_list)
return id_list
def ensure_texts_are_ids(id_list, text_to_id_fn):
if len(id_list) > 0 and isinstance(id_list[0], six.string_types):
id_list = text_to_id_fn(id_list)
return id_list
if tablename == const.ANNOTATION_TABLE:
id_list = ensure_uuids_are_ids(
id_list, back.ibs.get_annot_aids_from_visual_uuid
)
aid_list = ut.ensure_iterable(id_list)
nid_list = back.ibs.get_annot_nids(aid_list)
gid_list = back.ibs.get_annot_gids(aid_list)
flag_list = ut.flag_None_items(gid_list)
nid_list = ut.filterfalse_items(nid_list, flag_list)
gid_list = ut.filterfalse_items(gid_list, flag_list)
aid_list = ut.filterfalse_items(aid_list, flag_list)
elif tablename == const.IMAGE_TABLE:
id_list = ensure_uuids_are_ids(id_list, back.ibs.get_image_gids_from_uuid)
gid_list = ut.ensure_iterable(id_list)
aid_list = ut.flatten(back.ibs.get_image_aids(gid_list))
nid_list = back.ibs.get_annot_nids(aid_list)
flag_list = ut.flag_None_items(nid_list)
aid_list = ut.filterfalse_items(aid_list, flag_list)
aid_list = ut.filterfalse_items(aid_list, flag_list)
elif tablename == const.NAME_TABLE:
id_list = ensure_texts_are_ids(id_list, back.ibs.get_name_rowids_from_text_)
nid_list = ut.ensure_iterable(id_list)
aid_list = ut.flatten(back.ibs.get_name_aids(nid_list))
gid_list = back.ibs.get_annot_gids(aid_list)
flag_list = ut.flag_None_items(gid_list)
aid_list = ut.filterfalse_items(aid_list, flag_list)
gid_list = ut.filterfalse_items(gid_list, flag_list)
back._set_selection2(const.ANNOTATION_TABLE, aid_list, mode)
back._set_selection2(const.NAME_TABLE, nid_list, mode)
back._set_selection2(const.IMAGE_TABLE, gid_list, mode)
return id_list
def _clear_selection(back):
print('[back] _clear_selection')
back.sel_aids = []
back.sel_gids = []
back.sel_nids = []
[docs] def update_selection_texts(back):
if back.ibs is None:
return
sel_imagesettexts = back.ibs.get_imageset_text(back.sel_imgsetids)
if sel_imagesettexts == [None]:
sel_imagesettexts = []
else:
sel_imagesettexts = map(str, sel_imagesettexts)
back.ibswgt.set_selection_status(gh.IMAGESET_TABLE, sel_imagesettexts)
back.ibswgt.set_selection_status(gh.IMAGE_TABLE, back.sel_gids)
back.ibswgt.set_selection_status(gh.ANNOTATION_TABLE, back.sel_aids)
back.ibswgt.set_selection_status(gh.NAMES_TREE, back.sel_nids)
def _set_selection(
back,
sel_gids=None,
sel_aids=None,
sel_nids=None,
sel_cm=None,
sel_imgsetids=None,
mode='set',
**kwargs
):
def modify_collection_attr(self, attr, aug, mode):
aug = ut.ensure_iterable(aug)
old = getattr(self, attr)
if mode == 'set':
new = aug
elif mode == 'add':
new = list(set(old) + set(aug))
elif mode == 'remove':
new = list(set(old) - set(aug))
else:
raise AssertionError('uknown mode=%r' % (mode,))
setattr(self, attr, new)
if sel_imgsetids is not None:
sel_imgsetids = ut.ensure_iterable(sel_imgsetids)
back.sel_imgsetids = sel_imgsetids
sel_imagesettexts = back.ibs.get_imageset_text(back.sel_imgsetids)
if sel_imagesettexts == [None]:
sel_imagesettexts = []
else:
sel_imagesettexts = map(str, sel_imagesettexts)
back.ibswgt.set_selection_status(gh.IMAGESET_TABLE, sel_imagesettexts)
if sel_gids is not None:
modify_collection_attr(back, 'sel_gids', sel_gids, mode)
back.ibswgt.set_selection_status(gh.IMAGE_TABLE, back.sel_gids)
if sel_aids is not None:
sel_aids = ut.ensure_iterable(sel_aids)
back.sel_aids = sel_aids
back.ibswgt.set_selection_status(gh.ANNOTATION_TABLE, back.sel_aids)
if sel_nids is not None:
sel_nids = ut.ensure_iterable(sel_nids)
back.sel_nids = sel_nids
back.ibswgt.set_selection_status(gh.NAMES_TREE, back.sel_nids)
if sel_cm is not None:
raise NotImplementedError('no select cm implemented')
back.sel_sel_qres = sel_cm
[docs] def select_imgsetid(back, imgsetid=None, **kwargs):
""" Table Click -> Result Table """
imgsetid = cast_from_qt(imgsetid)
if False:
prefix = ut.get_caller_name(range(1, 8))
else:
prefix = ''
print(prefix + '[back] select imageset imgsetid=%r' % (imgsetid))
back._set_selection(sel_imgsetids=imgsetid, **kwargs)
[docs] def select_gid(
back, gid, imgsetid=None, show=True, sel_aids=None, fnum=None, web=False, **kwargs
):
r"""
Table Click -> Image Table
Example:
>>> # xdoctest: +REQUIRES(--gui)
>>> print('''
>>> get_valid_gids
>>> ''')
>>> valid_gids = ibs.get_valid_gids()
>>> print('''
>>> get_valid_aids
>>> ''')
>>> valid_aids = ibs.get_valid_aids()
>>> #
>>> print('''
>>> * len(valid_aids) = %r
>>> * len(valid_gids) = %r
>>> ''' % (len(valid_aids), len(valid_gids)))
>>> assert len(valid_gids) > 0, 'database images cannot be empty for test'
>>> #
>>> gid = valid_gids[0]
>>> aid_list = ibs.get_image_aids(gid)
>>> aid = aid_list[-1]
>>> back.select_gid(gid, aids=[aid])
"""
# Select the first ANNOTATION in the image if unspecified
if sel_aids is None:
sel_aids = back.ibs.get_image_aids(gid)
if len(sel_aids) > 0:
sel_aids = sel_aids[0:1]
else:
sel_aids = []
print(
'[back] select_gid(gid=%r, imgsetid=%r, sel_aids=%r)'
% (gid, imgsetid, sel_aids)
)
back._set_selection(
sel_gids=gid, sel_aids=sel_aids, sel_imgsetids=imgsetid, **kwargs
)
if show:
back.show_image(gid, sel_aids=sel_aids, fnum=fnum, web=web)
[docs] def copy_species_to_imageset(back, aid, imgsetid=None, refresh=True):
species_rowid = back.ibs.get_annot_species_rowids(aid)
aid_list = back.ibs.get_imageset_aids(imgsetid)
species_rowid_list = [species_rowid] * len(aid_list)
back.ibs.set_annot_species_rowids(aid_list, species_rowid_list)
if refresh:
back.front.update_tables([gh.IMAGE_TABLE, gh.ANNOTATION_TABLE])
[docs] def select_gid_from_aid(back, aid, imgsetid=None, show=True, web=False):
gid = back.ibs.get_annot_gids(aid)
back.select_gid(gid, imgsetid=imgsetid, show=show, web=web, sel_aids=[aid])
[docs] def select_aid(
back, aid, imgsetid=None, show=True, show_annotation=True, web=False, **kwargs
):
""" Table Click -> Chip Table """
print('[back] select aid=%r, imgsetid=%r' % (aid, imgsetid))
gid = back.ibs.get_annot_gids(aid)
nid = back.ibs.get_annot_name_rowids(aid)
back._set_selection(
sel_aids=aid, sel_gids=gid, sel_nids=nid, sel_imgsetids=imgsetid, **kwargs
)
if show and show_annotation:
back.show_annotation(aid, web=web, **kwargs)
[docs] @backblock
def select_nid(back, nid, imgsetid=None, show=True, show_name=True, **kwargs):
""" Table Click -> Name Table """
nid = cast_from_qt(nid)
print('[back] select nid=%r, imgsetid=%r' % (nid, imgsetid))
back._set_selection(sel_nids=nid, sel_imgsetids=imgsetid, **kwargs)
if show and show_name:
back.show_name(nid, **kwargs)
# --------------------------------------------------------------------------
# Action menu slots
# --------------------------------------------------------------------------
[docs] @blocking_slot()
def add_annotation_from_image(back, gid_list, refresh=True):
""" Context -> Add Annotation from Image"""
print('[back] add_annotation_from_image')
assert isinstance(gid_list, list), 'must pass in list here'
size_list = back.ibs.get_image_sizes(gid_list)
bbox_list = [(0, 0, w, h) for (w, h) in size_list]
theta_list = [0.0] * len(gid_list)
aid_list = back.ibs.add_annots(gid_list, bbox_list, theta_list)
if refresh:
back.front.update_tables([gh.IMAGE_TABLE, gh.ANNOTATION_TABLE])
return aid_list
[docs] @blocking_slot()
def delete_image_annotations(back, gid_list):
aid_list = ut.flatten(back.ibs.get_image_aids(gid_list))
back.delete_annot(aid_list)
[docs] @blocking_slot()
def delete_annot(back, aid_list=None):
""" Action -> Delete Annotation
CommandLine:
python -m wbia.gui.guiback --test-delete_annot --show
python -m wbia.gui.guiback --test-delete_annot --show --no-api-cache
python -m wbia.gui.guiback --test-delete_annot --show --assert-api-cache
python -m wbia.gui.guiback --test-delete_annot --show --debug-api-cache --yes
SeeAlso:
manual_annot_funcs.delete_annots
Example:
>>> # xdoctest: +REQUIRES(--gui)
>>> from wbia.gui.guiback import * # NOQA
>>> back = testdata_guiback()
>>> ibs = back.ibs
>>> imgsetid_list = back.ibs.get_valid_imgsetids()
>>> imgsetid = ut.take(imgsetid_list, ut.list_argmax(list(map(len, back.ibs.get_imageset_gids(imgsetid_list)))))
>>> back.front.select_imageset_tab(imgsetid)
>>> gid = back.ibs.get_imageset_gids(imgsetid)[0]
>>> # add a test annotation to delete
>>> aid_list = back.add_annotation_from_image([gid])
>>> # delte annotations
>>> aids1 = back.ibs.get_image_aids(gid)
>>> back.delete_annot(aid_list)
>>> aids2 = back.ibs.get_image_aids(gid)
>>> #assert len(aids2) == len(aids1) - 1
>>> ut.quit_if_noshow()
>>> gt.qtapp_loop(back.mainwin, frequency=100)
"""
print('[back] delete_annot, aid_list = %r' % (aid_list,))
if aid_list is None:
aid_list = back.get_selected_aids()
if not back.are_you_sure(use_msg='Delete %d annotations?' % (len(aid_list))):
return
back._set_selection3(const.ANNOTATION_TABLE, aid_list, mode='diff')
# get the image-id of the annotation we are deleting
# gid_list = back.ibs.get_annot_gids(aid_list)
# delete the annotation
back.ibs.delete_annots(aid_list)
# Select only one image
# try:
# if len(gid_list) > 0:
# gid = gid_list[0]
# except AttributeError:
# gid = gid_list
# back.select_gid(gid, show=False)
# update display, to show image without the deleted annotation
back.front.update_tables()
[docs] @blocking_slot()
def unset_names(back, aid_list):
msg = '[back] unsetting %d names' % (len(aid_list))
print(msg)
if not back.are_you_sure(msg):
return
back.ibs.set_annot_names(aid_list, [const.UNKNOWN] * len(aid_list))
back.front.update_tables()
[docs] @blocking_slot()
def toggle_thumbnails(back):
ibswgt = back.front
tabwgt = ibswgt._tables_tab_widget
index = tabwgt.currentIndex()
tblname = ibswgt.tblname_list[index]
view = ibswgt.views[tblname]
col_name_list = view.col_name_list
if 'thumb' in col_name_list:
idx = col_name_list.index('thumb')
view.col_hidden_list[idx] = not view.col_hidden_list[idx]
view.hide_cols()
# view.resizeRowsToContents() Too slow to use
back.front.update_tables()
# @blocking_slot(int)
# @QtCore.pyqtSlot(int)
[docs] @QtCore.pyqtSlot()
@QtCore.pyqtSlot(int)
def delete_image(back, gid_list=None):
""" Action -> Delete Images"""
print('[back] delete_image, gid_list = %r' % (gid_list,))
if gid_list is None or gid_list is False:
gid_list = [back.get_selected_gid()]
gid_list = ut.ensure_iterable(gid_list)
if not back.are_you_sure(action='delete %d images!' % (len(gid_list))):
return
# FIXME: The api cache seems to break here
back.ibs.delete_images(gid_list)
back.ibs.reset_table_cache()
back.front.update_tables()
[docs] @blocking_slot()
def delete_all_imagesets(back):
print('\n\n[back] delete all imagesets')
if not back.are_you_sure(action='delete ALL imagesets'):
return
back.ibs.delete_all_imagesets()
back.update_special_imagesets_()
back.front.update_tables()
[docs] @blocking_slot()
def update_special_imagesets_(back):
use_more_special_imagesets = back.ibs.cfg.other_cfg.ensure_attr(
'use_more_special_imagesets', False
)
back.ibs.update_special_imagesets(use_more_special_imagesets)
back.front.update_tables([gh.IMAGESET_TABLE])
[docs] @blocking_slot(int)
def delete_imageset_and_images(back, imgsetid_list):
print('\n\n[back] delete_imageset_and_images')
if back.contains_special_imagesets(imgsetid_list):
back.display_special_imagesets_error()
return
if not back.are_you_sure(action='delete this imageset AND ITS IMAGES!'):
return
gid_list = ut.flatten(back.ibs.get_imageset_gids(imgsetid_list))
back.ibs.delete_images(gid_list)
back.ibs.delete_imagesets(imgsetid_list)
back.update_special_imagesets_()
back.front.update_tables()
[docs] @blocking_slot(int)
def mark_imageset_as_shipped(back, imgsetid_list):
print('\n\n[back] mark_imageset_as_shipped')
if back.contains_special_imagesets(imgsetid_list):
back.display_special_imagesets_error()
return
if not back.are_you_sure(action='mark this imageset and shipped to Wildbook'):
return
back.ibs.set_imageset_shipped_flags(imgsetid_list, [1] * len(imgsetid_list))
back.ibs.update_special_imagesets()
back.front.update_tables()
[docs] @blocking_slot(int)
def delete_imageset(back, imgsetid_list):
print('\n\n[back] delete_imageset')
if back.contains_special_imagesets(imgsetid_list):
back.display_special_imagesets_error()
return
if not back.are_you_sure(action='delete %d imagesets' % (len(imgsetid_list))):
return
back.ibs.delete_imagesets(imgsetid_list)
back.update_special_imagesets_()
back.front.update_tables()
[docs] @blocking_slot(int)
def export_imagesets(back, imgsetid_list):
print('\n\n[back] export imageset')
# new_dbname = back.user_input(
# msg='What do you want to name the new database?',
# title='Export to New Database')
# if new_dbname is None or len(new_dbname) == 0:
# print('Abort export to new database. new_dbname=%r' % new_dbname)
# return
back.ibs.export_imagesets(imgsetid_list, new_dbdir=None)
[docs] @blocking_slot()
def train_rf_with_imageset(back, **kwargs):
from wbia.algo.detect import randomforest
imgsetid = back._eidfromkw(kwargs)
if imgsetid < 0:
gid_list = back.ibs.get_valid_gids()
else:
gid_list = back.ibs.get_valid_gids(imgsetid=imgsetid)
species = back.ibs.cfg.detect_cfg.species_text
if species == 'none':
species = None
print(
'[train_rf_with_imageset] Training Random Forest trees with imgsetid=%r and species=%r'
% (imgsetid, species,)
)
randomforest.train_gid_list(back.ibs, gid_list, teardown=False, species=species)
[docs] @blocking_slot(int)
def merge_imagesets(back, imgsetid_list, destination_imgsetid):
assert len(imgsetid_list) > 1, 'Cannot merge fewer than two imagesets'
print('[back] merge_imagesets: %r, %r' % (destination_imgsetid, imgsetid_list))
if back.contains_special_imagesets(imgsetid_list):
back.display_special_imagesets_error()
return
ibs = back.ibs
try:
destination_index = imgsetid_list.index(destination_imgsetid)
except Exception:
# Default to the first value selected if the imgsetid doesn't exist in imgsetid_list
print(
'[back] merge_imagesets cannot find index for %r'
% (destination_imgsetid,)
)
destination_index = 0
destination_imgsetid = imgsetid_list[destination_index]
deprecated_imgsetids = list(imgsetid_list)
deprecated_imgsetids.pop(destination_index)
gid_list = ut.flatten(
[ibs.get_valid_gids(imgsetid=imgsetid) for imgsetid in imgsetid_list]
)
imgsetid_list = [destination_imgsetid] * len(gid_list)
ibs.set_image_imgsetids(gid_list, imgsetid_list)
ibs.delete_imagesets(deprecated_imgsetids)
for imgsetid in deprecated_imgsetids:
back.front.imageset_tabwgt._close_tab_with_imgsetid(imgsetid)
back.front.update_tables([gh.IMAGESET_TABLE], clear_view_selection=True)
[docs] @blocking_slot(int)
def copy_imageset(back, imgsetid_list):
print('[back] copy_imageset: %r' % (imgsetid_list,))
if back.contains_special_imagesets(imgsetid_list):
back.display_special_imagesets_error()
return
ibs = back.ibs
new_imgsetid_list = ibs.copy_imagesets(imgsetid_list)
print('[back] new_imgsetid_list: %r' % (new_imgsetid_list,))
back.front.update_tables([gh.IMAGESET_TABLE], clear_view_selection=True)
[docs] @blocking_slot(list)
def remove_from_imageset(back, gid_list):
imgsetid = back.get_selected_imgsetid()
back.ibs.unrelate_images_and_imagesets(gid_list, [imgsetid] * len(gid_list))
back.update_special_imagesets_()
back.front.update_tables(
[gh.IMAGE_TABLE, gh.IMAGESET_TABLE], clear_view_selection=True
)
[docs] @blocking_slot(list)
def send_to_new_imageset(back, gid_list, mode='move'):
assert len(gid_list) > 0, 'Cannot create a new imageset with no images'
print('\n\n[back] send_to_new_imageset')
ibs = back.ibs
# imagesettext = const.NEW_IMAGESET_IMAGESETTEXT
# imagesettext_list = [imagesettext] * len(gid_list)
# ibs.set_image_imagesettext(gid_list, imagesettext_list)
new_imgsetid = ibs.create_new_imageset_from_images(gid_list) # NOQA
if mode == 'move':
imgsetid = back.get_selected_imgsetid()
imgsetid_list = [imgsetid] * len(gid_list)
ibs.unrelate_images_and_imagesets(gid_list, imgsetid_list)
elif mode == 'copy':
pass
else:
raise AssertionError('invalid mode=%r' % (mode,))
back.update_special_imagesets_()
back.front.update_tables(
[gh.IMAGE_TABLE, gh.IMAGESET_TABLE], clear_view_selection=True
)
# --------------------------------------------------------------------------
# Batch menu slots
# --------------------------------------------------------------------------
[docs] @blocking_slot()
def imageset_set_species(back, refresh=True):
"""
HACK: sets the species columns of all annotations in the imageset
to be whatever is currently in the detect config
"""
print('[back] imageset_set_species')
ibs = back.ibs
imgsetid = back.get_selected_imgsetid()
aid_list = back.ibs.get_valid_aids(imgsetid=imgsetid)
species_list = [ibs.cfg.detect_cfg.species_text] * len(aid_list)
ibs.set_annot_species(aid_list, species_list)
if refresh:
back.front.update_tables([gh.ANNOTATION_TABLE])
[docs] @blocking_slot()
def change_detection_species(back, index, species_text):
""" callback for combo box """
print('[back] change_detection_species(%r, %r)' % (index, species_text))
ibs = back.ibs
# Load full blown configs for each species
if back.edit_prefs_wgt:
back.edit_prefs_wgt.close()
if species_text == 'none':
cfgname = const.UNKNOWN # 'cfg'
else:
cfgname = species_text
#
current_species = None if species_text == 'none' else species_text
#####
# <GENERAL CONFIG SAVE>
config_fpath = ut.unixjoin(ibs.get_dbdir(), 'general_config.cPkl')
try:
general_config = ut.load_cPkl(config_fpath)
except IOError:
general_config = {}
general_config['current_species'] = current_species
ut.save_cPkl(ut.unixjoin(ibs.get_dbdir(), 'general_config.cPkl'), general_config)
# </GENERAL CONFIG SAVE>
#####
ibs._load_named_config(cfgname)
ibs.cfg.detect_cfg.species_text = species_text
ibs.cfg.save()
# TODO: incorporate this as a signal in guiback which connects to a
# slot in guifront
# back.front.detect_button.setEnabled(
# ibs.has_species_detector(species_text)
# )
[docs] def get_selected_species(back):
species_text = back.ibs.cfg.detect_cfg.species_text
if species_text == 'none':
species_text = None
print('species_text = %r' % (species_text,))
if species_text is None or species_text == const.UNKNOWN:
# hack to set species for user
pass
# species_text = back.ibs.get_primary_database_species()
# print('\'species_text = %r' % (species_text,))
# sig = signal_(str)
# sig.connect(back.ibswgt.species_combo.setItemText)
# back.ibswgt.species_combo.setItemText(species_text)
# back.changeSpeciesSignal.emit(str(species_text))
# sig.emit(species_text)
# back.ibs.cfg.detect_cfg.species_text = species_text
return species_text
[docs] @blocking_slot()
def change_daids_mode(back, index, value):
print('[back] change_daids_mode(%r, %r)' % (index, value))
back.daids_mode = value
# ibs = back.ibs
# ibs.cfg.detect_cfg.species_text = value
# ibs.cfg.save()
[docs] @blocking_slot()
def do_group_occurrence_step(back, refresh=True):
"""
Group Step for computing occurrneces
CommandLine:
python -m wbia.gui.guiback --test-MainWindowBackend.do_group_occurrence_step --show --no-cnn
Example:
>>> # xdoctest: +REQUIRES(--gui)
>>> from wbia.gui.guiback import * # NOQA
>>> import wbia
>>> main_locals = wbia.main(defaultdb='testdb1')
>>> ibs, back = ut.dict_take(main_locals, ['ibs', 'back'])
>>> ut.exec_funckw(back.do_group_occurrence_step, globals())
>>> back.do_group_occurrence_step()
>>> ut.quit_if_noshow()
"""
print('[back] do_group_occurrence_step')
from wbia import dtool
# class TmpConfig(dtool.Config):
# _param_info_list = back.ibs.cfg.occur_cfg.get_param_info_list()
ibs = back.ibs
ungrouped_gid_list = ibs.get_ungrouped_gids()
# ungrouped_images = ibs.get_image_instancelist(ungrouped_gid_list)
# image_list['unixtime']
existing_imgset_id_list = ibs.get_valid_imgsetids(
is_occurrence=True, shipped=False, min_num_gids=1
)
class TmpConfig(dtool.Config):
_param_info_list = [
ut.ParamInfo('seconds_thresh', 1600, 'sec'),
ut.ParamInfo('use_gps', True, ''),
]
config = TmpConfig(**back.ibs.cfg.occur_cfg.to_dict())
options = [
'Create new occurrences',
'Add to existing',
'Regroup all',
]
reply, new_config = back.user_option(
title='Occurrence Grouping',
msg=ut.codeblock(
'''
Choose how we should group the %d ungrouped images into occurrences.
We can either:
(1) append new occurrences
(2) add to the %d existing occurrences
(3) redo everything
'''
)
% (len(ungrouped_gid_list), len(existing_imgset_id_list)),
config=config,
options=options,
default=options[0],
)
print('reply = %r' % (reply,))
if reply not in options:
raise guiexcept.UserCancel
if reply != options[2] and len(ungrouped_gid_list) == 0:
back.user_warning(msg='There are no ungrouped images.')
raise guiexcept.UserCancel
if reply == options[0]:
back.ibs.compute_occurrences(config=new_config)
elif reply == options[1]:
# Add to existing imaesets
imagesettext_list = ibs.get_imageset_text(existing_imgset_id_list)
import numpy as np
# Get unixtimes of the new images
unixtime_list = ibs.get_image_unixtime(ungrouped_gid_list)
# Get unixtimes of the occurrences
imgset_gids_list = ibs.get_imageset_gids(existing_imgset_id_list)
imgset_unixtimes_list = ibs.unflat_map(
ibs.get_image_unixtime, imgset_gids_list
)
# imageset_start_time_posix_list = np.array(ut.lmap(np.min, unixtimes_list))
imageset_mean_time_posix_list = np.array(
ut.lmap(np.mean, imgset_unixtimes_list)
)
# imageset_end_time_posix_list = np.array(ut.lmap(np.max, unixtimes_list))
assigned_idx = [
np.abs(x - imageset_mean_time_posix_list).argmin() for x in unixtime_list
]
assigned_imgset = ut.take(imagesettext_list, assigned_idx)
ibs.set_image_imagesettext(ungrouped_gid_list, assigned_imgset)
# HACK TO UPDATE IMAGESET POSIX TIMES
# CAREFUL THIS BLOWS AWAY SMART DATA
ibs.update_imageset_info(ibs.get_valid_imgsetids())
elif reply == options[2]:
if back.are_you_sure(use_msg='Regrouping will destroy all existing groups'):
back.ibs.delete_all_imagesets()
back.ibs.compute_occurrences(config=new_config)
else:
raise guiexcept.UserCancel
back.update_special_imagesets_()
print('[back] about to finish computing imagesets')
back.front.imageset_tabwgt._close_all_tabs()
if refresh:
back.front.update_tables()
print('[back] finished computing imagesets')
[docs] @blocking_slot()
def run_detection_step(back, refresh=True, **kwargs):
r"""
Args:
refresh (bool): (default = True)
CommandLine:
python -m wbia.gui.guiback run_detection_step --show
Example:
>>> # xdoctest: +REQUIRES(--gui)
>>> from wbia.gui.guiback import * # NOQA
>>> import wbia
>>> main_locals = wbia.main(defaultdb='testdb1')
>>> ibs, back = ut.dict_take(main_locals, ['ibs', 'back'])
>>> ut.exec_funckw(back.run_detection_step, globals())
>>> back.run_detection_step()
>>> back.cleanup()
>>> ut.quit_if_noshow()
"""
print('\n\n')
imgsetid = back._eidfromkw(kwargs)
ibs = back.ibs
# Get images without any annotations
set_gids = ibs.get_valid_gids(imgsetid=imgsetid)
is_empty = [n == 0 for n in ibs.get_image_num_annotations(set_gids)]
gid_list = ut.compress(set_gids, is_empty)
print('[back] run_detection_step(imgsetid=%r)' % (imgsetid))
print('[back] detected %d/%d empty gids' % (len(gid_list), len(set_gids)))
imgset_text = back.ibs.get_imageset_text(imgsetid)
detector = back.ibs.cfg.detect_cfg.detector
review_in_web = True
comp_name = ut.get_computer_name()
db_name = ibs.dbname
is_lewa = comp_name in ['wbia.cs.uic.edu'] or db_name in ['LEWA', 'lewa_grevys']
if is_lewa:
review_in_web_mode = 'annotations'
else:
review_in_web_mode = 'detections'
# review_in_web_mode = 'annotations'
assert review_in_web_mode in ['detections', 'annotations']
if True:
# TODO better confirm dialog
from wbia import dtool
species_text = ibs.get_all_species_texts()
species_nice = ibs.get_all_species_nice()
class TmpDetectConfig(dtool.Config):
_param_info_list = [
ut.ParamInfo('review_in_web', review_in_web),
ut.ParamInfo(
'detector',
ibs.cfg.detect_cfg.detector,
valid_values=['cnn', 'rf'],
),
ut.ParamInfo(
'species',
ibs.cfg.detect_cfg.species_text,
valid_values=species_nice,
hideif=lambda cfg: cfg['detector'] != 'rf',
),
]
config = TmpDetectConfig(**back.ibs.cfg.detect_cfg.to_dict())
options = [
'Start Detection',
]
reply, new_config = back.user_option(
title='Run Detection Confirmation',
msg=ut.codeblock(
'''
Preparing to run detection on %d images in \'%s\'.
'''
)
% (len(gid_list), imgset_text),
config=config,
options=options,
default=options[0],
)
print('reply = %r' % (reply,))
if reply not in options:
raise guiexcept.UserCancel
review_in_web = new_config['review_in_web']
detector = new_config['detector']
species = new_config['species']
nice2_text = dict(zip(species_nice, species_text))
species = nice2_text.get(species, species)
if detector in ['cnn', 'cnn_yolo', 'yolo']:
ibs.detect_cnn_yolo(gid_list)
back.front.update_tables([gh.IMAGE_TABLE, gh.ANNOTATION_TABLE])
elif detector in ['random_forest', 'rf']:
ibs.detect_random_forest(gid_list, species)
else:
raise ValueError('Detector=%r not recognized' % (detector,))
print('[back] about to finish detection')
if refresh:
back.front.update_tables([gh.IMAGE_TABLE, gh.ANNOTATION_TABLE])
else:
# OLD CODE
if detector in ['cnn', 'cnn_yolo', 'yolo']:
# Construct message
msg_fmtstr_list = ['You are about to run detection using CNN YOLO...']
fmtdict = dict()
# Append detection configuration information
msg_fmtstr_list += [' Images: {num_gids}'] # Add more spaces
# msg_fmtstr_list += ['* # database annotations={num_daids}.']
# msg_fmtstr_list += ['* database species={d_species_phrase}.']
fmtdict['num_gids'] = len(gid_list)
# Finish building confirmation message
msg_fmtstr_list += ['']
msg_fmtstr_list += ['Press \'Yes\' to continue']
msg_fmtstr = '\n'.join(msg_fmtstr_list)
msg_str = msg_fmtstr.format(**fmtdict)
if back.are_you_sure(use_msg=msg_str):
print('[back] run_detection_step(imgsetid=%r)' % (imgsetid))
ibs.detect_cnn_yolo(gid_list)
print('[back] about to finish detection')
if refresh:
back.front.update_tables([gh.IMAGE_TABLE, gh.ANNOTATION_TABLE])
print('[back] finished detection')
elif detector in ['random_forest', 'rf']:
species = ibs.cfg.detect_cfg.species_text
# Construct message
msg_fmtstr_list = [
'You are about to run detection using Random Forests...'
]
fmtdict = dict()
# Append detection configuration information
msg_fmtstr_list += [' Images: {num_gids}'] # Add more spaces
msg_fmtstr_list += [' Species: {species_phrase}']
# msg_fmtstr_list += ['* # database annotations={num_daids}.']
# msg_fmtstr_list += ['* database species={d_species_phrase}.']
fmtdict['num_gids'] = len(gid_list)
fmtdict['species_phrase'] = species
# Finish building confirmation message
msg_fmtstr_list += ['']
msg_fmtstr_list += ['Press \'Yes\' to continue']
msg_fmtstr = '\n'.join(msg_fmtstr_list)
msg_str = msg_fmtstr.format(**fmtdict)
if back.are_you_sure(use_msg=msg_str):
print(
'[back] run_detection_step(species=%r, imgsetid=%r)'
% (species, imgsetid)
)
ibs.detect_random_forest(gid_list, species)
print('[back] about to finish detection')
if refresh:
back.front.update_tables([gh.IMAGE_TABLE, gh.ANNOTATION_TABLE])
print('[back] finished detection')
else:
raise ValueError('Detector not recognized')
if review_in_web:
# back.user_info(msg='Detection has finished. Launching web review')
web_domain = back.get_background_web_domain()
if review_in_web_mode == 'annotations':
url = 'http://%s/turk/annotation/?imgsetid=%s' % (web_domain, imgsetid,)
elif review_in_web_mode == 'detections':
url = 'http://%s/turk/detection/?imgsetid=%s' % (web_domain, imgsetid,)
else:
raise ValueError('invalid value for review_in_web_mode')
print('[guiback] Opening... %r' % (url,))
import webbrowser
back.start_web_server_parallel(browser=False)
webbrowser.open(url)
else:
back.user_info(msg='Detection has finished.')
[docs] @blocking_slot()
def show_advanced_id_interface(back):
"""
CommandLine:
python -m wbia.gui.guiback show_advanced_id_interface --show
Example:
>>> # xdoctest: +REQUIRES(--gui)
>>> from wbia.gui.guiback import * # NOQA
>>> import wbia
>>> main_locals = wbia.main(defaultdb='testdb1')
>>> ibs, back = ut.dict_take(main_locals, ['ibs', 'back'])
>>> ut.exec_funckw(back.show_advanced_id_interface, globals())
>>> back.show_advanced_id_interface()
>>> back.cleanup()
>>> ut.quit_if_noshow()
>>> #gt.ensure_qapp() # must be ensured before any embeding
>>> import wbia.plottool as pt
>>> gt.qtapp_loop(qwin=back)
"""
# from wbia.init import filter_annots
# filter_kw = filter_annots.get_default_annot_filter_form()
back.custom_query_widget = CustomAnnotCfgSelector(back.ibs)
back.custom_query_widget.show()
app = gt.get_qtapp()
app.processEvents()
app.processEvents()
back.custom_query_widget.onstart()
# back.custom_query_widget.apply_new_config()
# dlg = wgt.as_dialog(back)
# dlg.show()
# dlg.exec_()
# if not wgt.accept_flag:
# #reply not in options:
# raise guiexcept.UserCancel
# back.compute_queries(qaid_list=wgt.qaids, daid_list=back.daids)
[docs] def confirm_query_dialog2(
back,
species2_expanded_aids=None,
cfgdict=None,
query_msg=None,
query_title=None,
review_cfg={},
):
"""
Asks the user to confirm starting the identification query
"""
msg_str, detailed_msg = back.make_confirm_query_msg2(
species2_expanded_aids, cfgdict=cfgdict, query_title=query_title
)
if query_title is None:
query_title = 'custom'
confirm_kw = dict(
use_msg=msg_str,
title='Begin %s ID' % (query_title,),
default='Yes',
detailed_msg=detailed_msg,
)
# TODO better confirm dialog
if True:
from wbia import dtool
ibs = back.ibs # NOQA
# class TmpIDConfig(dtool.Config):
# _param_info_list = [
# #ut.ParamInfo('K', ibs.cfg.query_cfg.nn_cfg.K),
# #ut.ParamInfo('Knorm', ibs.cfg.query_cfg.nn_cfg.Knorm),
# #ut.ParamInfo('chip_size', ibs.cfg.chip_cfg.dim_size),
# ]
# **back.ibs.cfg.to_dict())
# config = TmpIDConfig()
review_config = {
'filter_reviewed': review_cfg.get(
'filter_reviewed',
ibs.cfg.other_cfg.ensure_attr('filter_reviewed', True),
),
'ranks_top': review_cfg.get(
'ranks_top', ibs.cfg.other_cfg.ensure_attr('ranks_top', 2)
),
'filter_true_matches': review_cfg.get('filter_true_matches', False),
}
if cfgdict is None:
cfgdict = {}
tmpdict = cfgdict.copy()
tmpdict.update(review_config)
config = dtool.Config.from_dict(tmpdict)
# print('config = %r' % (config,))
options = [
'Start ID',
]
if ut.get_argflag(('--yes', '-y')):
reply = options[0]
new_config = config
else:
reply, new_config = back.user_option(
title='Begin %s ID' % (query_title,),
msg=msg_str,
config=config,
options=options,
default=options[0],
detailed_msg=detailed_msg,
)
print('reply = %r' % (reply,))
updated_config = new_config.asdict()
updated_review_cfg = ut.dict_subset(updated_config, review_config.keys())
ut.delete_dict_keys(updated_config, review_config.keys())
if reply not in options:
raise guiexcept.UserCancel
return updated_config, updated_review_cfg
else:
if not back.are_you_sure(**confirm_kw):
raise guiexcept.UserCancel
[docs] @blocking_slot()
def compute_queries(
back,
refresh=True,
daids_mode=None,
query_is_known=None,
qaid_list=None,
use_prioritized_name_subset=False,
use_visual_selection=False,
cfgdict={},
query_msg=None,
custom_qaid_list_title=None,
daid_list=None,
partition_queries_by_species=False,
**kwargs
):
"""
MAIN QUERY FUNCTION
execute_query
Batch -> Compute OldStyle Queries
and Actions -> Query
Computes query results for all annotations in an imageset.
Results are either vs-exemplar or intra-imageset
CommandLine:
./reset_dbs.py && ./main.py --query 1 -y
./reset_dbs.py --reset-mtest && ./main.py --query 1 -y --db PZ_MTEST --progtext
./main.py --query 1 -y
python -m wbia --query 1 -y
python -m wbia --query 1:119 --db PZ_MTEST --nocache-query --nocache-nnmid -y
python -m wbia --query 1:119 --db PZ_MTEST --nocache-query --nocache-nnmid -y --force-all-progress
python -m wbia --query 1:119 --db PZ_MTEST --nocache-query --nocache-nnmid --hots-batch-size=3 -y
python -m wbia --query 1:119 --db PZ_MTEST --nocache-query --nocache-nnmid --hots-batch-size=3 -y
python -m wbia --query 1:119 --db PZ_MTEST --nocache-query --nocache-nnmid --hots-batch-size=32 -y
Example:
>>> # DISABLE_DOCTEST
>>> from wbia.gui.guiback import * # NOQA
>>> import wbia
>>> main_locals = wbia.main(db='testdb2')
>>> back = main_locals['back']
>>> ibs = back.ibs
>>> query_is_known = None
>>> refresh = True
>>> daids_mode = None
>>> imgsetid = None
>>> kwargs = {}
>>> print(result)
"""
imgsetid = back._eidfromkw(kwargs)
print('\n')
print('------')
print(
'[back] compute_queries: imgsetid=%r, mode=%r' % (imgsetid, back.daids_mode)
)
print('[back] use_prioritized_name_subset = %r' % (use_prioritized_name_subset,))
print('[back] use_visual_selection = %r' % (use_visual_selection,))
print('[back] daids_mode = %r' % (daids_mode,))
print('[back] cfgdict = %r' % (cfgdict,))
print('[back] query_is_known = %r' % (query_is_known,))
if qaid_list is not None:
if custom_qaid_list_title is None:
custom_qaid_list_title = 'Custom'
qaid_title = custom_qaid_list_title
elif use_visual_selection:
qaid_title = 'Selected'
else:
# if not visual selection, then qaids are selected by imageset
qaid_title = back.ibs.get_imageset_text(imgsetid)
if use_prioritized_name_subset:
qaid_title += '(priority_subset)'
# Set title variables
daid_title = None
if daids_mode == const.VS_EXEMPLARS_KEY:
daid_title = 'Exemplars'
elif daids_mode == const.INTRA_OCCUR_KEY:
daid_title = 'Intra ImageSet'
elif daids_mode == 'All':
daid_title = 'Everything'
else:
print('Unknown daids_mode=%r' % (daids_mode,))
query_title = '%s-vs-%s' % (qaid_title, daid_title)
print('query_title = %r' % (query_title,))
if daids_mode == const.VS_EXEMPLARS_KEY:
# Automatic setting of exemplars
back.set_exemplars_from_quality_and_viewpoint_()
species2_expanded_aids = back._get_expanded_aids_groups(
imgsetid,
daids_mode=daids_mode,
use_prioritized_name_subset=use_prioritized_name_subset,
use_visual_selection=use_visual_selection,
qaid_list=qaid_list,
daid_list=daid_list,
query_is_known=query_is_known,
partition_queries_by_species=partition_queries_by_species,
remove_unknown_species=False,
)
if not partition_queries_by_species:
# hack: overwrite species2_expanded_aids to undo species partition
all_qaids = set()
all_daids = set()
for _qaids, _daids in species2_expanded_aids.values():
all_qaids.update(_qaids)
all_daids.update(_daids)
species2_expanded_aids = {
'all_species': (sorted(all_qaids), sorted(all_daids))
}
if len(species2_expanded_aids) == 0:
raise guiexcept.InvalidRequest(
'Is the database empty? Are species labels assigned correctly? '
'There are no pairs of query and database annotations with the same species'
)
review_cfg = kwargs.copy()
review_cfg['filter_true_matches'] = daids_mode == const.VS_EXEMPLARS_KEY
# Check if there is nothing to do
flags = []
for species, expanded_aids in species2_expanded_aids.items():
qaids, daids = expanded_aids
flag = len(qaids) == 1 and len(daids) == 1 and daids[0] == qaids[0]
flags.append(flag)
if len(flags) > 0 and all(flags):
raise AssertionError('No need to compute a query against itself')
cfgdict, review_cfg = back.confirm_query_dialog2(
species2_expanded_aids,
query_msg=query_msg,
query_title=query_title,
cfgdict=cfgdict,
review_cfg=review_cfg,
)
print('cfgdict = %r' % (cfgdict,))
prog_bar = back.front.prog_bar
prog_bar.setVisible(True)
prog_bar.setWindowTitle('Initialize query')
prog_hook = prog_bar.utool_prog_hook
# prog_bar = guitool.newProgressBar(None) # back.front)
# Doesn't seem to work correctly
# prog_hook.show_indefinite_progress()
prog_hook.force_event_update()
# prog_hook.set_progress(0)
prog_bar.setWindowTitle('Start query')
# import utool
# utool.embed()
query_results = {}
for key, (qaids, daids) in ut.ProgressIter(
species2_expanded_aids.items(), prog_hook=prog_hook
):
prog_bar.setWindowTitle('Initialize %r query' % (key,))
qreq_ = back.ibs.new_query_request(qaids, daids, cfgdict=cfgdict)
prog_hook.initialize_subhooks(1)
subhook = prog_hook.next_subhook()
cm_list = qreq_.execute(prog_hook=subhook)
query_results[key] = (cm_list, qreq_)
# HACK IN IMAGESET INFO
if daids_mode == const.INTRA_OCCUR_KEY:
for cm in cm_list:
# if cm is not None:
cm.imgsetid = imgsetid
back.front.prog_bar.setVisible(False)
print('[back] About to finish compute_queries: imgsetid=%r' % (imgsetid,))
for key in query_results.keys():
(cm_list, qreq_) = query_results[key]
# Filter duplicate names if running vsexemplar
back.review_queries(
cm_list,
qreq_=qreq_,
query_title=query_title + ' ' + str(key),
review_cfg=review_cfg,
)
if refresh:
back.front.update_tables()
print('[back] FINISHED compute_queries: imgsetid=%r' % (imgsetid,))
[docs] def get_selected_daids(
back, imgsetid=None, daids_mode=None, qaid_list=None, species=None
):
daids_mode = back.daids_mode if daids_mode is None else daids_mode
daids_mode_valid_kw_dict = {
const.VS_EXEMPLARS_KEY: {'is_exemplar': True,},
const.INTRA_OCCUR_KEY: {'imgsetid': imgsetid,},
'all': {},
}
if qaid_list is not None and species is None:
ibs = back.ibs
hist_ = ut.dict_hist(ibs.get_annot_species_texts(qaid_list))
print('[back] len(qaid_list)=%r' % (len(qaid_list)))
print('[back] hist_ = %r' % (hist_,))
if len(hist_) == 1:
# select the query species if there is only one
species = hist_.keys()[0]
if species is None:
species = back.get_selected_species()
valid_kw = {
'minqual': 'ok',
}
if species != const.UNKNOWN:
# Query everything if you don't know the species
valid_kw['species'] = species
mode_str = {
const.VS_EXEMPLARS_KEY: 'vs_exemplar',
const.INTRA_OCCUR_KEY: 'intra_occurrence',
'all': 'all',
}[daids_mode]
valid_kw.update(daids_mode_valid_kw_dict[daids_mode])
print('[back] get_selected_daids: ' + mode_str)
print('[back] ... valid_kw = ' + ut.repr2(valid_kw))
daid_list = back.ibs.get_valid_aids(**valid_kw)
return daid_list
[docs] def make_confirm_query_msg2(
back, species2_expanded_aids, cfgdict=None, query_msg=None, query_title=None
):
r"""
CommandLine:
python -m wbia.gui.guiback --test-MainWindowBackend.make_confirm_query_msg2 --show
Example:
>>> # xdoctest: +REQUIRES(--gui)
>>> from wbia.gui.guiback import * # NOQA
>>> import wbia
>>> main_locals = wbia.main(defaultdb='testdb1')
>>> ibs, back = ut.dict_take(main_locals, ['ibs', 'back'])
>>> ut.exec_funckw(back.make_confirm_query_msg2, globals())
>>> imgsetid = ibs.get_imageset_imgsetids_from_text('*All Images')
>>> species2_expanded_aids = back._get_expanded_aids_groups(imgsetid)
>>> short_msg, detailed_msg = back.make_confirm_query_msg2(species2_expanded_aids)
>>> print(short_msg)
>>> print(detailed_msg)
>>> ut.quit_if_noshow()
>>> back.confirm_query_dialog2(species2_expanded_aids)
"""
ibs = back.ibs
species_text = ibs.get_all_species_texts()
species_nice = ibs.get_all_species_nice()
species_dict = dict(zip(species_text, species_nice))
def get_unique_species_phrase(aid_list):
def boldspecies(species):
species_bold_nice = '\'%s\'' % (
species_dict.get(species, species).upper(),
)
return species_bold_nice
species_list = list(set(ibs.get_annot_species_texts(aid_list)))
species_nice_list = list(map(boldspecies, species_list))
species_phrase = ut.conj_phrase(species_nice_list, 'and')
return species_phrase
# Build confirmation message
fmtdict = dict()
msg_fmtstr_list = []
if query_msg is not None:
msg_fmtstr_list += [query_msg]
if query_title is None:
query_title = 'custom'
ngroups = len(species2_expanded_aids)
if ngroups > 1:
msg_fmtstr_list += [
(
'You are about to run {query_title} '
'identification with {ngroups} groups...'
).format(
query_title=query_title, ngroups=ngroups,
)
]
else:
msg_fmtstr_list += [
('You are about to run {query_title} ' 'identification...').format(
query_title=query_title,
)
]
species_list = list(species2_expanded_aids.keys())
detailed_msg_list = []
annotstats_kw = {}
for count, species in enumerate(species_list):
qaids, daids = species2_expanded_aids[species]
species_nice = species_dict.get(species, species)
# species_phrase = get_unique_species_phrase(qaids + daids)
msg_fmtstr_list += ['']
fmtdict = {}
qaid_stats = ibs.get_annot_stats_dict(
qaids, prefix='q', per_name=True, old=False
)
daid_stats = ibs.get_annot_stats_dict(
daids, prefix='d', per_name=True, old=False
)
stats_ = ibs.get_annotconfig_stats(
qaids, daids, combined=False, species_hist=True, **annotstats_kw
)
fmtdict.update(**qaid_stats)
fmtdict.update(**daid_stats)
fmtdict['qannots'] = ut.pluralize('annotation', len(qaids))
fmtdict['dannots'] = ut.pluralize('annotation', len(daids))
fmtdict['species_nice'] = species_nice
fmtdict['count'] = count
# Add simple info
if ngroups > 1:
part1 = 'Group {count} '
else:
part1 = 'This '
part2 = (
part1
+ 'will identify {num_qaids} query {qannots} against {num_daids} {species_nice} database {dannots}.'
).format(**fmtdict)
msg_fmtstr_list += [part2]
# Add detailed info
stats_str2 = ut.repr2(
stats_, strvals=True, newlines=2, explicit=False, nobraces=False
)
detailed_msg_list.append('--- Group %d ---' % (count,))
detailed_msg_list.append(stats_str2)
# Finish building confirmation message
msg_fmtstr_list += ['']
msg_fmtstr_list += ['Press \'Yes\' to continue']
msg_fmtstr = '\n'.join(msg_fmtstr_list)
msg_str = msg_fmtstr.format(**fmtdict)
if cfgdict is not None and len(cfgdict) > 0:
detailed_msg_list = [
'Special Settings: {}'.format(ut.repr2(cfgdict))
] + detailed_msg_list
detailed_msg = '\n'.join(detailed_msg_list)
return msg_str, detailed_msg
[docs] def run_annot_splits(back, aid_list):
"""
Checks for mismatches within a group of annotations
Args:
aid_list (int): list of annotation ids
CommandLine:
python -m wbia.gui.guiback --test-MainWindowBackend.run_annot_splits --show
Example:
>>> # xdoctest: +REQUIRES(--gui)
>>> from wbia.gui.guiback import * # NOQA
>>> back = testdata_guiback()
>>> ibs = back.ibs
>>> aids_list, nids = back.ibs.group_annots_by_name(back.ibs.get_valid_aids())
>>> aid_list = aids_list[ut.list_argmax(list(map(len, aids_list)))]
>>> back.run_annot_splits(aid_list)
>>> ut.quit_if_noshow()
>>> guitool.qtapp_loop(back.mainwin, frequency=100)
"""
cfgdict = {
'can_match_samename': True,
'K': 3,
'Knorm': 3,
'prescore_method': 'csum',
'score_method': 'csum',
}
ranks_top = min(len(aid_list), 10)
review_cfg = {
'filter_reviewed': False,
'ranks_top': ranks_top,
'name_scoring': False,
}
ibs = back.ibs
cfgdict, review_cfg = back.confirm_query_dialog2(
{'split': (aid_list, aid_list)},
cfgdict=cfgdict,
query_msg='Checking for SPLIT cases (matching each annotation within a name)',
review_cfg=review_cfg,
)
qreq_ = ibs.new_query_request(aid_list, aid_list, cfgdict=cfgdict)
cm_list = qreq_.execute()
back.review_queries(
cm_list, qreq_=qreq_, query_title='Annot Splits', review_cfg=review_cfg
)
if False:
from wbia.viz import viz_graph2
import imp
imp.reload(viz_graph2)
win = viz_graph2.make_qt_graph_review(qreq_, cm_list, review_cfg=review_cfg)
win.show()
[docs] def run_merge_checks(back):
r"""
Checks for missed matches within a group of annotations
CommandLine:
python -m wbia.gui.guiback --test-run_merge_checks --show
Example:
>>> # xdoctest: +REQUIRES(--gui)
>>> from wbia.gui.guiback import * # NOQA
>>> back = testdata_guiback()
>>> result = back.run_merge_checks()
>>> print(result)
>>> ut.quit_if_noshow()
>>> guitool.qtapp_loop(back.mainwin, frequency=100)
"""
pass
qaid_list = back.ibs.get_valid_aids(is_exemplar=True)
cfgdict = {
'can_match_samename': False,
#'K': 3,
#'Knorm': 3,
#'prescore_method': 'csum',
#'score_method': 'csum'
}
query_msg = 'Checking for MERGE cases (this is an exemplars-vs-exemplars query)'
back.compute_queries(
qaid_list=qaid_list,
daids_mode=const.VS_EXEMPLARS_KEY,
query_msg=query_msg,
cfgdict=cfgdict,
custom_qaid_list_title='Merge Candidates',
)
[docs] def run_merge_checks_multitons(back):
r"""
Checks for missed matches within a group of annotations.
Only uses annotations with more 2 annots per id.
"""
pass
ibs = back.ibs
# qaid_list = back.ibs.get_valid_aids(is_exemplar=True)
from wbia import dtool
config = dtool.Config.from_dict(
{
'K': 1,
'Knorm': 5,
'min_pername': 1,
'max_pername': 1,
'exemplars_per_name': 1,
'method': 'randomize',
'seed': 42,
}
)
# ibswgt = None
dlg = gt.ConfigConfirmWidget.as_dialog(
title='Confirm Merge Query', msg='Confirm', config=config
)
self = dlg.widget
dlg.resize(700, 500)
dlg.exec_()
print('config = %r' % (config,))
updated_config = self.config # NOQA
print('updated_config = %r' % (updated_config,))
min_pername = updated_config['min_pername']
max_pername = updated_config['max_pername']
aid_list = ibs.filter_annots_general(
min_pername=min_pername, max_pername=max_pername, minqual='ok'
)
if updated_config['method'] == 'randomize':
import numpy as np
rng = np.random.RandomState(int(updated_config['seed']))
grouped_aids = ibs.group_annots_by_name(aid_list)[0]
grouped_aids2 = [
ut.random_sample(aids, updated_config['exemplars_per_name'], rng=rng)
for aids in grouped_aids
]
aid_list = ut.flatten(grouped_aids2)
else:
new_flag_list = ibs.get_annot_quality_viewpoint_subset(
aid_list, updated_config['exemplars_per_name'], allow_unknown=True
)
aid_list = ut.compress(aid_list, new_flag_list)
ibs.print_annot_stats(aid_list)
daid_list = qaid_list = aid_list
# len(aids)
cfgdict = {
'can_match_samename': False,
'K': updated_config['K'],
'Knorm': updated_config['Knorm'],
#'prescore_method': 'csum',
#'score_method': 'csum'
}
query_msg = 'Checking for MERGE cases (this is an special query)'
back.compute_queries(
qaid_list=qaid_list,
daid_list=daid_list,
query_msg=query_msg,
cfgdict=cfgdict,
custom_qaid_list_title='Merge2 Candidates',
)
def _get_expanded_aids_groups(
back,
imgsetid,
daids_mode=None,
use_prioritized_name_subset=False,
use_visual_selection=False,
qaid_list=None,
daid_list=None,
query_is_known=None,
partition_queries_by_species=True,
remove_unknown_species=None,
):
"""
Get the query annotation ids to search and
the database annotation ids to be searched
The query is either a specific selection or everything from this
image set that matches the appropriate filters.
Example:
>>> # DISABLE_DOCTEST
>>> ut.exec_funckw(back._get_expanded_aids_groups, globals())
>>> imgsetid = ibs.get_imageset_imgsetids_from_text('*All Images')
>>> species2_expanded_aids = back._get_expanded_aids_groups(imgsetid)
"""
ibs = back.ibs
daids_mode = back.daids_mode if daids_mode is None else daids_mode
if imgsetid is None:
raise Exception('[back] invalid imgsetid')
if ibs.cfg.other_cfg.enable_custom_filter:
back.user_warning(
msg=ut.codeblock(
'''
other_cfg.enable_custom_filter=True is not longer supported.
Please turn off in Preferences
'''
)
)
if remove_unknown_species is None:
# Default behavior is don't remove unknown species if qaid_list is specified
remove_unknown_species = qaid_list is not None or use_visual_selection
# Query aids are either: given, taken from gui selection, or by imageset
if qaid_list is not None:
qaid_list = qaid_list
elif use_visual_selection:
qaid_list = back.get_selected_aids()
else:
qaid_list = ibs.get_valid_aids(
imgsetid=imgsetid, is_known=query_is_known, minqual='ok'
)
print('[back] Initially loaded len(qaid_list) = %r' % (len(qaid_list),))
if use_prioritized_name_subset:
# Pick only a few queries per name to execute
annots_per_view = 2 # FIXME: use a configuration
new_flag_list = back.ibs.get_annot_quality_viewpoint_subset(
aid_list=qaid_list,
annots_per_view=annots_per_view,
allow_unknown=True,
verbose=True,
)
qaid_list = ut.compress(qaid_list, new_flag_list)
print(
'[back] Filtered query by quality and viewpoint: len(qaid_list) = %r'
% (len(qaid_list),)
)
print('[back] Found len(qaid_list) = %r' % (len(qaid_list),))
# Group annotations by species
species2_qaids = ibs.group_annots_by_prop(qaid_list, ibs.get_annot_species)
# ID unknown species against each database species
nospecies_qaids = species2_qaids.pop(ibs.const.UNKNOWN, [])
print('[back] num Queries without species = %r' % (len(nospecies_qaids),))
print('species2_qaids = %r' % (species2_qaids,))
species2_expanded_aids = {}
species_list = ut.unique(
list(ibs.get_all_species_texts()) + (list(species2_qaids.keys()))
)
for species in species_list:
print('[back] Finding daids for species = %r' % (species,))
qaids = species2_qaids[species]
if daid_list is not None:
daids = daid_list
else:
if not partition_queries_by_species:
species = const.UNKNOWN
daids = back.get_selected_daids(
imgsetid=imgsetid,
daids_mode=daids_mode,
qaid_list=qaids,
species=species,
)
print('[back] * Found len(daids) = %r' % (len(daids),))
qaids_ = ut.unique(qaids + nospecies_qaids)
if len(qaids_) > 0 and len(daids) > 0:
species2_expanded_aids[species] = (qaids_, daids)
else:
print('[back] ! len(nospecies_qaids) = %r' % (len(nospecies_qaids),))
print('[back] ! len(qaids) = %r' % (len(qaids),))
print('[back] ! len(qaids_) = %r' % (len(qaids_),))
print('[back] ! len(daids) = %r' % (len(daids),))
print('WARNING: species = %r is an invalid query' % (species,))
# Dont query unknown species
if remove_unknown_species:
if ibs.const.UNKNOWN in species2_expanded_aids:
del species2_expanded_aids[ibs.const.UNKNOWN]
return species2_expanded_aids
[docs] @blocking_slot()
def filter_imageset_as_camera_trap(back, refresh=True, score_thresh=0.30):
ibs = back.ibs
imgsetid = back.get_selected_imgsetid()
gid_list = ibs.get_imageset_gids(imgsetid)
aid_list = ut.flatten(ibs.get_image_aids(gid_list))
imgset_name = ibs.get_imageset_text(imgsetid)
imgset_dest_name = '%s (FILTERED)' % (imgset_name,)
confirm_kw = dict(
use_msg=ut.codeblock(
'''
This action will process the current ImageSet and search for the images (%d total) which contain zebras.
Note: this process is meant for images from sources like camera traps and aerial drones, where the vast majority of the images have no zebras in them. If you expect the majority of your images to contain zebras, run the standard Detect for better detection accuracy (but will be slower).
This operation will find Plains and Grevy's zebras.
Warning: performing this operation will delete all current annotations (%d total) on all images in the current ImageSet. This operation will also de-associate any metadata (e.g. names) from the annotations. This operation will not delete or modify any images, annotations, or names outside this ImageSet.
Selecting YES will remove this ImageSet's annotations, perform a filtering classification for zebras, run the detection pipeline, and create (or will empty if currently exists) an ImageSet named:
\t %r
This new ImageSet will contain the images where zebras were detected.
'''
)
% (len(gid_list), len(aid_list), imgset_dest_name,),
title='Process ImageSet?',
default='Yes',
)
if not back.are_you_sure(**confirm_kw):
raise guiexcept.UserCancel
depc = ibs.depc_image
ibs.delete_annots(aid_list)
# imgset_rowid_list = ibs.get_imageset_imgsetids_from_text(['POSITIVE_SET', 'NEGATIVE_SET'])
imgset_rowid_list = ibs.get_imageset_imgsetids_from_text([imgset_dest_name])
for imgset_rowid in imgset_rowid_list:
imgset_rowid_list_ = [imgset_rowid] * len(gid_list)
ibs.unrelate_images_and_imagesets(gid_list, imgset_rowid_list_)
config = {'classifier_weight_filepath': 'coco_zebra'}
class_list = depc.get_property('classifier', gid_list, 'class', config=config)
score_list = depc.get_property('classifier', gid_list, 'score', config=config)
score_list_ = [
score_ if class_ == 'positive' else 1.0 - score_
for class_, score_ in zip(class_list, score_list)
]
flag_list = [score_ >= score_thresh for score_ in score_list_]
print('%d / %d' % (flag_list.count(True), len(flag_list),))
pos_gid_list = ut.compress(gid_list, flag_list)
# neg_gid_list = ut.compress(gid_list, ut.not_list(flag_list))
# ibs.set_image_imagesettext(pos_gid_list, ['POSITIVE_SET'] * len(pos_gid_list))
# ibs.set_image_imagesettext(neg_gid_list, ['NEGATIVE_SET'] * len(neg_gid_list))
ibs.set_image_imagesettext(pos_gid_list, [imgset_dest_name] * len(pos_gid_list))
# Run detection
aids_list = ibs.detect_cnn_yolo(pos_gid_list)
# aids_list = ibs.get_image_aids(pos_gid_list)
species_list_list = map(ibs.get_annot_species_texts, aids_list)
species_set_list = map(set, species_list_list)
wanted_set = set(['zebra_grevys', 'zebra_plains'])
flag_list = [
len(wanted_set & species_set) == 0 for species_set in species_set_list
]
print('%d / %d' % (flag_list.count(True), len(flag_list),))
nothing_gid_list = ut.compress(pos_gid_list, flag_list)
imgset_rowid_list_ = [imgset_rowid_list[0]] * len(nothing_gid_list)
ibs.unrelate_images_and_imagesets(nothing_gid_list, imgset_rowid_list_)
# ibs.set_image_imagesettext(nothing_gid_list, ['NEGATIVE_SET'] * len(nothing_gid_list))
[docs] @blocking_slot()
def commit_to_wb_step(back, refresh=True, dry=False):
"""
Step 6) Commit
Sets all imagesets as reviwed and ships them to wildbook
commit step
Args:
refresh (bool): (default = True)
CommandLine:
python -m wbia.gui.guiback commit_to_wb_step --show
Example:
>>> # xdoctest: +REQUIRES(--gui)
>>> from wbia.gui.guiback import * # NOQA
>>> import wbia
>>> main_locals = wbia.main(defaultdb='testdb1')
>>> ibs, back = ut.dict_take(main_locals, ['ibs', 'back'])
>>> ut.exec_funckw(back.do_group_occurrence_step, globals())
>>> dry = True
>>> back.do_group_occurrence_step(dry=dry)
>>> from wbia.gui.guiback import * # NOQA
>>> refresh = True
>>> result = back.commit_to_wb_step(refresh)
>>> print(result)
>>> ut.quit_if_noshow()
>>> import wbia.plottool as pt
>>> ut.show_if_requested()
"""
imgsetid = back.get_selected_imgsetid()
if back.contains_special_imagesets([imgsetid]) or imgsetid is None:
back.user_warning(
msg=ut.codeblock(
'''
This operation is only allowed for OCCURRENCES.
Tried to send a special ImageSet to Wildbook as an occurrence.
Special ImageSets are living entities and are never truely complete.
'''
)
)
else:
# Check to make sure imagesets are ok:
# First, check if imageset can be pushed
ibs = back.ibs
# imgsets = ibs.imagesets(imgsetid)
# aid_list = imgsets.aids[0]
aid_list = ibs.get_imageset_aids(imgsetid)
assert (
len(aid_list) > 0
), 'ImageSet imgsetid=%r cannot be shipped with0 annots' % (imgsetid,)
unknown_flags = ibs.is_aid_unknown(aid_list)
nUnnamed = sum(unknown_flags)
unnamed_aid_list = ut.compress(aid_list, unknown_flags)
named_aid_list = ut.compress(aid_list, ut.not_list(unknown_flags))
nid2_aids = ut.group_items(named_aid_list, ibs.get_annot_nids(named_aid_list))
nMultiEncounters = sum([len(x) > 1 for x in nid2_aids.values()])
nSingleEncounters = sum([len(x) == 1 for x in nid2_aids.values()])
unnamed_ok_aid_list = ibs.filter_annots_general(
unnamed_aid_list, minqual='ok',
)
nUnnamedOk = sum(unnamed_ok_aid_list)
other_aids = ibs.get_annot_groundtruth(
ut.take_column(nid2_aids.values(), 0), is_exemplar=True
)
other_aids = [set(aids) - set(aid_list) for aids in other_aids]
nMatchedExemplars = sum([len(x) >= 1 for x in other_aids])
msg_list = [
'%d Encounter%s matched an exemplar'
% ((nMatchedExemplars), '' if nMatchedExemplars == 1 else 's'),
'%d Encounter%s only one annotation'
% ((nSingleEncounters), ' has' if nSingleEncounters == 1 else 's have'),
'%d Encounter%s more than one annotation'
% ((nMultiEncounters), ' has' if nMultiEncounters == 1 else 's have'),
'%d annotation%s have not had a name assigned%s'
% (nUnnamed, '' if nUnnamed == 1 else 's', '!' if nUnnamed > 0 else '.'),
'%d annotation%s unnamed with an identifiable quality%s'
% (
nUnnamedOk,
'' if nUnnamedOk == 1 else 's',
'!' if nUnnamedOk > 0 else '.',
),
]
# Set all images to be reviewed
gid_list = back.ibs.get_valid_gids(imgsetid=imgsetid)
confirm_kw = dict(
use_msg=ut.codeblock(
'''
Have you finished reviewing ALL detections,
Intra-Occurence IDs, and Vs-Exemplar IDs?
%s
Selecting YES will remove this occurrence, mark all images are
reviewed and processed, and then send it to wildbook.
'''
)
% '\n'.join(msg_list),
title='Complete Occurrence?',
default='Yes',
)
if not back.are_you_sure(**confirm_kw):
raise guiexcept.UserCancel
assert nUnnamedOk == 0, (
'ImageSet imgsetid=%r1 cannot be shipped becuase '
'annotation(s) %r with an identifiable quality have '
'not been named'
) % (imgsetid, unnamed_ok_aid_list,)
if not dry:
# back.start_web_server_parallel(browser=False)
# gid_list = ibs.get_imageset_gids(imgsetid)
back.ibs.set_image_reviewed(gid_list, [1] * len(gid_list))
# Set imageset to be processed
back.ibs.set_imageset_processed_flags([imgsetid], [1])
back.ibs.wildbook_signal_imgsetid_list([imgsetid])
back.front.imageset_tabwgt._close_tab_with_imgsetid(imgsetid)
if refresh:
back.front.update_tables([gh.IMAGESET_TABLE])
[docs] def send_unshipped_processed_imagesets(back, refresh=True):
back.start_web_server_parallel(browser=False)
processed_set = set(back.ibs.get_valid_imgsetids(processed=True))
shipped_set = set(back.ibs.get_valid_imgsetids(shipped=True))
imgsetid_list = list(processed_set - shipped_set)
back.ibs.wildbook_signal_imgsetid_list(imgsetid_list)
# --------------------------------------------------------------------------
# Option menu slots
# --------------------------------------------------------------------------
[docs] @blocking_slot()
def layout_figures(back):
""" Options -> Layout Figures"""
print('[back] layout_figures')
fig_presenter.all_figures_tile()
pass
[docs] @slot_()
@backreport
def edit_preferences(back):
""" Options -> Edit Preferences"""
print('[back] edit_preferences')
assert back.ibs is not None, 'No database is loaded. Open a database to continue'
epw = back.ibs.cfg.createQWidget()
fig_presenter.register_qt4_win(epw)
epw.ui.defaultPrefsBUT.clicked.connect(back.default_config)
epw.show()
back.edit_prefs_wgt = epw
# query_cfgstr = ''.join(back.ibs.cfg.query_cfg.get_cfgstr())
# print('[back] query_cfgstr = %s' % query_cfgstr)
# print('')
# --------------------------------------------------------------------------
# Help menu slots
# --------------------------------------------------------------------------
[docs] @slot_()
@backreport
def view_docs(back):
""" Help -> View Documentation"""
print('[back] view_docs')
raise NotImplementedError()
pass
[docs] @slot_()
@backreport
def view_database_dir(back):
""" Help -> View Directory Slots"""
print('[back] view_database_dir')
ut.view_directory(back.ibs.get_dbdir())
pass
[docs] @slot_()
@backreport
def view_app_files_dir(back):
print('[back] view_app_files_dir')
ut.view_directory(ut.get_app_resource_dir('wbia'))
pass
[docs] @slot_()
@backreport
def view_log_dir_local(back):
print('[back] view_log_dir_local')
ut.view_directory(back.ibs.get_logdir_local())
[docs] @slot_()
@backreport
def view_log_dir_global(back):
print('[back] view_log_dir_global')
ut.view_directory(back.ibs.get_logdir_global())
[docs] @slot_()
@backreport
def view_logs_global(back):
print('[back] view_logs_global')
log_fpath = ut.get_current_log_fpath()
log_text = back.ibs.get_current_log_text()
gt.msgbox(
'Click show details to view logs from log_fpath=%r' % (log_fpath,),
detailed_msg=log_text,
)
# ut.startfile(back.ibs.get_logdir_global())
[docs] @slot_()
@backreport
def redownload_detection_models(back):
print('[back] redownload_detection_models')
if not back.are_you_sure('[back] redownload_detection_models'):
return
ibsfuncs.redownload_detection_models(back.ibs)
[docs] @slot_()
@backreport
def delete_cache(back):
""" Help -> Delete Directory Slots"""
print('[back] delete_cache')
if not back.are_you_sure('[back] delete_cache'):
return
back.ibs.delete_cache()
print('[back] finished delete_cache')
[docs] @slot_()
@backreport
def delete_thumbnails(back):
""" Help -> Delete Thumbnails """
msg = '[back] delete_thumbnails'
print(msg)
if not back.are_you_sure(msg):
return
back.ibs.delete_thumbnails()
print('[back] finished delete_thumbnails')
[docs] @slot_()
@backreport
def delete_global_prefs(back):
msg = '[back] delete_global_prefs'
if not back.are_you_sure(msg):
return
ut.delete(ut.get_app_resource_dir('wbia', 'global_cache'))
[docs] @slot_()
@backreport
def delete_queryresults_dir(back):
msg = '[back] delete_queryresults_dir'
print(msg)
if not back.are_you_sure(
use_msg=('Are you sure you want to delete the ' 'cached query results?')
):
return
ut.delete(back.ibs.qresdir)
ut.delete(back.ibs.bigcachedir)
ut.ensuredir(back.ibs.qresdir)
ut.ensuredir(back.ibs.bigcachedir)
[docs] @blocking_slot()
def dev_reload(back):
""" Help -> Developer Reload"""
print('[back] dev_reload')
back.ibs.rrr()
# back.rrr()
# reload_all()
[docs] @blocking_slot()
def dev_mode(back):
""" Help -> Developer Mode"""
print('[back] dev_mode')
ibs = back.ibs # NOQA
front = back.front # NOQA
# import IPython
# IPython.embed()
ut.embed()
[docs] @blocking_slot()
def dev_cls(back):
""" Help -> Developer Mode"""
print('[back] dev_cls')
print('\n'.join([''] * 100))
if back.ibs is not None:
back.ibs.reset_table_cache()
back.refresh_state()
from wbia.plottool import draw_func2 as df2
df2.update()
[docs] @slot_()
@backreport
def dev_export_annotations(back):
ibs = back.ibs
ibs.export_to_xml()
[docs] def start_web_server_parallel(back, browser=True):
import wbia
ibs = back.ibs
if back.web_ibs is None:
print('[guiback] Starting web service')
# back.web_ibs = wbia.opendb_in_background(dbdir=ibs.get_dbdir(), web=True, browser=browser)
back.web_ibs = wbia.opendb_bg_web(
dbdir=ibs.get_dbdir(), web=True, browser=browser, start_job_queue=False
)
print('[guiback] Web service started')
else:
print('[guiback] CANNOT START WEB SERVER: WEB INSTANCE ALREADY RUNNING')
[docs] def kill_web_server_parallel(back):
if back.web_ibs is not None:
print('[guiback] Stopping web service')
# back.web_ibs.terminate()
back.web_ibs.terminate2()
back.web_ibs = None
else:
print('[guiback] CANNOT TERMINATE WEB SERVER: WEB INSTANCE NOT RUNNING')
[docs] @blocking_slot()
def fix_and_clean_database(back):
""" Help -> Fix/Clean Database """
print('[back] Fix/Clean Database')
back.ibs.fix_and_clean_database()
back.front.update_tables()
[docs] @blocking_slot()
def run_integrity_checks(back):
back.ibs.run_integrity_checks()
# --------------------------------------------------------------------------
# File Slots
# --------------------------------------------------------------------------
[docs] @blocking_slot()
def new_database(back, new_dbdir=None):
""" File -> New Database
Args:
new_dbdir (None): (default = None)
CommandLine:
python -m wbia.gui.guiback new_database --show
Example:
>>> # DISABLE_DOCTEST
>>> from wbia.gui.guiback import * # NOQA
>>> import wbia
>>> #back = testdata_guiback(defaultdb='testdb1')
>>> back = testdata_guiback(defaultdb=None)
>>> dbdir = None
>>> result = back.new_database(dbdir)
>>> ut.quit_if_noshow()
>>> gt.qtapp_loop(qwin=back.front, freq=10)
"""
if new_dbdir is None:
old = False
if old:
new_dbname = back.user_input(
msg='What do you want to name the new database?', title='New Database'
)
if new_dbname is None or len(new_dbname) == 0:
print('Abort new database. new_dbname=%r' % new_dbname)
return
new_dbdir_options = ['Choose Directory', 'My Work Dir']
reply = back.user_option(
msg='Where should I put the new database?',
title='Import Images',
options=new_dbdir_options,
default=new_dbdir_options[1],
use_cache=False,
)
if reply == 'Choose Directory':
print('[back] new_database(): SELECT A DIRECTORY')
putdir = gt.select_directory(
'Select new database directory',
other_sidebar_dpaths=[back.get_work_directory()],
)
elif reply == 'My Work Dir':
putdir = back.get_work_directory()
else:
print('Abort new database')
return
new_dbdir = join(putdir, new_dbname)
if not exists(putdir):
raise ValueError('Directory %r does not exist.' % putdir)
if exists(new_dbdir):
raise ValueError('New DB %r already exists.' % new_dbdir)
ut.ensuredir(new_dbdir)
print('[back] new_database(new_dbdir=%r)' % new_dbdir)
back.open_database(dbdir=new_dbdir)
else:
from wbia.guitool.__PYQT__.QtCore import Qt # NOQA
from wbia.guitool.__PYQT__ import QtGui # NOQA
dlg = NewDatabaseWidget.as_dialog(
back.front, back=back, on_chosen=back.open_database, mode='new'
)
dlg.exec_()
[docs] @blocking_slot()
def open_database(back, dbdir=None):
"""
File -> Open Database
Args:
dbdir (None): (default = None)
CommandLine:
python -m wbia.gui.guiback --test-open_database
Example:
>>> # xdoctest: +REQUIRES(--gui)
>>> from wbia.gui.guiback import * # NOQA
>>> back = testdata_guiback(defaultdb='testdb1')
>>> testdb0 = sysres.db_to_dbdir('testdb0')
>>> testdb1 = sysres.db_to_dbdir('testdb1')
>>> print('[TEST] TEST_OPEN_DATABASE testdb1=%r' % testdb1)
>>> back.open_database(testdb1)
>>> print('[TEST] TEST_OPEN_DATABASE testdb0=%r' % testdb0)
>>> back.open_database(testdb0)
>>> import wbia
>>> #dbdir = join(wbia.sysres.get_workdir(), 'PZ_MTEST', '_ibsdb')
>>> dbdir = None
>>> result = back.open_database(dbdir)
>>> print(result)
"""
if dbdir is None:
print('[back] new_database(): SELECT A DIRECTORY')
# director
dbdir = gt.select_directory(
'Open a database directory',
other_sidebar_dpaths=[back.get_work_directory()],
)
if dbdir is None:
return
print('[back] open_database(dbdir=%r)' % dbdir)
with ut.Indenter(lbl=' [opendb]'):
try:
# should this use wbia.opendb? probably. at least it should be
# be request IBEISControl
# ibs = IBEISControl.IBEISController(dbdir=dbdir)
ibs = IBEISControl.request_IBEISController(dbdir=dbdir)
back.connect_wbia_control(ibs)
except Exception as ex:
ut.printex(ex, 'caught Exception while opening database')
raise
else:
sysres.set_default_dbdir(dbdir)
[docs] @blocking_slot()
def export_database_as_csv(back):
""" File -> Export Database """
print('[back] export_database_as_csv')
dump_dir = join(back.ibs.get_dbdir(), 'CSV_DUMP')
ut.ensuredir(dump_dir)
ut.view_directory(dump_dir)
back.ibs.dump_database_csv()
[docs] @blocking_slot()
def backup_database(back):
""" File -> Backup Database"""
print('[back] backup_database')
back.ibs.backup_database()
[docs] @blocking_slot()
def make_database_duplicate(back):
""" File -> Copy Database"""
print('[back] make_database_duplicate')
def on_chosen(new_dbdir):
back.ibs.copy_database(new_dbdir)
dlg = NewDatabaseWidget.as_dialog(
back.front, back=back, on_chosen=on_chosen, mode='copy'
)
dlg.exec_()
[docs] @blocking_slot()
def import_images_from_file(
back, gpath_list=None, refresh=True, as_annots=False, clock_offset=False
):
r"""
File -> Import Images From File
Example
>>> # xdoctest: +REQUIRES(--gui)
>>> print('[TEST] GET_TEST_IMAGE_PATHS')
>>> # The test api returns a list of interesting chip indexes
>>> mode = 'FILE'
>>> if mode == 'FILE':
>>> gpath_list = list(map(utool.unixpath, grabdata.get_test_gpaths()))
>>> #
>>> # else:
>>> # dir_ = utool.truepath(join(sysres.get_workdir(), 'PZ_MOTHERS/images'))
>>> # gpath_list = utool.list_images(dir_, fullpath=True, recursive=True)[::4]
>>> print('[TEST] IMPORT IMAGES FROM FILE\n * gpath_list=%r' % gpath_list)
>>> gid_list = back.import_images(gpath_list=gpath_list)
>>> thumbtup_list = ibs.get_image_thumbtup(gid_list)
>>> imgpath_list = [tup[1] for tup in thumbtup_list]
>>> gpath_list2 = ibs.get_image_paths(gid_list)
>>> for path in gpath_list2:
>>> assert path in imgpath_list, "Imported Image not in db, path=%r" % path
>>> elif mode == 'DIR':
>>> dir_ = grabdata.get_testdata_dir()
>>> print('[TEST] IMPORT IMAGES FROM DIR\n * dir_=%r' % dir_)
>>> gid_list = back.import_images(dir_=dir_)
>>> else:
>>> raise AssertionError('unknown mode=%r' % mode)
>>> print('[TEST] * len(gid_list)=%r' % len(gid_list))
"""
print('[back] import_images_from_file')
if back.ibs is None:
raise ValueError('back.ibs is None! must open IBEIS database first')
if gpath_list is None:
gpath_list = gt.select_images('Select image files to import')
ibs = back.ibs
gid_list = back.ibs.add_images(
gpath_list,
as_annots=as_annots,
location_for_names=ibs.cfg.other_cfg.location_for_names,
)
back._process_new_images(refresh, gid_list, clock_offset=clock_offset)
return gid_list
[docs] @blocking_slot()
def import_button_click(back):
msg = 'How do you want to import images?'
ans = back.user_option(
msg=msg,
title='Import Images',
options=[
'Directory',
'Files',
'Smart XML',
'Encounters (1)',
'Encounters (2)',
],
use_cache=False,
default='Directory',
)
if ans == 'Directory':
back.import_images_from_dir()
elif ans == 'Files':
back.import_images_from_file()
elif ans == 'Smart XML':
back.import_images_from_dir_with_smart()
elif ans == 'Encounters (1)':
back.import_images_from_encounters_1()
elif ans == 'Encounters (2)':
back.import_images_from_encounters_2()
elif ans is None:
pass
else:
raise Exception('Unknown anser=%r' % (ans,))
[docs] @blocking_slot()
def import_images_from_dir(
back,
dir_=None,
size_filter=None,
refresh=True,
clock_offset=False,
return_dir=False,
defaultdir=None,
):
""" File -> Import Images From Directory"""
print('[back] import_images_from_dir')
if dir_ is None:
dir_ = gt.select_directory(
'Select directory with images in it', directory=defaultdir
)
if dir_ is None:
return
gpath_list = ut.list_images(dir_, fullpath=True, recursive=True)
if size_filter is not None:
raise NotImplementedError('Can someone implement the size filter?')
ibs = back.ibs
gid_list = back.ibs.add_images(
gpath_list, location_for_names=ibs.cfg.other_cfg.location_for_names
)
back._process_new_images(refresh, gid_list, clock_offset=clock_offset)
if return_dir:
return gid_list, dir_
else:
return gid_list
[docs] @blocking_slot()
def import_images_from_dir_with_smart(
back,
dir_=None,
size_filter=None,
refresh=True,
smart_xml_fpath=None,
defaultdir=None,
):
""" File -> Import Images From Directory with smart
Args:
dir_ (None): (default = None)
size_filter (None): (default = None)
refresh (bool): (default = True)
Returns:
list: gid_list
CommandLine:
python -m wbia.gui.guiback --test-import_images_from_dir_with_smart --show
python -m wbia.gui.guiback --test-import_images_from_dir_with_smart --show --auto
Example:
>>> # DEV_GUI_DOCTEST
>>> # xdoctest: +REQUIRES(--gui)
>>> from wbia.gui.guiback import * # NOQA
>>> back = testdata_guiback(defaultdb='freshsmart_test', delete_ibsdir=True, allow_newdir=True)
>>> ibs = back.ibs
>>> defaultdir = ut.truepath('~/lewa-desktop/Desktop/GZ_Foal_Patrol_22_06_2015')
>>> dir_ = None if not ut.get_argflag('--auto') else join(defaultdir, 'Photos')
>>> smart_xml_fpath = None if not ut.get_argflag('--auto') else join(defaultdir, 'Patrols', 'LWC_000526LEWA_GZ_FOAL_PATROL.xml')
>>> size_filter = None
>>> refresh = True
>>> gid_list = back.import_images_from_dir_with_smart(dir_, size_filter, refresh, defaultdir=defaultdir, smart_xml_fpath=smart_xml_fpath)
>>> result = ('gid_list = %s' % (str(gid_list),))
>>> print(result)
>>> ut.quit_if_noshow()
>>> gt.qtapp_loop(back.mainwin, frequency=100)
"""
print('[back] import_images_from_dir_with_smart')
gid_list, add_dir_ = back.import_images_from_dir(
dir_=dir_,
size_filter=size_filter,
refresh=False,
clock_offset=False,
return_dir=True,
defaultdir=defaultdir,
)
back._group_images_with_smartxml(
gid_list,
refresh=refresh,
smart_xml_fpath=smart_xml_fpath,
defaultdir=dirname(add_dir_),
)
def _group_images_with_smartxml(
back, gid_list, refresh=True, smart_xml_fpath=None, defaultdir=None
):
"""
Clusters the newly imported images with smart xml file
"""
if gid_list is not None and len(gid_list) > 0:
if smart_xml_fpath is None:
name_filter = 'XML Files (*.xml)'
xml_path_list = gt.select_files(
caption='Select Patrol XML File:',
directory=defaultdir,
name_filter=name_filter,
single_file=True,
)
try:
assert len(xml_path_list) == 1, 'Must specity one Patrol XML file'
smart_xml_fpath = xml_path_list[0]
assert (
len(smart_xml_fpath) > 0
), 'Must specity a valid Patrol XML file'
except AssertionError as e:
back.ibs.delete_images(gid_list)
print(
(
'[back] ERROR: Parsing Patrol XML file failed, '
'rolling back by deleting %d images...'
)
% (len(gid_list,))
)
raise e
back.ibs.compute_occurrences_smart(gid_list, smart_xml_fpath)
if refresh:
back.update_special_imagesets_()
# back.front.update_tables([gh.IMAGESET_TABLE])
back.front.update_tables()
def _process_new_images(back, refresh, gid_list, clock_offset=False):
if refresh:
back.update_special_imagesets_()
back.front.update_tables([gh.IMAGE_TABLE, gh.IMAGESET_TABLE])
if clock_offset:
co_wgt = clock_offset_gui.ClockOffsetWidget(back.ibs, gid_list)
co_wgt.show()
return gid_list
[docs] @blocking_slot()
def import_images_from_encounters(
back,
level=1,
dir_list=None,
size_filter=None,
refresh=True,
clock_offset=False,
return_dir=False,
defaultdir=None,
):
import os
""" File -> Import Images From Encounters"""
print('[back] import_images_from_encounters')
assert level in [1, 2]
if dir_list is None:
if level == 1:
prompt = 'Select folder(s) of encounter(s) (1 level - folders with only images)'
if level == 2:
prompt = 'Select folder(s) of encounter(s) (2 levels - folders of folders with only images)'
dir_list = gt.select_directories(prompt, directory=defaultdir)
if dir_list is None or len(dir_list) == 0:
return
# We need to check that the first directory is not a subdirectory of the others
if len(dir_list) >= 2:
subdir1 = dir_list[0]
subdir2, _ = os.path.split(dir_list[1])
if subdir1 == subdir2:
dir_list = dir_list[1:]
# Check the folders for invalid values
invalid_list = []
warning_set = set([])
for index, dir_ in enumerate(dir_list):
for root, subdirs, files in os.walk(dir_):
images = ut.list_images(root, recursive=False)
try:
# Assert structure
if level == 1:
assert len(subdirs) == 0
assert len(images) > 0
if level == 2:
assert len(images) == 0
assert len(subdirs) > 0
# Check subdirectories for level 1 structure
for subdir in subdirs:
for root_, subdirs_, files_ in os.walk(subdir):
images_ = ut.list_images(root, recursive=False)
try:
assert len(subdirs_) == 0
assert len(images_) > 0
except AssertionError:
invalid_list.append(join(root, root_))
# Combined a warning set of non-image files
for file_ in set(files_) - set(images_):
if len(file_.strip('.')) == 0:
warning_set.add(join(root, file_))
# Only look at the root path
break
except AssertionError:
invalid_list.append(root)
# Combined a warning set of non-image files
for file_ in set(files) - set(images):
if len(file_.strip('.')) == 0:
warning_set.add(file_)
# Only look at the root path
break
# If invalid, give user input information
invalid = len(invalid_list) > 0
if invalid:
if level == 1:
raise IOError(
'''
[guiback] The following encounter folder structures (1 level) are not valid: %r
[guiback] * The selected folders must contain images
[guiback] * The selected folders must NOT contain any sub-folders
[guiback] * The selected folders must NOT be empty
'''
% (invalid_list,)
)
if level == 2:
raise IOError(
'''
[guiback] The following encounter folder structures (2 levels) are not valid: %r
[guiback] * The selected folders must NOT contain images
[guiback] * The selected folders must contain sub-folders
[guiback] * The selected folders must NOT be empty
[guiback] * The sub-folders in the selected folders must contain images
[guiback] * The sub-folders in the selected folders must NOT contain any sub-folders
[guiback] * The sub-folders in the selected folders must NOT be empty
'''
% (invalid_list,)
)
print('[guiback] Encounters are valid, continue with import')
# print any warning files
if len(warning_set) > 0:
warning_list = list(sorted(warning_set))
args = (warning_list,)
print(
'[guiback] WARNING: Some files in the encounters will not be imported: %r'
% args
)
# Compile the list of images now that the encounter's structure have been verified
gpath_list = []
for dir_ in dir_list:
gpath_list_ = ut.list_images(dir_, fullpath=True, recursive=True)
gpath_list += gpath_list_
# Check that the encounters behave as expected
if size_filter is not None:
raise NotImplementedError('Can someone implement the size filter?')
# Add images to ibs
ibs = back.ibs
gid_list = back.ibs.add_images(
gpath_list, location_for_names=ibs.cfg.other_cfg.location_for_names
)
# Add imagesets for newly added images
imageset_text_list = []
for gpath in gpath_list:
base, gname = os.path.split(gpath)
base, level1 = os.path.split(base)
if level == 1:
imageset_text = level1
if level == 2:
base, level2 = os.path.split(base)
imageset_text = '%s (+) %s' % (level2, level1,)
imageset_text_list.append(imageset_text)
ibs.set_image_imagesettext(gid_list, imageset_text_list)
# Refresh GUI and return
back._process_new_images(refresh, gid_list, clock_offset=clock_offset)
if return_dir:
return gid_list, dir_list
else:
return gid_list
[docs] @blocking_slot()
def import_images_from_encounters_1(
back, dir_list=None, size_filter=None, refresh=True, defaultdir=None
):
""" File -> Import Images From Encounters (1 level)
Args:
dir_ (None): (default = None)
size_filter (None): (default = None)
refresh (bool): (default = True)
Returns:
list: gid_list
"""
print('[back] import_images_from_encounters_1')
gid_list, add_dir_ = back.import_images_from_encounters(
level=1,
dir_list=dir_list,
size_filter=size_filter,
refresh=False,
clock_offset=False,
return_dir=True,
defaultdir=defaultdir,
)
[docs] @blocking_slot()
def import_images_from_encounters_2(
back, dir_list=None, size_filter=None, refresh=True, defaultdir=None
):
""" File -> Import Images From Encounters (2 levels)
Args:
dir_ (None): (default = None)
size_filter (None): (default = None)
refresh (bool): (default = True)
Returns:
list: gid_list
"""
print('[back] import_images_from_encounters_2')
gid_list, add_dir_ = back.import_images_from_encounters(
level=2,
dir_list=dir_list,
size_filter=size_filter,
refresh=False,
clock_offset=False,
return_dir=True,
defaultdir=defaultdir,
)
[docs] @blocking_slot()
def import_images_as_annots_from_file(back, gpath_list=None, refresh=True):
return back.import_images_from_file(gpath_list=None, refresh=True, as_annots=True)
[docs] @slot_()
@backreport
def localize_images(back):
""" File -> Localize Images """
print('[back] localize_images')
back.ibs.localize_images()
[docs] @slot_()
def quit(back):
""" File -> Quit"""
print('[back] ')
# back.cleanup()
gt.exit_application()
# --------------------------------------------------------------------------
# Helper functions
# --------------------------------------------------------------------------
[docs] def user_info(back, **kwargs):
return gt.user_info(parent=back.front, **kwargs)
[docs] def user_warning(back, title='Warning', **kwargs):
return gt.user_info(parent=back.front, title=title, **kwargs)
[docs] def user_input(back, msg='user input', **kwargs):
return gt.user_input(parent=back.front, msg=msg, **kwargs)
[docs] def user_option(back, **kwargs):
if kwargs.get('config', None) is not None:
# options, config, msg, title
dlg = gt.ConfigConfirmWidget.as_dialog(**kwargs)
# dlg.resize(700, 500)
confirm_widget = dlg.widget
dlg.exec_()
return confirm_widget.confirm_option, confirm_widget.config
# return gt.user_option(parent=back.front, **kwargs)
else:
return gt.user_option(parent=back.front, **kwargs)
[docs] def are_you_sure(
back,
use_msg=None,
title='Confirmation',
default=None,
action=None,
detailed_msg=None,
):
""" Prompt user for conformation before changing something """
if action is None:
default_msg = 'Are you sure?'
else:
default_msg = 'Are you sure you want to %s?' % (action,)
msg = default_msg if use_msg is None else use_msg
print('[back] Asking User if sure')
print('[back] title = %s' % (title,))
print('[back] msg =\n%s' % (msg,))
print('[back] detailed_msg =\n%s' % (detailed_msg,))
if ut.get_argflag('-y') or ut.get_argflag('--yes'):
# DONT ASK WHEN SPECIFIED
return True
ans = back.user_option(
msg=msg,
title=title,
options=['Yes', 'No'],
use_cache=False,
default=default,
detailed_msg=detailed_msg,
)
print('[back] User answered: %r' % (ans,))
return ans == 'Yes'
[docs] def get_work_directory(back):
return sysres.get_workdir()
def _eidfromkw(back, kwargs):
if 'imgsetid' not in kwargs:
imgsetid = back.get_selected_imgsetid()
else:
imgsetid = kwargs['imgsetid']
return imgsetid
[docs] def contains_special_imagesets(back, imgsetid_list):
isspecial_list = back.ibs.is_special_imageset(imgsetid_list)
return any(isspecial_list)
[docs] def display_special_imagesets_error(back):
back.user_warning(msg='Contains special imagesets')
[docs] @slot_()
def override_all_annotation_species(back, aids=None, gids=None):
"""
Give the user a dialog box asking to input a species
"""
if aids is None:
if gids is not None:
aid_list = list(ub.flatten(back.ibs.images(gids=gids).aids))
else:
aid_list = back.ibs.get_valid_aids()
else:
aid_list = aids
resp = gt.user_input(
title='edit species',
msg='Override species for {} annots'.format(len(aid_list)),
text='',
)
if resp is not None:
print('override_all_annotation_species. resp = %r' % (resp,))
species_rowid = back.ibs.add_species(resp)
use_msg = 'Are you sure you want to change %d annotations species to %r?' % (
len(aid_list),
resp,
)
if back.are_you_sure(use_msg=use_msg):
print('performing override')
back.ibs.set_annot_species_rowids(
aid_list, [species_rowid] * len(aid_list)
)
# FIXME: api-cache is broken here too
back.ibs.reset_table_cache()
[docs] @blocking_slot()
def update_species_nice_name(back):
from wbia.control.manual_species_funcs import _convert_species_nice_to_code
ibs = back.ibs
species_text = back.get_selected_species()
if species_text in [const.UNKNOWN, '']:
back.user_warning(msg='Cannot rename this species...')
raise guiexcept.UserCancel
species_rowid = ibs.get_species_rowids_from_text(species_text)
species_nice = ibs.get_species_nice(species_rowid)
new_species_nice = back.user_input(
msg='Rename species\n Name: %r \n Tag: %r'
% (species_nice, species_text),
title='Rename Species',
)
if new_species_nice is not None:
species_rowid = [species_rowid]
new_species_nice = [new_species_nice]
species_code = _convert_species_nice_to_code(new_species_nice)
ibs._set_species_nice(species_rowid, new_species_nice)
ibs._set_species_code(species_rowid, species_code)
back.ibswgt.update_species_available(
reselect=True, reselect_new_name=new_species_nice[0]
)
[docs] @blocking_slot()
def delete_selected_species(back):
ibs = back.ibs
species_text = back.get_selected_species()
if species_text in [const.UNKNOWN, '']:
back.user_warning(msg='Cannot delete this species...')
raise guiexcept.UserCancel
species_rowid = ibs.get_species_rowids_from_text(species_text)
species_nice = ibs.get_species_nice(species_rowid)
msg_str = (
'You are about to delete species\n Name: %r \n '
+ 'Tag: %r\n\nDo you wish to continue?\nAll annotations '
+ 'with this species will be set to unknown.'
)
msg_str = msg_str % (species_nice, species_text,)
confirm_kw = dict(use_msg=msg_str, title='Delete Selected Species?', default='No')
if not back.are_you_sure(**confirm_kw):
raise guiexcept.UserCancel
ibs.delete_species([species_rowid])
back.ibswgt.update_species_available(deleting=True)
[docs] @slot_()
def set_exemplars_from_quality_and_viewpoint_(back):
exemplars_per_view = back.ibs.cfg.other_cfg.exemplars_per_view
imgsetid = back.get_selected_imgsetid()
print('set_exemplars_from_quality_and_viewpoint, imgsetid=%r' % (imgsetid,))
HACK = back.ibs.cfg.other_cfg.enable_custom_filter
assert not HACK, 'enable_custom_filter is no longer supported'
back.ibs.set_exemplars_from_quality_and_viewpoint(
imgsetid=imgsetid, exemplars_per_view=exemplars_per_view
)
[docs] @slot_()
def batch_rename_consecutive_via_species_(back):
# imgsetid = back.get_selected_imgsetid()
# back.ibs.batch_rename_consecutive_via_species(imgsetid=imgsetid)
imgsetid = None
print('batch_rename_consecutive_via_species, imgsetid=%r' % (imgsetid,))
location_text = back.ibs.cfg.other_cfg.location_for_names
back.ibs.batch_rename_consecutive_via_species(
imgsetid=imgsetid, location_text=location_text
)
[docs] @slot_()
def run_tests(back):
from wbia.tests import run_tests
run_tests.run_tests()
[docs] @slot_()
def run_utool_tests(back):
import utool.tests.run_tests
utool.tests.run_tests.run_tests()
[docs] @slot_()
def run_vtool_tests(back):
import vtool.tests.run_tests
vtool.tests.run_tests.run_tests()
[docs] @slot_()
def assert_modules(back):
from wbia.tests import assert_modules
detailed_msg = assert_modules.assert_modules()
gt.msgbox(msg='Running checks', title='Module Checks', detailed_msg=detailed_msg)
[docs] @slot_()
def display_dbinfo(back):
r"""
CommandLine:
python -m wbia.gui.guiback --test-display_dbinfo
Example:
>>> # DISABLE_DOCTEST
>>> from wbia.gui.guiback import * # NOQA
>>> back = testdata_guiback()
>>> result = back.display_dbinfo()
>>> print(result)
"""
dbinfo = back.ibs.get_dbinfo_str()
print(dbinfo)
gt.msgbox(msg=back.ibs.get_infostr(), title='DBInfo', detailed_msg=dbinfo)
[docs] @slot_()
def show_about_message(back):
import wbia
version = wbia.__version__
about_msg = (
'IBEIS version %s\nImage Based Ecological Information System\nhttp://wbia.org/'
% (version,)
)
gt.msgbox(msg=about_msg, title='About')
[docs] @slot_()
def take_screenshot(back):
""" dev command only """
print('[back] TAKING SCREENSHOT')
from wbia.guitool.__PYQT__.QtGui import QPixmap
# screengrab_fpath = ut.truepath('~/latex/wbia_userguide/figures/filemenu.jpg')
# Find the focused window
app = gt.get_qtapp()
widget = app.focusWidget()
if widget is None or widget == 0:
widget = back.mainwin
window = widget.window()
win_title = window.windowTitle()
window_id = window.winId()
# Resolve screengrab path
screengrab_dpath = ut.get_argval('--screengrab_dpath', type_=str, default=None)
screengrab_fname = ut.get_argval('--screengrab_fname', type_=str, default=None)
if screengrab_fname is None:
screengrab_fname = win_title
if screengrab_dpath is None:
screengrab_dpath = './screenshots'
ut.ensuredir(screengrab_dpath)
screengrab_dpath = ut.truepath(screengrab_dpath)
screengrab_fname = ut.sanitize_filename(screengrab_fname)
fpath_base = join(screengrab_dpath, screengrab_fname)
fpath_fmt = fpath_base + '_%d.jpg'
screengrab_fpath = ut.get_nonconflicting_path(fpath_fmt)
# Grab image in window
screenimg = QPixmap.grabWindow(window_id)
# Save image to disk
screenimg.save(screengrab_fpath, 'jpg')
print('saved screengrab to %r' % (screengrab_fpath,))
if ut.get_argflag('--diskshow'):
ut.startfile(screengrab_fpath)
[docs] @slot_()
def make_qt_graph_interface(back):
from wbia.viz import viz_graph2
imgsetid = back.get_selected_imgsetid()
aids = back.ibs.get_valid_aids(imgsetid=imgsetid)
if len(aids) == 0:
raise AssertionError('Choose an imageset with annotations')
back.graph_iden_win = viz_graph2.make_qt_graph_interface(aids=aids, ibs=back.ibs)
[docs] @slot_()
def reconnect_controller(back):
back.connect_wbia_control(back.ibs)
[docs] @slot_()
def browse_wildbook(back):
wb_base_url = back.ibs.get_wildbook_base_url()
ut.get_prefered_browser().open(wb_base_url)
[docs] @slot_()
def install_wildbook(back):
import wbia.control.wildbook_manager as wb_man
wb_man.install_wildbook()
[docs] @slot_()
def startup_wildbook(back):
import wbia.control.wildbook_manager as wb_man
back.wb_server_running = True
wb_man.startup_wildbook_server()
[docs] @slot_()
def shutdown_wildbook(back):
import wbia.control.wildbook_manager as wb_man
wb_man.shutdown_wildbook_server()
back.wb_server_running = False
[docs] @slot_()
def force_wildbook_namechange(back):
back.ibs.wildbook_signal_annot_name_changes()
[docs] @slot_()
def set_workdir(back):
import wbia
wbia.sysres.set_workdir(work_dir=None, allow_gui=True)
[docs] @slot_()
def ensure_demodata(back):
from wbia import demodata
demodata.ensure_demodata()
[docs] @slot_()
def launch_ipy_notebook(back):
from wbia.templates import generate_notebook
generate_notebook.autogen_ipynb(back.ibs, launch=True)
[docs] @slot_()
def update_source_install(back):
import wbia
from os.path import dirname
repo_path = dirname(ut.truepath(ut.get_modpath(wbia, prefer_pkg=True)))
with ut.ChdirContext(repo_path):
command = ut.python_executable() + ' super_setup.py pull'
ut.cmd(command)
print('Done updating source install')
[docs] @slot_()
def toggle_output_widget(back):
current = back.front.outputLog.isVisible()
back.front.outputLog.setVisible(not current)
[docs]def testdata_guiback(defaultdb='testdb2', **kwargs):
import wbia
print('testdata guiback')
if defaultdb is None:
back = wbia.main_module._init_gui()
# back = MainWindowBackend()
else:
main_locals = wbia.main(defaultdb=defaultdb, **kwargs)
back = main_locals['back']
return back
if __name__ == '__main__':
"""
CommandLine:
xdoctest -m wbia.gui.guiback
"""
import xdoctest
xdoctest.doctest_module(__file__)