コード例 #1
0
  public static void evaluate(String dataset) {
    Class type = ImageFloat32.class;

    DebugTldTrackerTldData generator = new DebugTldTrackerTldData(ImageType.single(type));

    InterpolatePixelS interpolate = FactoryInterpolation.bilinearPixelS(type, BorderType.EXTENDED);
    ImageGradient gradient = FactoryDerivative.sobel(type, type);

    TldTracker tracker = new TldTracker(null, interpolate, gradient, type, type);

    generator.evaluate(dataset, tracker);
  }
コード例 #2
0
/** @author Peter Abeles */
public class TestCirculantTracker {

  Random rand = new Random(234);

  int width = 60;
  int height = 80;

  InterpolatePixelS<ImageFloat32> interp = FactoryInterpolation.bilinearPixelS(ImageFloat32.class);

  @Test
  public void meanShift() {
    int w = 32;

    CirculantTracker<ImageFloat32> alg =
        new CirculantTracker<ImageFloat32>(1f / 16, 0.2, 1e-2, 0.075, 1.0, w, 255, interp);

    int peakX = 13;
    int peakY = 17;

    alg.getResponse().reshape(w, w);
    for (int i = 0; i < w; i++) {
      double b = Math.exp(-(i - peakY) * (i - peakY) / 3.0);
      for (int j = 0; j < w; j++) {
        double a = Math.exp(-(j - peakX) * (j - peakX) / 3.0);

        alg.getResponse().set(j, i, a * b);
      }
    }

    alg.subpixelPeak(peakX - 2, peakY + 1);

    assertEquals(2, alg.offX, 0.3);
    assertEquals(-1, alg.offY, 0.3);
  }

  @Test
  public void basicTrackingCheck() {
    ImageFloat32 a = new ImageFloat32(30, 35);
    ImageFloat32 b = new ImageFloat32(30, 35);

    // randomize input image and move it
    GImageMiscOps.fillUniform(a, rand, 0, 200);
    GImageMiscOps.fillUniform(b, rand, 0, 200);

    CirculantTracker<ImageFloat32> alg =
        new CirculantTracker<ImageFloat32>(1f / 16, 0.2, 1e-2, 0.075, 1.0, 64, 255, interp);
    alg.initialize(a, 5, 6, 20, 25);

    shiftCopy(2, 4, a, b);
    alg.performTracking(b);

    double tolerance = 1;

    Rectangle2D_F32 r = alg.getTargetLocation();
    assertEquals(5 + 2, r.x0, tolerance);
    assertEquals(6 + 4, r.y0, tolerance);
  }

  @Test
  public void computeCosineWindow() {
    ImageFloat64 found = new ImageFloat64(20, 25);

    CirculantTracker.computeCosineWindow(found);

    // should be between 0 and 1
    for (int i = 0; i < found.data.length; i++) {
      assertTrue(found.data[i] >= 0 && found.data[i] <= 1);
    }

    centeredSymmetricChecks(found, false);
  }

  @Test
  public void computeGaussianWeights() {
    int w = 16;
    CirculantTracker<ImageFloat32> alg =
        new CirculantTracker<ImageFloat32>(1f / 16, 0.2, 1e-2, 0.075, 1.0, w, 255, interp);

    alg.gaussianWeight.reshape(w, w);
    alg.gaussianWeightDFT.reshape(w, w);

    alg.computeGaussianWeights(w);

    centeredSymmetricChecks(alg.gaussianWeight, true);
  }

  private void centeredSymmetricChecks(ImageFloat64 image, boolean offByOne) {

    // see comments in computeGaussianWeights
    int offX = offByOne ? 1 - image.width % 2 : 0;
    int offY = offByOne ? 1 - image.height % 2 : 0;

    int cx = image.width / 2;
    int cy = image.height / 2;
    int w = image.width - 1;
    int h = image.height - 1;

    // edges should be smaller than center
    assertTrue(image.get(cx, cy) > image.get(0, 0));
    assertTrue(image.get(cx, cy) > image.get(w, h));
    assertTrue(image.get(cx, cy) > image.get(w, h));
    assertTrue(image.get(cx, cy) > image.get(w, 0));

    // symmetry check
    for (int i = offY; i < cy; i++) {
      for (int j = offX; j < cx; j++) {
        double v0 = image.get(j, i);
        double v1 = image.get(w - j + offX, i);
        double v2 = image.get(j, h - i + offY);
        double v3 = image.get(w - j + offX, h - i + offY);

        assertEquals(v0, v1, 1e-4);
        assertEquals(v0, v2, 1e-4);
        assertEquals(i + " " + j, v0, v3, 1e-4);
      }
    }
  }

