@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 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); }
@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); } } }
@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 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 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 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 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); }
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); }
/** * 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); }