Beispiel #1
0
def checkfreq(freqlike_, count):
    # checks frequency of param, also handles the case where it is specified
    # as a bool. does not trigger on 0
    if ut.is_int(freqlike_):
        return (count % freqlike_) == (freqlike_ - 1)
    else:
        return freqlike_ is True
Beispiel #2
0
 def set_sorting(model, col_sort_index=None, order=Qt.DescendingOrder):
     model.sortreverse = order == Qt.DescendingOrder
     if col_sort_index is not None:
         if utool.is_str(col_sort_index):
             col_sort_index = model.col_name_list.index(col_sort_index)
         assert utool.is_int(col_sort_index), "sort by an index not %r" % type(col_sort_index)
         model.sortcolumn = col_sort_index
         assert model.sortcolumn < len(model.col_name_list), "outofbounds"
         print("sortcolumn: %r" % model.sortcolumn)
Beispiel #3
0
def _type_from_data(data):
    """ If type is not given make an educated guess """
    if utool.is_bool(data) or data == 'True' or data == 'False':
        return bool
    elif utool.is_int(data):
        return int
    elif utool.is_float(data):
        return float
    else:
        return str
Beispiel #4
0
def _type_from_data(data):
    """ If type is not given make an educated guess """
    if utool.is_bool(data) or data == 'True' or data == 'False':
        return bool
    elif utool.is_int(data):
        return int
    elif utool.is_float(data):
        return float
    else:
        return str
 def set_sorting(model, col_sort_index=None, order=Qt.DescendingOrder):
     model.sortreverse = order == Qt.DescendingOrder
     if col_sort_index is not None:
         if utool.is_str(col_sort_index):
             col_sort_index = model.col_name_list.index(col_sort_index)
         assert utool.is_int(
             col_sort_index
         ), 'sort by an index not %r' % type(col_sort_index)
         model.sortcolumn = col_sort_index
         assert model.sortcolumn < len(model.col_name_list), 'outofbounds'
         print('sortcolumn: %r' % model.sortcolumn)
Beispiel #6
0
def cast_into_qt(data):
    """
    Casts python data into a representation suitable for QT (usually a string)
    """
    if SIMPLE_CASTING:
        if ut.is_str(data):
            return __STR__(data)
        elif ut.is_float(data):
            # qnumber = QString.number(float(data), format='g', precision=8)
            return locale_float(data)
        elif ut.is_bool(data):
            return bool(data)
        elif ut.is_int(data):
            return int(data)
        elif isinstance(data, uuid.UUID):
            return __STR__(data)
        elif ut.isiterable(data):
            return ', '.join(map(__STR__, data))
        else:
            return __STR__(data)
    if ut.is_str(data):
        return __STR__(data)
    elif ut.is_float(data):
        # qnumber = QString.number(float(data), format='g', precision=8)
        return locale_float(data)
    elif ut.is_bool(data):
        return bool(data)
    elif ut.is_int(data):
        return int(data)
    elif isinstance(data, uuid.UUID):
        return __STR__(data)
    elif ut.isiterable(data):
        return ', '.join(map(__STR__, data))
    elif data is None:
        return 'None'
    else:
        return 'Unknown qtype: %r for data=%r' % (type(data), data)
Beispiel #7
0
def cast_into_qt(data):
    """
    Casts python data into a representation suitable for QT (usually a string)
    """
    if SIMPLE_CASTING:
        if ut.is_str(data):
            return __STR__(data)
        elif ut.is_float(data):
            #qnumber = QString.number(float(data), format='g', precision=8)
            return locale_float(data)
        elif ut.is_bool(data):
            return bool(data)
        elif  ut.is_int(data):
            return int(data)
        elif isinstance(data, uuid.UUID):
            return __STR__(data)
        elif ut.isiterable(data):
            return ', '.join(map(__STR__, data))
        else:
            return __STR__(data)
    if ut.is_str(data):
        return __STR__(data)
    elif ut.is_float(data):
        #qnumber = QString.number(float(data), format='g', precision=8)
        return locale_float(data)
    elif ut.is_bool(data):
        return bool(data)
    elif  ut.is_int(data):
        return int(data)
    elif isinstance(data, uuid.UUID):
        return __STR__(data)
    elif ut.isiterable(data):
        return ', '.join(map(__STR__, data))
    elif data is None:
        return 'None'
    else:
        return 'Unknown qtype: %r for data=%r' % (type(data), data)