  /**
   * Check a few simple motions. It seems to be accurate to within 1 pixel. Considering alphas seems
   * to be the issue
   */
  @Test
  public void updateTrackLocation() {
    ImageFloat32 a = new ImageFloat32(100, 100);
    ImageFloat32 b = new ImageFloat32(100, 100);

    // randomize input image and move it
    GImageMiscOps.fillUniform(a, rand, 0, 200);
    GImageMiscOps.fillUniform(b, rand, 0, 200);
    shiftCopy(0, 0, a, b);

    CirculantTracker<ImageFloat32> alg =
        new CirculantTracker<ImageFloat32>(1f / 16, 0.2, 1e-2, 0.075, 1.0, 64, 255, interp);
    alg.initialize(a, 5, 6, 20, 25);

    alg.updateTrackLocation(b);

    // only pixel level precision.
    float tolerance = 1f;

    // No motion motion
    Rectangle2D_F32 r = alg.getTargetLocation();
    assertEquals(5, r.x0, tolerance);
    assertEquals(6, r.y0, tolerance);

    // check estimated motion
    GImageMiscOps.fillUniform(b, rand, 0, 200);
    shiftCopy(-3, 2, a, b);
    alg.updateTrackLocation(b);
    r = alg.getTargetLocation();
    assertEquals(5 - 3, r.x0, tolerance);
    assertEquals(6 + 2, r.y0, tolerance);

    // try out of bounds case
    GImageMiscOps.fillUniform(b, rand, 0, 200);
    shiftCopy(-6, 0, a, b);
    alg.updateTrackLocation(b);
    assertEquals(5 - 6, r.x0, tolerance);
    assertEquals(6, r.y0, tolerance);
  }

  @Test
  public void performLearning() {
    float interp_factor = 0.075f;

    ImageFloat32 a = new ImageFloat32(20, 25);
    ImageFloat32 b = new ImageFloat32(20, 25);

    ImageMiscOps.fill(a, 100);
    ImageMiscOps.fill(b, 200);

    CirculantTracker<ImageFloat32> alg =
        new CirculantTracker<ImageFloat32>(1f / 16, 0.2, 1e-2, 0.075, 1.0, 64, 255, interp);
    alg.initialize(a, 0, 0, 20, 25);

    // copy its internal value
    ImageFloat64 templateC = new ImageFloat64(alg.template.width, alg.template.height);
    templateC.setTo(alg.template);

    // give it two images
    alg.performLearning(b);

    // make sure the images aren't full of zero
    assertTrue(Math.abs(ImageStatistics.sum(templateC)) > 0.1);
    assertTrue(Math.abs(ImageStatistics.sum(alg.template)) > 0.1);

    int numNotSame = 0;
    // the result should be an average of the two
    for (int i = 0; i < a.data.length; i++) {
      if (Math.abs(a.data[i] - alg.templateNew.data[i]) > 1e-4) numNotSame++;

      // should be more like the original one than the new one
      double expected =
          templateC.data[i] * (1 - interp_factor) + interp_factor * alg.templateNew.data[i];
      double found = alg.template.data[i];

      assertEquals(expected, found, 1e-4);
    }

    // make sure it is actually different
    assertTrue(numNotSame > 100);
  }

  @Test
  public void dense_gauss_kernel() {
    // try several different shifts
    dense_gauss_kernel(0, 0);
    dense_gauss_kernel(5, 0);
    dense_gauss_kernel(0, 5);
    dense_gauss_kernel(-3, -2);
  }

