@Override
 public void extractFeatures(List regions) throws FeatureExtractorException {
   for (Region r : (List<Region>) regions) {
     if (r.hasAttribute(f1) && r.hasAttribute(f2)) {
       Contour c1, c2;
       try {
         c1 = ContourUtils.getSubContour((Contour) r.getAttribute(f1), r.getStart(), r.getEnd());
         c2 = ContourUtils.getSubContour((Contour) r.getAttribute(f2), r.getStart(), r.getEnd());
       } catch (AuToBIException e) {
         throw new FeatureExtractorException(e.getMessage());
       }
       r.setAttribute("rmse[" + f1 + "," + f2 + "]", contourRMSE(c1, c2));
       r.setAttribute("meanError[" + f1 + "," + f2 + "]", contourError(c1, c2));
     }
   }
 }
  @Override
  public void extractFeatures(List regions) throws FeatureExtractorException {
    for (Region r : (List<Region>) regions) {
      if (r.hasAttribute(feature)) {
        Contour super_c = (Contour) r.getAttribute(feature);
        Contour c;
        try {
          c = ContourUtils.getSubContour(super_c, r.getStart(), r.getEnd());
        } catch (AuToBIException e) {
          throw new FeatureExtractorException(e.getMessage());
        }

        CurveShape falling = smooth(-1, true, c);
        CurveShape rising = smooth(c.size(), true, c);
        CurveShape best_peak = null;
        double min_peak_rmse = Double.MAX_VALUE;

        // Sample the curve at at most 20 points to calculate peak and valley likelihoods.
        // On long regions calculating at every point gets *very* slow.
        int step = Math.max(1, c.size() / 20);
        for (int i = 1; i < c.size() - 1; i += step) {
          CurveShape peak = smooth(i, true, c);
          if (peak.rmse < min_peak_rmse) {
            best_peak = peak;
            min_peak_rmse = peak.rmse;
          }
        }
        CurveShape best_valley = null;
        double min_valley_rmse = Double.MAX_VALUE;
        for (int i = 1; i < c.size() - 1; i += step) {
          CurveShape valley = smooth(i, false, c);
          if (valley.rmse < min_valley_rmse) {
            best_valley = valley;
            min_valley_rmse = valley.rmse;
          }
        }
        r.setAttribute("risingCurve[" + feature + "]", rising);
        r.setAttribute("fallingCurve[" + feature + "]", falling);
        r.setAttribute("peakCurve[" + feature + "]", best_peak);
        r.setAttribute("valleyCurve[" + feature + "]", best_valley);
      }
    }
  }