Beispiel #8
0
def testdata_expanded_aids(defaultdb=None,
                           a=None,
                           ibs=None,
                           default_qaids=None,
                           default_daids=None,
                           qaid_override=None,
                           daid_override=None,
                           return_annot_info=False,
                           verbose=None,
                           use_cache=None):
    r"""
    Args:
        default_qaids (list): (default = [1])
        default_daids (str): (default = 'all')
        defaultdb (str): (default = 'testdb1')
        ibs (IBEISController):  ibeis controller object(default = None)
        verbose (bool):  verbosity flag(default = False)
        return_annot_info (bool): (default = False)

    Returns:
        ibs, qaid_list, daid_list, annot_info:

    CommandLine:
        python -m ibeis.init.main_helpers testdata_expanded_aids
        python -m ibeis.init.main_helpers testdata_expanded_aids --db PZ_MTEST --acfg default:index=0:25 --verbose-testdata
        python -m ibeis.init.main_helpers testdata_expanded_aids --db PZ_MTEST --qaid 3
        python -m ibeis.init.main_helpers testdata_expanded_aids --db GZ_ALL --acfg ctrl --verbose-testdata

    Example:
        >>> # ENABLE_DOCTEST
        >>> from ibeis.init.main_helpers import *  # NOQA
        >>> import ibeis
        >>> from ibeis.expt import annotation_configs
        >>> ibs, qaid_list, daid_list, aidcfg = testdata_expanded_aids(return_annot_info=True)
        >>> print('Printing annot config')
        >>> annotation_configs.print_acfg(aidcfg)
        >>> print('Printing annotconfig stats')
        >>> ibs.print_annotconfig_stats(qaid_list, daid_list)
        >>> print('Combined annotconfig stats')
        >>> ibs.print_annot_stats(qaid_list + daid_list, viewcode_isect=True)
        >>> print('qaid_list = %r' % (qaid_list,))
    """
    if verbose is None:
        verbose = 1

    if verbose:
        print('[main_helpers] testdata_expanded_aids')

    default_qaids = ut.get_argval(('--qaid', '--qaid-override'),
                                  type_=list,
                                  default=default_qaids)
    if default_qaids is None:
        default_qaids = [1]

    if defaultdb is None:
        defaultdb = 'testdb1'
    import ibeis
    if ibs is None:
        ibs = ibeis.opendb(defaultdb=defaultdb)

    # TODO: rectify command line with function arguments
    from ibeis.expt import experiment_helpers
    _specified2 = True
    if a is None:
        _specified2 = False
        a = ['default']
    if isinstance(a, six.string_types):
        a = [a]
    aidcfg_name_list, _specified = ut.get_argval(('--aidcfg', '--acfg', '-a'),
                                                 type_=list,
                                                 default=a,
                                                 return_specified=True)

    if not _specified:
        # Allow a to be specified an explicit default
        if len(a) == 2:
            qaids, daids = a
            if ut.is_int(qaids[0]) and ut.is_int(daids[0]):
                if return_annot_info:
                    return ibs, qaids, daids, None
                else:
                    return ibs, qaids, daids

    acfg_list, expanded_aids_list = experiment_helpers.get_annotcfg_list(
        ibs,
        aidcfg_name_list,
        qaid_override=qaid_override,
        use_cache=use_cache,
        daid_override=daid_override,
        verbose=max(0, verbose - 1))

    #aidcfg = old_main_helpers.get_commandline_aidcfg()
    assert len(acfg_list) == 1, (
        ('multiple acfgs specified, but this function'
         'is built to return only 1. len(acfg_list)=%r') % (len(acfg_list), ))
    aidcfg = acfg_list[0]

    qaid_list, daid_list = expanded_aids_list[0]

    if not (_specified or _specified2):
        # hack
        if default_qaids is not None and qaid_override is None:
            qaid_list = default_qaids
        if default_daids is not None and daid_override is None:
            daid_list = default_daids

    if ut.VERYVERBOSE:
        ibs.print_annotconfig_stats(qaid_list, daid_list)
        #ibeis.other.dbinfo.print_qd_info(ibs, qaid_list, daid_list, verbose=True)
    if return_annot_info:
        return ibs, qaid_list, daid_list, aidcfg
    else:
        return ibs, qaid_list, daid_list