  public void dense_gauss_kernel(int offX, int offY) {
    ImageFloat64 region = new ImageFloat64(32, 32);
    ImageFloat64 target = new ImageFloat64(32, 32);
    ImageFloat64 k = new ImageFloat64(32, 32);

    CirculantTracker<ImageFloat32> alg =
        new CirculantTracker<ImageFloat32>(1f / 16, 0.2, 1e-2, 0.075, 1.0, 32, 255, interp);
    alg.initialize(new ImageFloat32(32, 32), 0, 0, 32, 32);

    // create a shape inside the image
    GImageMiscOps.fillRectangle(region, 200, 10, 15, 5, 7);

    // copy a shifted portion of the region
    shiftCopy(offX, offY, region, target);

    // process and see if the peak is where it should be
    alg.dense_gauss_kernel(0.2f, region, target, k);

    int maxX = -1, maxY = -1;
    double maxValue = -1;
    for (int y = 0; y < k.height; y++) {
      for (int x = 0; x < k.width; x++) {
        if (k.get(x, y) > maxValue) {
          maxValue = k.get(x, y);
          maxX = x;
          maxY = y;
        }
      }
    }

    int expectedX = k.width / 2 - offX;
    int expectedY = k.height / 2 - offY;

    assertEquals(expectedX, maxX);
    assertEquals(expectedY, maxY);
  }

  private void shiftCopy(int offX, int offY, ImageFloat32 src, ImageFloat32 dst) {
    for (int y = 0; y < src.height; y++) {
      for (int x = 0; x < src.width; x++) {
        int xx = x + offX;
        int yy = y + offY;

        if (xx >= 0 && xx < src.width && yy >= 0 && yy < src.height) {
          dst.set(xx, yy, src.get(x, y));
        }
      }
    }
  }

  private void shiftCopy(int offX, int offY, ImageFloat64 src, ImageFloat64 dst) {
    for (int y = 0; y < src.height; y++) {
      for (int x = 0; x < src.width; x++) {
        int xx = x + offX;
        int yy = y + offY;

        if (xx >= 0 && xx < src.width && yy >= 0 && yy < src.height) {
          dst.set(xx, yy, src.get(x, y));
        }
      }
    }
  }

  @Test
  public void imageDotProduct() {
    ImageFloat64 a = new ImageFloat64(width, height);
    ImageMiscOps.fillUniform(a, rand, 0, 10);

    double total = 0;
    for (int y = 0; y < height; y++) {
      for (int x = 0; x < width; x++) {
        total += a.get(x, y) * a.get(x, y);
      }
    }
    double found = CirculantTracker.imageDotProduct(a);
    assertEquals(total, found, 1e-8);
  }

  @Test
  public void elementMultConjB() {
    InterleavedF64 a = new InterleavedF64(width, height, 2);
    InterleavedF64 b = new InterleavedF64(width, height, 2);
    InterleavedF64 c = new InterleavedF64(width, height, 2);

    ImageMiscOps.fillUniform(a, rand, -10, 10);
    ImageMiscOps.fillUniform(b, rand, -10, 10);
    ImageMiscOps.fillUniform(c, rand, -10, 10);

    CirculantTracker.elementMultConjB(a, b, c);

    for (int y = 0; y < height; y++) {
      for (int x = 0; x < width; x++) {
        Complex64F aa = new Complex64F(a.getBand(x, y, 0), a.getBand(x, y, 1));
        Complex64F bb = new Complex64F(b.getBand(x, y, 0), b.getBand(x, y, 1));

        Complex64F cc = new Complex64F();
        ComplexMath64F.conj(bb, bb);
        ComplexMath64F.mult(aa, bb, cc);

        double foundReal = c.getBand(x, y, 0);
        double foundImg = c.getBand(x, y, 1);

        assertEquals(cc.real, foundReal, 1e-4);
        assertEquals(cc.imaginary, foundImg, 1e-4);
      }
    }
  }

