The input has a specific format, which is a list of two-element tuples.
Each of the tuples contains the negative :math:`\\{S_p^-\\}` and the positive :math:`\\{S_p^+\\}` scores for one probe item :math:`p`, or ``None`` in case of open set recognition.
To read the lists from score files in 4 or 5 column format, please use the :py:func:`bob.measure.load.cmc_four_column` or :py:func:`bob.measure.load.cmc_five_column` function.
If **threshold** is set to ``None``, the rank 1 recognition rate is defined as the number of test items, for which the highest positive :math:`\\max\\{S_p^+\\}` score is greater than or equal to all negative scores, divided by the number of all probe items :math:`P`:
.. math::
If **threshold** is set to `None`, the recognition rate is defined as the number of test items, for which the
positive score is greater than or equal to all negative scores, divided by
the number of all test items. If several positive scores for one test item exist, the **highest** score is taken.
For a given rank :math:`r>1`, up to :math:`r` negative scores that are higher than the highest positive score are allowed to still count as correctly classified in the top :math:`r` rank.
If ``threshold`` :math:`\\theta` is given, **all** scores below threshold will be filtered out.
Hence, if all positive scores are below threshold :math:`\\max\\{S_p^+\\} < \\theta`, the probe will be misclassified **at any rank**.
For open set recognition, i.e., when there exist a tuple including negative scores without corresponding positive scores (``None``), and **all** negative scores are below ``threshold`` :math:`\\max\\{S_p^+\\} < \\theta`, the probe item is correctly rejected, **and it does not count into the denominator** :math:`P`.
When no ``threshold`` is provided, the open set probes will **always** count as misclassified, regardless of the ``rank``.
.. warn:
For open set tests, this rate does not correspond to a standard rate.
Please use :py:func:`detection_identification_rate` and :py:func:`false_alarm_rate` instead.
If **threshold** assumes one value, the recognition rate is defined as the number of test items, for which the
positive score is greater than or equal to all negative scores and the threshold divided by
the number of all test items. If several positive scores for one test item exist, the **highest** score is taken.
**Parameters:**
``cmc_scores`` : CMC scores loaded with one of the functions (:py:func:`bob.measure.load.cmc_four_column` or :py:func:`bob.measure.load.cmc_five_column`)
``threshold`` : Decision threshold. If `None`, the decision threshold will be the **highest** positive score.
CMC scores loaded with one of the functions (:py:func:`bob.measure.load.cmc_four_column` or :py:func:`bob.measure.load.cmc_five_column`).
Each pair contains the ``negative`` and the ``positive`` scores for **one probe item**.
Each pair can contain up to one empty array (or ``None``), i.e., in case of open set recognition.
``threshold`` : float or ``None``
Decision threshold. If not ``None``, **all** scores will be filtered by the threshold.
In an open set recognition problem, all open set scores (negatives with no corresponding positive) for which all scores are below threshold, will be counted as correctly rejected and **removed** from the probe list (i.e., the denominator).
``rank`` : int or ``None``
The rank for which the recognition rate should be computed, 1 by default.
**Returns:**
``RR`` : float
The rank 1 recognition rate, i.e., the relative number of correctly identified identities
The (open set) recognition rate for the given rank, a value between 0 and 1.
"""
# If no scores are given, the recognition rate is exactly 0.
ifnotcmc_scores:
return0.
correct=0.
correct=0
counter=0
forneg,posincmc_scores:
# set all values that are empty before to None
ifposisnotNoneandnotnumpy.array(pos).size:
pos=None
ifnegisnotNoneandnotnumpy.array(neg).size:
neg=None
ifposisNoneandnegisNone:
raiseValueError("One pair of the CMC scores has neither positive nor negative values")
# filter out any negative or positive scores below threshold; scores with exactly the threshold are also filtered out
# now, None and an empty array have different meanings.
ifthresholdisnotNone:
ifnegisnotNone:
neg=numpy.array(neg)[neg>threshold]
ifposisnotNone:
pos=numpy.array(pos)[pos>threshold]
ifposisNone:
# no positives, so we definitely do not have a match;
# check if we have negatives above threshold
ifnotneg.size:
# we have no negative scores over the threshold, so we have correctly rejected the probe
# don't increase any of the two counters...
continue
# we have negatives over threshold, so we have incorrect classifications; independent on the actual rank
counter+=1
else:
# we have a positive, so we need to count the probe
counter+=1
ifnotnumpy.array(pos).size:
# all positive scores have been filtered out by the threshold, we definitely have a mis-match
continue
#If threshold is none, let's use the highest positive score as the decision threshold
if(thresholdisNone):
# get the maximum positive score for the current probe item
# (usually, there is only one positive score, but just in case...)
max_pos=numpy.max(pos)
# check if the positive score is smaller than all negative scores
if(neg<max_pos).all():
correct+=1.
else:
#If threshold is NOT None, we have an openset identification
max_pos=numpy.max(pos)
if((threshold<max_pos)and(neg<max_pos).all()):
correct+=1.
# return relative number of correctly matched scores
returncorrect/float(len(cmc_scores))
ifnegisNoneornotnumpy.array(neg).size:
# if we had no negatives, or all negatives were below threshold, we have a match at rank 1
correct+=1
else:
# count the number of negative scores that are higher than the best positive score
index=numpy.sum(neg>=max_pos)
ifindex<rank:
correct+=1
returnfloat(correct)/float(counter)
defcmc(cmc_scores):
...
...
@@ -143,24 +189,26 @@ def cmc(cmc_scores):
Calculates the cumulative match characteristic (CMC) from the given input.
The input has a specific format, which is a list of two-element tuples. Each
of the tuples contains the negative and the positive scores for one test
of the tuples contains the negative and the positive scores for one probe
item. To read the lists from score files in 4 or 5 column format, please use
the :py:func:`bob.measure.load.cmc_four_column` or
raiseValueError("For the CMC computation at least one positive score is necessary. Please review who you are loading the scores. You must set `load_only_negatives=False` in the :py:func:`bob.measure.load.cmc_four_column` or `:py:func:`bob.measure.load.cmc_five_column` methods.")
ifposisNoneornotnumpy.array(pos).size:
raiseValueError("For the CMC computation at least one positive score per pair is necessary.")
ifnegisNone:
neg=[]
# get the maximum positive score for the current probe item
# (usually, there is only one positive score, but just in case...)
# (usually, there is only one positive score, but just in case...)
max_pos=numpy.max(pos)
# count the number of negative scores that are higher than the best positive score
# count the number of negative scores that are higher than the best positive score
"""detection_identification_rate(cmc_scores, threshold, rank) -> dir
Computes the `detection and identification rate` for the given threshold.
This value is designed to be used in an open set identification protocol, and defined in Chapter 14.1 of [LiJain2005]_.
Although the detection and identification rate is designed to be computed on an open set protocol, it uses only the probe elements, for which a corresponding gallery element exists.
For closed set identification protocols, this function is identical to :py:func:`recognition_rate`.
The only difference is that for this function, a ``threshold`` for the scores need to be defined, while for :py:func:`recognition_rate` it is optional.
CMC scores loaded with one of the functions (:py:func:`bob.measure.load.cmc_four_column` or :py:func:`bob.measure.load.cmc_five_column`).
Each pair contains the ``negative`` and the ``positive`` scores for **one probe item**.
There need to be at least one probe item, for which positive and negative scores exist.
``threshold`` : float
The decision threshold :math:`\\tau``.
``rank`` : int
The rank for which the curve should be plotted, by default 1.
**Returns:**
``dir`` : float
The detection and identification rate for the given threshold.
"""
# count the correctly classifier probes
correct=0
counter=0
forneg,posincmc_scores:
ifposisNoneornotnumpy.array(pos).size:
# we only consider probes with corresponding gallery items
continue
# we have an in-gallery probe
counter+=1
# check, if it is correctly classified
ifnegisNone:
neg=[]
# get the maximum positive score for the current probe item
# (usually, there is only one positive score, but just in case...)
max_pos=numpy.max(pos)
index=numpy.sum(neg>=max_pos)# compute the rank (in fact, rank - 1)
ifmax_pos>=thresholdandindex<rank:
correct+=1
ifnotcounter:
logger.warn("No in-gallery probe was found")
return0.
returnfloat(correct)/float(counter)
deffalse_alarm_rate(cmc_scores,threshold):
"""false_alarm_rate(cmc_scores, threshold) -> far
Computes the `false alarm rate` for the given threshold,.
This value is designed to be used in an open set identification protocol, and defined in Chapter 14.1 of [LiJain2005]_.
The false alarm rate is designed to be computed on an open set protocol, it uses only the probe elements, for which **no** corresponding gallery element exists.
raiseValueError("Only scores with 4 and 5 columns are supported.")
numpy.savetxt(filename,score_lines,fmt=fmt)
def_convert_cmc_scores(neg_dict,pos_dict):
"""Converts the negative and positive scores read with :py:func:`cmc_four_column` or :py:func:`cmc_four_column` into a format that is handled by the :py:func:`bob.measure.cmc` and similar functions."""
# convert to lists of tuples of ndarrays (or None)
This function computes log-scaled values between :math:`10^{M}` and 1 (including), where :math:`M` is the ``min_ste`` argument, which needs to be a negative integer.
The integral ``counts_per_step`` value defines how many values between two adjacent powers of 10 will be created.
The total number of values will be ``-min_step * counts_per_step + 1``.
**Parameters:**
``min_step`` : int (negative)
The power of 10 that will be the minimum value. E.g., the default ``-4`` will result in the first number to be :math:`10^{-4}` = ``0.00001`` or ``0.01%``
``counts_per_step`` : int (positive)
The number of values that will be put between two adjacent powers of 10.
With the default value ``4`` (and default values of ``min_step``), we will get ``log_list[0] == 1e-4``, ``log_list[4] == 1e-3``, ..., ``log_list[16] == 1``.
**Returns**
``log_list`` : [float]
A list of logarithmically scaled values between :math:`10^{M}` and 1.
See :py:func:`bob.measure.detection_identification_rate`
``far_values`` : [float]
The values for the FAR, where the CAR should be plotted; each value should be in range [0,1].
``rank`` : int or ``None``
The rank for which the curve should be plotted, 1 by default.
``logx`` : bool
Plot the FAR axis in logarithmic scale using :py:func:`matplotlib.pyplot.semilogx` or in linear scale using :py:func:`matplotlib.pyplot.plot`? (Default: ``True``)
``kwargs`` : keyword arguments
Extra plotting parameters, which are passed directly to :py:func:`matplotlib.pyplot.plot` or :py:func:`matplotlib.pyplot.semilogx`.
**Returns:**
The return value is the ``matplotlib`` line that was added as defined by :py:func:`matplotlib.pyplot.plot`.
.. [LiJain2005] **Stan Li and Anil K. Jain**, *Handbook of Face Recognition*, Springer, 2005