Beispiel #9
0
def batch_iterator(model, X, y, randomize_batch_order=False, augment_on=False,
                   X_is_cv2_native=True, verbose=None,
                   lbl='verbose batch iteration'):
    r"""
    Breaks up data into to batches defined by model batch size

    CommandLine:
        python -m ibeis_cnn --tf batch_iterator:0
        python -m ibeis_cnn --tf batch_iterator:1
        python -m ibeis_cnn --tf batch_iterator:2
        python -m ibeis_cnn --tf batch_iterator:1 --DEBUG_AUGMENTATION

        python -m ibeis_cnn --tf batch_iterator:1 --noaugment
        # Threaded buffering seems to help a lot
        python -m ibeis_cnn --tf batch_iterator:1 --augment

    Example0:
        >>> # ENABLE_DOCTEST
        >>> from ibeis_cnn.batch_processing import *  # NOQA
        >>> from ibeis_cnn import models
        >>> model = models.DummyModel(batch_size=16)
        >>> X, y = model.make_random_testdata(num=99, seed=None, cv2_format=True)
        >>> model.ensure_data_params(X, y)
        >>> y = None
        >>> encoder = None
        >>> randomize_batch_order = True
        >>> result_list = [(Xb, Yb) for Xb, Yb in batch_iterator(model, X, y,
        ...                randomize_batch_order)]
        >>> result = ut.depth_profile(result_list, compress_consecutive=True)
        >>> print(result)
        [[(16, 1, 4, 4), 16]] * 6 + [[(3, 1, 4, 4), 3]]

    Example1:
        >>> # ENABLE_DOCTEST
        >>> from ibeis_cnn.batch_processing import *  # NOQA
        >>> from ibeis_cnn import models
        >>> import time
        >>> model = models.SiameseL2(batch_size=128, data_shape=(8, 8, 1))
        >>> X, y = model.make_random_testdata(num=1000, seed=None, cv2_format=True)
        >>> model.ensure_data_params(X, y)
        >>> encoder = None
        >>> result_list1 = []
        >>> result_list2 = []
        >>> augment_on=not ut.get_argflag('--noaugment')
        >>> iterkw = dict(randomize_batch_order=True,
        >>>              augment_on=augment_on,
        >>>              showprog=True, verbose=ut.VERBOSE)
        >>> sleep_time = .05
        >>> inside_time1 = 0
        >>> inside_time2 = 0
        >>> with ut.Timer('buffered') as t2:
        >>>     generator =  batch_iterator(model, X, y, **iterkw)
        >>>     for Xb, Yb in ut.buffered_generator(generator, buffer_size=3):
        >>>         with ut.Timer('Inside', verbose=False) as t:
        >>>             time.sleep(sleep_time)
        >>>             result_list2.append(Xb.shape)
        >>>         inside_time2 += t.ellapsed
        >>> with ut.Timer('unbuffered') as t1:
        >>>     generator =  batch_iterator(model, X, y, **iterkw)
        >>>     for Xb, Yb in generator:
        >>>         with ut.Timer('Inside', verbose=False) as t:
        >>>             time.sleep(sleep_time)
        >>>             result_list1.append(Xb.shape)
        >>>         inside_time1 += t.ellapsed
        >>> print('\nInside times should be the same')
        >>> print('inside_time1 = %r' % (inside_time1,))
        >>> print('inside_time2 = %r' % (inside_time2,))
        >>> print('Outside times show the overhead of data augmentation')
        >>> print('Overhead Unbuffered = %r' % (t1.ellapsed - inside_time1,))
        >>> print('Overhead Buffered   = %r' % (t2.ellapsed - inside_time2,))
        >>> print('Efficiency Unbuffered  = %.2f' % (100 * inside_time1 / t1.ellapsed,))
        >>> print('Efficiency Buffered    = %.2f' % (100 * inside_time2 / t2.ellapsed,))
        >>> assert result_list1 == result_list2
        >>> print(len(result_list2))

    Example2:
        >>> # DISABLE_DOCTEST
        >>> from ibeis_cnn.batch_processing import *  # NOQA
        >>> from ibeis_cnn.models.mnist import MNISTModel
        >>> from ibeis_cnn import ingest_data
        >>> # should yield float32 regardlesss of original format
        >>> ut.exec_funckw(batch_iterator, globals())
        >>> randomize_batch_order = False
        >>> # ---
        >>> dataset1 = ingest_data.grab_mnist_category_dataset_float()
        >>> model1 = MNISTModel(batch_size=8, data_shape=dataset1.data_shape,
        >>>                    output_dims=dataset1.output_dims,
        >>>                    arch_tag=dataset1.alias_key,
        >>>                    training_dpath=dataset1.training_dpath)
        >>> X1, y1 = dataset1.subset('train')
        >>> model1.ensure_data_params(X1, y1)
        >>> _iter1 = batch_iterator(model1, X1, y1, randomize_batch_order)
        >>> Xb1, yb1 = six.next(_iter1)
        >>> # ---
        >>> dataset2 = ingest_data.grab_mnist_category_dataset()
        >>> model2 = MNISTModel(batch_size=8, data_shape=dataset2.data_shape,
        >>>                    output_dims=dataset2.output_dims,
        >>>                    arch_tag=dataset2.alias_key,
        >>>                    training_dpath=dataset2.training_dpath)
        >>> X2, y2 = dataset2.subset('train')
        >>> model2.ensure_data_params(X2, y2)
        >>> _iter2 = batch_iterator(model2, X2, y2, randomize_batch_order)
        >>> Xb2, yb2 = six.next(_iter2)
        >>> # ---
        >>> X, y, model = X1, y1, model1
        >>> assert np.all(yb2 == yb2)
        >>> # The uint8 and float32 data should produce similar values
        >>> # For this mnist set it will be a bit more off because the
        >>> # original uint8 scaling value was 256 not 255.
        >>> assert (Xb1[0] - Xb2[0]).max() < .1
        >>> assert np.isclose(Xb1.mean(), 0, atol=.01)
        >>> assert np.isclose(Xb2.mean(), 0, atol=.01)
        >>> assert Xb1.max() <  1.0 and Xb2.max() <  1.0, 'out of (-1, 1)'
        >>> assert Xb1.min() > -1.0 and Xb1.min() > -1.0, 'out of (-1, 1)'
        >>> assert Xb1.min() < 0, 'should have some negative values'
        >>> assert Xb2.min() < 0, 'should have some negative values'
        >>> assert Xb1.max() > 0, 'should have some positive values'
        >>> assert Xb2.max() > 0, 'should have some positive values'
    """
    if verbose:
        verbose = VERBOSE_BATCH

    # need to be careful with batchsizes if directly specified to theano
    wraparound = model.input_shape[0] is not None
    augment_on = augment_on and hasattr(model, 'augment')
    encoder = getattr(model, 'encoder', None)
    needs_convert = ut.is_int(X)
    if y is not None:
        assert X.shape[0] == (y.shape[0] * model.data_per_label_input), (
            'bad data / label alignment')
    num_batches = (X.shape[0] + model.batch_size - 1) // model.batch_size

    if randomize_batch_order:
        # Randomly shuffle data
        # 0.079 mnist time fraction
        X, y = utils.data_label_shuffle(X, y, model.data_per_label_input)
    if verbose:
        print('[batchiter] BEGIN')
        print('[batchiter] X.shape %r' % (X.shape, ))
        if y is not None:
            print('[batchiter] y.shape %r' % (y.shape, ))
        print('[batchiter] augment_on %r' % (augment_on, ))
        print('[batchiter] encoder %r' % (encoder, ))
        print('[batchiter] wraparound %r' % (wraparound, ))
        print('[batchiter] model.data_per_label_input %r' % (model.data_per_label_input, ))
        print('[batchiter] needs_convert = %r' % (needs_convert,))

    # FIXME: put in a layer?
    center_mean = None
    center_std  = None
    # Load precomputed whitening parameters
    if model.data_params is not None:
        center_mean = np.array(model.data_params['center_mean'], dtype=np.float32)
        center_std  = np.array(model.data_params['center_std'], dtype=np.float32)
    do_whitening = (center_mean is not None and
                    center_std is not None and
                    center_std != 0.0)

    if needs_convert:
        ceneter_mean01 = center_mean / np.array(255.0, dtype=np.float32)
        center_std01 = center_std / np.array(255.0, dtype=np.float32)
    else:
        ceneter_mean01 = center_mean
        center_std01 = center_std

    # Slice and preprocess data in batch
    for batch_index in range(num_batches):
        # Take a slice from the data
        Xb_orig, yb_orig = utils.slice_data_labels(
            X, y, model.batch_size, batch_index, model.data_per_label_input,
            wraparound=wraparound)
        # Ensure correct format for the GPU
        Xb = Xb_orig.astype(np.float32)
        yb = None if yb_orig is None else yb_orig.astype(np.int32)
        if needs_convert:
            # Rescale the batch data to the range 0 to 1
            Xb = Xb / 255.0
        if augment_on:
            # Apply defined transformations
            Xb, yb, = augment_batch(model, Xb, yb, batch_index, verbose, num_batches)
        if do_whitening:
            # Center the batch data in the range (-1.0, 1.0)
            Xb = (Xb - ceneter_mean01) / (center_std01)
        if X_is_cv2_native:
            # Convert from cv2 to lasange format
            Xb = Xb.transpose((0, 3, 1, 2))
        if yb is not None:
            if encoder is not None:
                # Apply an encoding if applicable
                yb = encoder.transform(yb).astype(np.int32)
            if model.data_per_label_input > 1 and getattr(model, 'needs_padding', False):
                # Pad data for siamese networks
                yb = pad_labels(model, yb)
        if verbose:
            # Print info if requested
            print_batch_info(Xb, yb, verbose, batch_index, num_batches)
        yield Xb, yb
    if verbose:
        print('[batch] END')