  @Test
  public void computeAlphas() {
    InterleavedF64 yf = new InterleavedF64(width, height, 2);
    InterleavedF64 kf = new InterleavedF64(width, height, 2);
    InterleavedF64 alphaf = new InterleavedF64(width, height, 2);

    ImageMiscOps.fillUniform(yf, rand, -10, 10);
    ImageMiscOps.fillUniform(kf, rand, -10, 10);
    ImageMiscOps.fillUniform(alphaf, rand, -10, 10);

    float lambda = 0.01f;
    CirculantTracker.computeAlphas(yf, kf, lambda, alphaf);

    for (int y = 0; y < height; y++) {
      for (int x = 0; x < width; x++) {
        Complex64F a = new Complex64F(yf.getBand(x, y, 0), yf.getBand(x, y, 1));
        Complex64F b = new Complex64F(kf.getBand(x, y, 0) + lambda, kf.getBand(x, y, 1));

        Complex64F c = new Complex64F();
        ComplexMath64F.div(a, b, c);

        double foundReal = alphaf.getBand(x, y, 0);
        double foundImg = alphaf.getBand(x, y, 1);

        assertEquals(c.real, foundReal, 1e-4);
        assertEquals(c.imaginary, foundImg, 1e-4);
      }
    }
  }
}
コード例 #3
0
  /**
   * Create a standard image pyramid used by dense optical flow parameters. The first layer is the
   * size of the input image and the last layer is &ge; the minSize. The sigma for each layer is
   * computed using the following formula:<br>
   * <br>
   * sigmaLayer = sigma*sqrt( scale^-2 - 1 )
   *
   * <p>If the scale is 1 then a single layer pyramid will be created. If the scale is 0 then the
   * scale will be determined by the maxLayers parameter.
   *
   * @param width Width of input image.
   * @param height Height of input image.
   * @param scale Scale between layers. 0 &le; scale &le; 1. Try 0.7
   * @param sigma Adjusts the amount of blur applied to each layer. If sigma &le; 0 then no blur is
   *     applied.
   * @param minSize The minimum desired image size in the pyramid
   * @param maxLayers The maximum number of layers in the pyramid.
   * @param imageType Type of image for each layer
   * @param <T> Image type
   * @return The image pyramid.
   */
  public static <T extends ImageSingleBand> PyramidFloat<T> standardPyramid(
      int width,
      int height,
      double scale,
      double sigma,
      int minSize,
      int maxLayers,
      Class<T> imageType) {
    if (scale > 1.0 || scale < 0)
      throw new IllegalArgumentException("Scale must be 0 <= scale <= 1");

    int numScales;

    if (scale == 1 || maxLayers == 1) {
      numScales = 1;
    } else if (scale == 0) {
      numScales = maxLayers;

      double desiredReduction = minSize / (double) Math.min(width, height);
      scale = Math.pow(desiredReduction, 1.0 / (numScales - 1));

    } else {
      // this is how much the input image needs to be shrunk
      double desiredReduction = minSize / (double) Math.min(width, height);

      // number the number of frames needed and round to the nearest integer
      numScales = (int) (Math.log(desiredReduction) / Math.log(scale) + 0.5);

      if (numScales > maxLayers) numScales = maxLayers;

      // compute a new scale factor using this number of scales
      scale = Math.pow(desiredReduction, 1.0 / numScales);

      // add one since the first scale is going to be the original image
      numScales++;
    }

    InterpolatePixelS<T> interp =
        FactoryInterpolation.bilinearPixelS(imageType, BorderType.EXTENDED);

    if (sigma > 0) {
      double layerSigma = sigma * Math.sqrt(Math.pow(scale, -2) - 1);

      double scaleFactors[] = new double[numScales];
      double scaleSigmas[] = new double[numScales];

      scaleFactors[0] = 1;
      scaleSigmas[0] = layerSigma;
      for (int i = 1; i < numScales; i++) {
        scaleFactors[i] = scaleFactors[i - 1] / scale;
        scaleSigmas[i] = layerSigma;
      }

      return new PyramidFloatGaussianScale<T>(interp, scaleFactors, scaleSigmas, imageType);
    } else {
      double scaleFactors[] = new double[numScales];

      scaleFactors[0] = 1;
      for (int i = 1; i < numScales; i++) {
        scaleFactors[i] = scaleFactors[i - 1] / scale;
      }

      return new PyramidFloatScale<T>(interp, scaleFactors, imageType);
    }
  }