diff --git a/bob/measure/cpp/error.cpp b/bob/measure/cpp/error.cpp index a67f28ffd0cdbb3978632928694d1933c9fd0af0..facf8114b9c5b989991f6956d0ec639bbb86b14d 100644 --- a/bob/measure/cpp/error.cpp +++ b/bob/measure/cpp/error.cpp @@ -421,73 +421,21 @@ bob::measure::roc_for_far(const blitz::Array &negatives, sort(negatives, neg, is_sorted); sort(positives, pos, is_sorted); - // do some magic to compute the FRR list blitz::Array retval(2, n_points); - // index into the FAR and FRR list + // index into the FAR list int far_index = n_points - 1; - int pos_index = 0, neg_index = 0; - int n_pos = pos.extent(0), n_neg = neg.extent(0); - - // iterators into the result lists - auto pos_it = pos.begin(), neg_it = neg.begin(); - // do some fast magic to compute the FRR values ;-) - do { - // check whether the current positive value is less than the current - // negative one - if (*pos_it < *neg_it) { - // increase the positive count - ++pos_index; - // go to the next positive value - ++pos_it; - } else { - // increase the negative count - ++neg_index; - // go to the next negative value - ++neg_it; - } - // check, if we have reached a new FAR limit, - // i.e. if the relative number of negative similarities is greater than - // 1-FAR (which is the CRR) - - if (((double)neg_index / (double)n_neg > 1. - far_list(far_index)) && - !(bob::core::isClose((double)neg_index / (double)n_neg, - 1. - far_list(far_index), 1e-9, 1e-9))) { - // copy the far value - retval(0, far_index) = far_list(far_index); - // calculate the FRR for the current FAR - retval(1, far_index) = (double)pos_index / (double)n_pos; - // go to the next FAR value - --far_index; - } - // do this, as long as there are elements in both lists left and not all FRR - // elements where calculated yet - } while (pos_it != pos.end() && neg_it != neg.end() && far_index >= 0); - - // check if all FRR values have been set - if (far_index >= 0) { - // walk to the end of both lists; at least one of both lists should already - // have reached its limit. - while (pos_it != pos.end() && pos_it++ != pos.end()) - ++pos_index; - while (neg_it != neg.end() && neg_it++ != neg.end()) - ++neg_index; - // fill in the remaining elements of the CAR list - do { - // copy the FAR value - retval(0, far_index) = far_list(far_index); - // check if the criterion is fulfilled (should be, as long as the lowest - // far is not below 0) - if ((double)neg_index / (double)n_neg > 1. - far_list(far_index)) { - // calculate the FRR for the current FAR - retval(1, far_index) = (double)pos_index / (double)n_pos; - } else { - // set FRR to 1 (this should never happen, but might be due to numerical - // issues) - retval(1, far_index) = 1.; - } - } while (far_index--); + // Get the threshold for the requested far values and calculate far and frr + // values based on the threshold. + while(far_index >= 0) { + // calculate the threshold for the requested far + auto threshold = bob::measure::farThreshold(neg, pos, far_list(far_index), true); + // calculate the frr and re-calculate the far + auto farfrr = bob::measure::farfrr(neg, pos, threshold); + retval(0, far_index) = farfrr.first; + retval(1, far_index) = farfrr.second; + far_index--; } return retval; diff --git a/bob/measure/data/nonsep-det.hdf5 b/bob/measure/data/nonsep-det.hdf5 index 82e94bdc0f1536556552ccb84e95c97d09a58eb8..27c703b4247754a4b26270cfb1b1d97dc8e7ca9d 100644 Binary files a/bob/measure/data/nonsep-det.hdf5 and b/bob/measure/data/nonsep-det.hdf5 differ diff --git a/bob/measure/data/nonsep-roc.hdf5 b/bob/measure/data/nonsep-roc.hdf5 index ab2c517098768664fe03bf33b4ed63879d92ec44..4b00e0d61cea8c40ec5cd89ee2e2394c0a578683 100644 Binary files a/bob/measure/data/nonsep-roc.hdf5 and b/bob/measure/data/nonsep-roc.hdf5 differ diff --git a/bob/measure/main.cpp b/bob/measure/main.cpp index eb68dbc8e4f42d617ab739ed879cc8d13adc1efa..1afcb9c64ed8d4423dc11a84ba33cc3883139040 100644 --- a/bob/measure/main.cpp +++ b/bob/measure/main.cpp @@ -714,11 +714,7 @@ static auto far_threshold_doc = bob::extension::FunctionDoc( "far_threshold", "Computes the threshold such that the real FAR is " "**at most** the requested ``far_value`` if possible", - "If no such threshold can be computed, ``NaN`` is returned. It is " - "impossible to compute the threshold, when too few non-identical " - "highest scores exist, so that the desired ``far_value`` cannot be " - "reached by any threshold.\n\n" - ".. note::\n\n" + "\n\n.. note::\n\n" " The scores will be sorted internally, requiring the scores to be " "copied.\n" " To avoid this copy, you can sort the ``negatives`` scores " @@ -774,11 +770,7 @@ static auto frr_threshold_doc = bob::extension::FunctionDoc( "frr_threshold", "Computes the threshold such that the real FRR is " "**at most** the requested ``frr_value`` if possible", - "If no such threshold can be computed, ``NaN`` is returned. It is " - "impossible to compute the threshold, when too few non-identical " - "lowest scores exist, so that the desired ``frr_value`` cannot be " - "reached by any threshold.\n\n" - ".. note::\n\n" + "\n\n.. note::\n\n" " The scores will be sorted internally, requiring the scores to be " "copied.\n" " To avoid this copy, you can sort the ``positives`` scores " diff --git a/bob/measure/test_error.py b/bob/measure/test_error.py index f7e727a7f79d136dac91c306b1777094318e35c0..e0d6064868613a64aca7df318768c4e6c7ae9f88 100644 --- a/bob/measure/test_error.py +++ b/bob/measure/test_error.py @@ -293,17 +293,18 @@ def test_plots(): assert numpy.array_equal(xy, xyref) # This example will test the ROC for FAR plot calculation functionality. - far = [0.01, 0.1, 1] - ref = [0.42, 0.12, 0] - xy = roc_for_far(negatives, positives, far) + requested_far = [0.01, 0.1, 1] + expected_far = [0.0, 0.1, 1] + expected_frr = [0.48, 0.12, 0] + xy = roc_for_far(negatives, positives, requested_far) - assert numpy.array_equal(xy[0], far) - assert numpy.array_equal(xy[1], ref) + assert numpy.array_equal(xy[0], expected_far), xy[0] + assert numpy.array_equal(xy[1], expected_frr), xy[1] # This example will test the Precision-Recall plot calculation functionality. xy = precision_recall_curve(negatives, positives, 100) # uncomment the next line to save a reference value - # save('nonsep-roc.hdf5', xy) + # save('nonsep-precisionrecall.hdf5', xy) xyref = bob.io.base.load(F('nonsep-precisionrecall.hdf5')) assert numpy.array_equal(xy, xyref)