Beispiel #10
0
def figure(fnum=None,
           pnum=(1, 1, 1),
           docla=False,
           title=None,
           figtitle=None,
           doclf=False,
           projection=None,
           **kwargs):
    """
    TODO: gridspec
    http://matplotlib.org/users/gridspec.html

    Args:
        fnum (int):  fignum = figure number
        pnum (int, str, or tuple(int, int, int)): plotnum = plot tuple

    Args:
        fnum (int): fignum = figure number
        pnum (int, str, or tuple(int, int, int)): plotnum = plot tuple
        docla (bool): (default = False)
        title (str):  (default = None)
        figtitle (None): (default = None)
        doclf (bool): (default = False)
        projection (None): (default = None)

    Returns:
        ?: fig

    CommandLine:
        python -m plottool.custom_figure --exec-figure:0 --show
        python -m plottool.custom_figure --exec-figure:1 --show

    Example:
        >>> # ENABLE_DOCTEST
        >>> from plottool.custom_figure import *  # NOQA
        >>> fnum = 1
        >>> fig = figure(fnum, (2, 2, 1))
        >>> gca().text(0.5, 0.5, "ax1", va="center", ha="center")
        >>> fig = figure(fnum, (2, 2, 2))
        >>> gca().text(0.5, 0.5, "ax2", va="center", ha="center")
        >>> ut.show_if_requested()

    Example:
        >>> # ENABLE_DOCTEST
        >>> from plottool.custom_figure import *  # NOQA
        >>> fnum = 1
        >>> fig = figure(fnum, (2, 2, 1))
        >>> gca().text(0.5, 0.5, "ax1", va="center", ha="center")
        >>> fig = figure(fnum, (2, 2, 2))
        >>> gca().text(0.5, 0.5, "ax2", va="center", ha="center")
        >>> fig = figure(fnum, (2, 4, (1, slice(1, None))))
        >>> gca().text(0.5, 0.5, "ax3", va="center", ha="center")
        >>> ut.show_if_requested()
    """
    #mpl.pyplot.xkcd()
    fig = get_fig(fnum)
    axes_list = fig.get_axes()
    # Ensure my customized settings
    customize_figure(fig, docla)
    if ut.is_int(pnum):
        pnum = _convert_pnum_int_to_tup(pnum)

    def pnum_to_subspec(pnum):
        if isinstance(pnum, six.string_types):
            pnum = list(pnum)
        #if isinstance(pnum, (list, tuple)):
        nrow, ncols, plotnum = pnum

        if kwargs.get('use_gridspec', True):
            # Convert old pnums to gridspec
            gs = gridspec.GridSpec(nrow, ncols)
            if isinstance(plotnum, (tuple, slice, list)):
                subspec = gs[plotnum]
            else:
                subspec = gs[plotnum - 1]
            return (subspec, )
        else:
            return (nrow, ncols, plotnum)

    if doclf:  # a bit hacky. Need to rectify docla and doclf
        fig.clf()
        # <HACK TO CLEAR AXES>
        #for ax in axes_list:
        #    ax.clear()
        #for ax in fig.get_axes:
        #    fig.delaxes(ax)
        #axes_list = []
        # </HACK TO CLEAR AXES>
    # Get the subplot
    if docla or len(axes_list) == 0:
        #printDBG('[df2] *** NEW FIGURE %r.%r ***' % (fnum, pnum))
        if pnum is not None:
            assert pnum[0] > 0, 'nRows must be > 0: pnum=%r' % (pnum, )
            assert pnum[1] > 0, 'nCols must be > 0: pnum=%r' % (pnum, )
            #ax = plt.subplot(*pnum)
            subspec = pnum_to_subspec(pnum)
            ax = fig.add_subplot(*subspec, projection=projection)
            #ax = fig.add_subplot(*pnum, projection=projection)
            ax.cla()
        else:
            ax = gca()
    else:
        #printDBG('[df2] *** OLD FIGURE %r.%r ***' % (fnum, pnum))
        if pnum is not None:
            subspec = pnum_to_subspec(pnum)
            ax = plt.subplot(*subspec)
            #ax = plt.subplot(nrow, ncols, plotnum)
            #ax = plt.subplot(*pnum)  # fig.add_subplot fails here
            #ax = fig.add_subplot(*pnum)
        else:
            ax = gca()
        #ax  = axes_list[0]
    # Set the title
    if title is not None:
        ax = gca()
        set_title(title, ax=ax)
        # Add title to figure
        # HACK HACK HACK
        if figtitle is None and pnum == (1, 1, 1):
            figtitle = title
        if figtitle is not None:
            set_figtitle(figtitle, incanvas=False)
    return fig
