/** Associates the features together. */
  public void associate() {
    // initialize data structures
    matches.reset();
    unassociatedSrc.reset();

    // find and add the matches
    assoc.setSource((FastQueue) srcPositive);
    assoc.setDestination((FastQueue) dstPositive);
    assoc.associate();
    FastQueue<AssociatedIndex> m = assoc.getMatches();
    for (int i = 0; i < m.size; i++) {
      AssociatedIndex a = m.data[i];
      int globalSrcIndex = srcPositive.data[a.src].index;
      int globalDstIndex = dstPositive.data[a.dst].index;
      matches.grow().setAssociation(globalSrcIndex, globalDstIndex, a.fitScore);
    }
    GrowQueue_I32 un = assoc.getUnassociatedSource();
    for (int i = 0; i < un.size; i++) {
      unassociatedSrc.add(srcPositive.data[un.get(i)].index);
    }
    assoc.setSource((FastQueue) srcNegative);
    assoc.setDestination((FastQueue) dstNegative);
    assoc.associate();
    m = assoc.getMatches();
    for (int i = 0; i < m.size; i++) {
      AssociatedIndex a = m.data[i];
      int globalSrcIndex = srcNegative.data[a.src].index;
      int globalDstIndex = dstNegative.data[a.dst].index;
      matches.grow().setAssociation(globalSrcIndex, globalDstIndex, a.fitScore);
    }
    un = assoc.getUnassociatedSource();
    for (int i = 0; i < un.size; i++) {
      unassociatedSrc.add(srcNegative.data[un.get(i)].index);
    }
  }
  /**
   * Splits the set of input features into positive and negative laplacian lists. Keep track of the
   * feature's index in the original input list. This is the index that needs to be returned.
   */
  private void sort(FastQueue<SurfFeature> input, FastQueue<Helper> pos, FastQueue<Helper> neg) {
    pos.reset();
    neg.reset();

    for (int i = 0; i < input.size; i++) {
      SurfFeature f = input.get(i);
      if (f.laplacianPositive) {
        pos.grow().wrap(f, i);
      } else {
        neg.grow().wrap(f, i);
      }
    }
  }
  private FastQueue<TupleDesc_F64> createData(double... values) {
    FastQueue<TupleDesc_F64> ret =
        new FastQueue<TupleDesc_F64>(10, TupleDesc_F64.class, true) {
          @Override
          protected TupleDesc_F64 createInstance() {
            return new TupleDesc_F64(1);
          }
        };

    for (int i = 0; i < values.length; i++) {
      ret.grow().set(values[i]);
    }

    return ret;
  }
  /**
   * Processes the image and extracts SIFT features
   *
   * @param input input image
   */
  public void process(ImageFloat32 input) {

    features.reset();
    featureScales.reset();
    featureAngles.reset();
    location.reset();

    ss.constructPyramid(input);
    ss.computeFeatureIntensity();
    ss.computeDerivatives();

    detector.process(ss);
    orientation.setScaleSpace(ss);
    describe.setScaleSpace(ss);

    FastQueue<ScalePoint> found = detector.getFoundPoints();

    for (int i = 0; i < found.size; i++) {
      ScalePoint sp = found.data[i];
      orientation.process(sp.x, sp.y, sp.scale);

      GrowQueue_F64 angles = orientation.getOrientations();

      int imageIndex = orientation.getImageIndex();
      double pixelScale = orientation.getPixelScale();

      for (int j = 0; j < angles.size; j++) {
        SurfFeature desc = features.grow();

        double yaw = angles.data[j];

        describe.process(sp.x, sp.y, sp.scale, yaw, imageIndex, pixelScale, desc);

        desc.laplacianPositive = sp.white;
        featureScales.push(sp.scale);
        featureAngles.push(yaw);
        location.grow().set(sp.x, sp.y);
      }
    }
  }