/*
* Copyright (c) 2011-2016, Peter Abeles. All Rights Reserved.
*
* This file is part of BoofCV (http://boofcv.org).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package boofcv.abst.tracker;
import boofcv.abst.distort.FDistort;
import boofcv.struct.image.ImageBase;
import boofcv.struct.image.ImageType;
import georegression.struct.shapes.Quadrilateral_F64;
import org.junit.Test;
import java.util.Random;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* @author Peter Abeles
*/
public abstract class GenericTrackerObjectRectangleTests<T extends ImageBase> {
Random rand = new Random(234);
int width = 320;
int height = 240;
ImageType<T> imageType;
protected T input;
Quadrilateral_F64 where = new Quadrilateral_F64();
// tolerances for different tests
protected double tolTranslateSmall = 0.02;
// tolerance for scale changes
protected double tolScale = 0.1;
// tolerance for stationary test
protected double tolStationary = 1e-8;
// the initial location of the target in the image
protected Quadrilateral_F64 initRegion = rect(20,25,120,160);
protected GenericTrackerObjectRectangleTests(ImageType<T> imageType) {
this.imageType = imageType;
}
public abstract TrackerObjectQuad<T> create( ImageType<T> imageType );
@Test
public void changeInputImageSize() {
TrackerObjectQuad<T> tracker = create(imageType);
render(1,0,0);
T smaller = (T)input.createNew(width/2,height/2);
new FDistort(input,smaller).scaleExt().apply();
assertTrue(tracker.initialize(smaller, rect(20, 25, 70, 100)));
assertTrue(tracker.process(smaller, where));
assertTrue(tracker.initialize(input, initRegion));
assertTrue(tracker.process(input, where));
}
@Test
public void stationary() {
TrackerObjectQuad<T> tracker = create(imageType);
render(1,0,0);
assertTrue(tracker.initialize(input, initRegion));
assertTrue(tracker.process(input, where));
assertEquals(initRegion.a.x, where.a.x, tolStationary);
assertEquals(initRegion.a.y, where.a.y, tolStationary);
assertEquals(initRegion.c.x, where.c.x, tolStationary);
assertEquals(initRegion.c.y, where.c.y, tolStationary);
}
@Test
public void translation_small() {
TrackerObjectQuad<T> tracker = create(imageType);
render(1,0,0);
assertTrue(tracker.initialize(input, initRegion));
for( int i = 0; i < 10; i++ ) {
int tranX = 2*i;
int tranY = -2*i;
render(1,tranX,tranY);
assertTrue(tracker.process(input, where));
checkSolution(20+tranX,25+tranY,120+tranX,160+tranY,tolTranslateSmall);
}
double totalX = (where.a.x+where.b.x+where.c.x+where.d.x)/4 -
(initRegion.a.x+initRegion.b.x+initRegion.c.x+initRegion.d.x)/4;
double totalY = (where.a.y+where.b.y+where.c.y+where.d.y)/4 -
(initRegion.a.y+initRegion.b.y+initRegion.c.y+initRegion.d.y)/4;
assertEquals(2*9,totalX,4);
assertEquals(-2*9,totalY,4);
}
@Test
public void translation_large() {
TrackerObjectQuad<T> tracker = create(imageType);
render(1,0,0);
assertTrue(tracker.initialize(input, initRegion));
int tranX = 20;
int tranY = 30;
render(1,tranX,tranY);
assertTrue(tracker.process(input, where));
checkSolution(20+tranX,25+tranY,120+tranX,160+tranY,0.05);
}
@Test
public void zooming_in() {
zoom(-1);
}
@Test
public void zooming_out() {
zoom(1);
}
/**
* Zoom in and out without any visual translation of the object. e.g. the center is constant
* @param dir
*/
protected void zoom( double dir ) {
TrackerObjectQuad<T> tracker = create(imageType);
render(1,0,0);
assertTrue(tracker.initialize(input, initRegion));
double centerX = 20+50;
double centerY = 25+(160-25)/2.0;
for( int i = 0; i < 20; i++ ) {
double scale = 1 + dir*0.2*(i/9.0);
// System.out.println("scale "+scale);
double w2 = 100*scale/2.0;
double h2 = (160-25)*scale/2.0;
double tranX = centerX - centerX*scale;
double tranY = centerY - centerY*scale;
render(scale,tranX,tranY);
assertTrue(tracker.process(input, where));
checkSolution(centerX-w2,centerY-h2,centerX+w2,centerY+h2,tolScale);
}
}
/**
* See if it correctly reinitializes. Should produce identical results when given the same inputs after
* being reinitialized.
*/
@Test
public void reinitialize() {
Quadrilateral_F64 where1 = new Quadrilateral_F64();
TrackerObjectQuad<T> tracker = create(imageType);
render(1,0,0);
assertTrue(tracker.initialize(input, initRegion));
render(1,3,-3);
assertTrue(tracker.process(input, where));
render(1,6,-6);
assertTrue(tracker.process(input, where));
render(1,0,0);
assertTrue(tracker.initialize(input, initRegion));
render(1,3,-3);
assertTrue(tracker.process(input, where1));
render(1,6,-6);
assertTrue(tracker.process(input, where1));
// Might not be a perfect match due to robust algorithm not being reset to their initial state
checkSolution(where1.a.x,where1.a.y,where1.c.x,where1.c.y,0.02);
}
private void checkSolution( double x0 , double y0 , double x1 , double y1 , double fractionError ) {
// System.out.println("Expected "+x0+" "+y0+" "+x1+" "+y1);
// System.out.println("Actual "+where.a.x+" "+where.a.y+" "+where.c.x+" "+where.c.y);
double tolX = (x1-x0)*fractionError;
double tolY = (y1-y0)*fractionError;
double tol = Math.max(tolX,tolY);
assertTrue(Math.abs(where.a.x - x0) <= tol);
assertTrue(Math.abs(where.a.y - y0) <= tol);
assertTrue(Math.abs(where.c.x - x1) <= tol);
assertTrue(Math.abs(where.c.y - y1) <= tol);
}
protected abstract void render( double scale , double tranX , double tranY );
private static Quadrilateral_F64 rect( int x0 , int y0 , int x1 , int y1 ) {
return new Quadrilateral_F64(x0,y0,x1,y0,x1,y1,x0,y1);
}
}