Beispiel #11
0
def figure(fnum=None, pnum=(1, 1, 1), docla=False, title=None, figtitle=None,
           doclf=False, projection=None, **kwargs):
    """
    TODO: gridspec
    http://matplotlib.org/users/gridspec.html

    Args:
        fnum (int):  fignum = figure number
        pnum (int, str, or tuple(int, int, int)): plotnum = plot tuple

    Args:
        fnum (int): fignum = figure number
        pnum (int, str, or tuple(int, int, int)): plotnum = plot tuple
        docla (bool): (default = False)
        title (str):  (default = None)
        figtitle (None): (default = None)
        doclf (bool): (default = False)
        projection (None): (default = None)

    Returns:
        ?: fig

    CommandLine:
        python -m plottool.custom_figure --exec-figure:0 --show
        python -m plottool.custom_figure --exec-figure:1 --show

    Example:
        >>> # ENABLE_DOCTEST
        >>> from plottool.custom_figure import *  # NOQA
        >>> fnum = 1
        >>> fig = figure(fnum, (2, 2, 1))
        >>> gca().text(0.5, 0.5, "ax1", va="center", ha="center")
        >>> fig = figure(fnum, (2, 2, 2))
        >>> gca().text(0.5, 0.5, "ax2", va="center", ha="center")
        >>> ut.show_if_requested()

    Example:
        >>> # ENABLE_DOCTEST
        >>> from plottool.custom_figure import *  # NOQA
        >>> fnum = 1
        >>> fig = figure(fnum, (2, 2, 1))
        >>> gca().text(0.5, 0.5, "ax1", va="center", ha="center")
        >>> fig = figure(fnum, (2, 2, 2))
        >>> gca().text(0.5, 0.5, "ax2", va="center", ha="center")
        >>> fig = figure(fnum, (2, 4, (1, slice(1, None))))
        >>> gca().text(0.5, 0.5, "ax3", va="center", ha="center")
        >>> ut.show_if_requested()
    """
    #mpl.pyplot.xkcd()
    fig = get_fig(fnum)
    axes_list = fig.get_axes()
    # Ensure my customized settings
    customize_figure(fig, docla)
    if ut.is_int(pnum):
        pnum = _convert_pnum_int_to_tup(pnum)

    def pnum_to_subspec(pnum):
        if isinstance(pnum, six.string_types):
            pnum = list(pnum)
        #if isinstance(pnum, (list, tuple)):
        nrow, ncols, plotnum = pnum

        if kwargs.get('use_gridspec', True):
            # Convert old pnums to gridspec
            gs = gridspec.GridSpec(nrow, ncols)
            if isinstance(plotnum, (tuple, slice, list)):
                subspec = gs[plotnum]
            else:
                subspec = gs[plotnum - 1]
            return (subspec,)
        else:
            return (nrow, ncols, plotnum)

    if doclf:  # a bit hacky. Need to rectify docla and doclf
        fig.clf()
        # <HACK TO CLEAR AXES>
        #for ax in axes_list:
        #    ax.clear()
        #for ax in fig.get_axes:
        #    fig.delaxes(ax)
        #axes_list = []
        # </HACK TO CLEAR AXES>
    # Get the subplot
    if docla or len(axes_list) == 0:
        #printDBG('[df2] *** NEW FIGURE %r.%r ***' % (fnum, pnum))
        if pnum is not None:
            assert pnum[0] > 0, 'nRows must be > 0: pnum=%r' % (pnum,)
            assert pnum[1] > 0, 'nCols must be > 0: pnum=%r' % (pnum,)
            #ax = plt.subplot(*pnum)
            subspec = pnum_to_subspec(pnum)
            ax = fig.add_subplot(*subspec, projection=projection)
            #ax = fig.add_subplot(*pnum, projection=projection)
            ax.cla()
        else:
            ax = gca()
    else:
        #printDBG('[df2] *** OLD FIGURE %r.%r ***' % (fnum, pnum))
        if pnum is not None:
            subspec = pnum_to_subspec(pnum)
            ax = plt.subplot(*subspec)
            #ax = plt.subplot(nrow, ncols, plotnum)
            #ax = plt.subplot(*pnum)  # fig.add_subplot fails here
            #ax = fig.add_subplot(*pnum)
        else:
            ax = gca()
        #ax  = axes_list[0]
    # Set the title
    if title is not None:
        ax = gca()
        set_title(title, ax=ax)
        # Add title to figure
        # HACK HACK HACK
        if figtitle is None and pnum == (1, 1, 1):
            figtitle = title
        if figtitle is not None:
            set_figtitle(figtitle, incanvas=False)
    return fig
def figure(
    fnum=None,
    pnum=(1, 1, 1),
    docla=False,
    title=None,
    figtitle=None,
    doclf=False,
    projection=None,
    **kwargs,
):
    """
    http://matplotlib.org/users/gridspec.html

    Args:
        fnum (int): fignum = figure number
        pnum (int, str, or tuple(int, int, int)): plotnum = plot tuple
        docla (bool): (default = False)
        title (str):  (default = None)
        figtitle (None): (default = None)
        doclf (bool): (default = False)
        projection (None): (default = None)

    Returns:
        ?: fig

    CommandLine:
        python -m wbia.plottool.custom_figure --exec-figure:0 --show
        python -m wbia.plottool.custom_figure --exec-figure:1 --show

    Example:
        >>> # ENABLE_DOCTEST
        >>> from wbia.plottool.custom_figure import *  # NOQA
        >>> fnum = 1
        >>> fig = figure(fnum, (2, 2, 1))
        >>> gca().text(0.5, 0.5, "ax1", va="center", ha="center")
        >>> fig = figure(fnum, (2, 2, 2))
        >>> gca().text(0.5, 0.5, "ax2", va="center", ha="center")
        >>> import wbia.plottool as pt
        >>> pt.show_if_requested()

    Example:
        >>> # ENABLE_DOCTEST
        >>> from wbia.plottool.custom_figure import *  # NOQA
        >>> fnum = 1
        >>> fig = figure(fnum, (2, 2, 1))
        >>> gca().text(0.5, 0.5, "ax1", va="center", ha="center")
        >>> fig = figure(fnum, (2, 2, 2))
        >>> gca().text(0.5, 0.5, "ax2", va="center", ha="center")
        >>> fig = figure(fnum, (2, 4, (1, slice(1, None))))
        >>> gca().text(0.5, 0.5, "ax3", va="center", ha="center")
        >>> import wbia.plottool as pt
        >>> pt.show_if_requested()
    """
    # mpl.pyplot.xkcd()
    fig = ensure_fig(fnum)
    axes_list = fig.get_axes()
    # Ensure my customized settings
    customize_figure(fig, docla)
    if pnum is None:
        return fig
    if ut.is_int(pnum):
        pnum = _convert_pnum_int_to_tup(pnum)
    if doclf:
        fig.clf()
    if docla or len(axes_list) == 0:
        if pnum is not None:
            assert pnum[0] > 0, 'nRows must be > 0: pnum=%r' % (pnum, )
            assert pnum[1] > 0, 'nCols must be > 0: pnum=%r' % (pnum, )
            subspec = _pnum_to_subspec(pnum)
            ax = fig.add_subplot(*subspec, projection=projection)
            if docla:
                ax.cla()
        else:
            ax = gca()
    else:
        if pnum is not None:
            subspec = _pnum_to_subspec(pnum)
            ax = plt.subplot(*subspec)
        else:
            ax = gca()
    # Set the title / figtitle
    if title is not None:
        ax = gca()
        set_title(title, ax=ax)
    if figtitle is not None:
        set_figtitle(figtitle, incanvas=False, fig=fig)
    return fig