/*
* 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.alg.tracker.meanshift;
import boofcv.alg.interpolate.InterpolatePixelMB;
import boofcv.alg.misc.GImageMiscOps;
import boofcv.core.image.border.BorderType;
import boofcv.core.image.border.ImageBorder;
import boofcv.factory.interpolate.FactoryInterpolation;
import boofcv.struct.RectangleRotate_F32;
import boofcv.struct.image.*;
import georegression.struct.point.Point2D_F32;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* @author Peter Abeles
*/
public class TestLocalWeightedHistogramRotRect {
Random rand = new Random(234);
/**
* Crudely checks to see that the center has the most weight
*/
@Test
public void computeWeights() {
int w = 9;
LocalWeightedHistogramRotRect alg = new LocalWeightedHistogramRotRect(w,3,12,3,255,null);
float maxW = alg.weights[w*6 + 6];
assertTrue(alg.weights[0] < maxW);
assertTrue(alg.weights[w-1] < maxW);
assertTrue(alg.weights[(w-1)*w + w-1] < maxW);
assertTrue(alg.weights[(w-1)*w] < maxW);
}
@Test
public void createSamplePoints() {
int w = 9;
LocalWeightedHistogramRotRect alg = new LocalWeightedHistogramRotRect(w,3,12,3,255,null);
int i = 0;
for( int y = 0; y < w; y++ ) {
for( int x = 0; x < w; x++ , i++ ) {
Point2D_F32 p = (Point2D_F32)alg.samplePts.get(i);
float expectedX = (x/(float)(w-1))-0.5f;
float expectedY = (y/(float)(w-1))-0.5f;
assertEquals(expectedX,p.x,1e-4f);
assertEquals(expectedY,p.y,1e-4f);
}
}
}
@Test
public void computeHistogram() {
Planar<GrayF32> image = new Planar<>(GrayF32.class,40,50,3);
InterpolatePixelMB interp = FactoryInterpolation.createPixelPL(FactoryInterpolation.bilinearPixelS(
GrayF32.class, BorderType.EXTENDED));
GImageMiscOps.fillUniform(image,rand,0,100);
interp.setImage(image);
RectangleRotate_F32 rect = new RectangleRotate_F32(20,25,10,15,0);
LocalWeightedHistogramRotRect alg = new LocalWeightedHistogramRotRect(10,3,12,3,255,interp);
alg.computeHistogram(image, rect);
float hist[] = alg.getHistogram().clone();
int histIndex[] = alg.getSampleHistIndex().clone();
// crude sanity check
int numNotZero = 0;
for( int i = 0; i < hist.length; i++ )
if( hist[i] != 0 ) numNotZero++;
assertTrue(numNotZero > 0);
for( int i = 0; i < histIndex.length; i++ )
assertTrue(histIndex[i] != 0);
// should produce the same answer after a second call
alg.computeHistogram(image,rect);
for( int i = 0; i < hist.length; i++ )
assertEquals(hist[i], alg.getHistogram()[i], 1e-4);
for( int i = 0; i < histIndex.length; i++ )
assertEquals(histIndex[i], alg.getSampleHistIndex()[i], 1e-4);
}
/**
* When given a region entirely inside, both inside and outside should produce identical solutions
*/
@Test
public void computeHistogramBorder_compare() {
Planar<GrayF32> image = new Planar<>(GrayF32.class,40,50,3);
InterpolatePixelMB interp = FactoryInterpolation.createPixelPL(FactoryInterpolation.bilinearPixelS(
GrayF32.class, BorderType.EXTENDED));
GImageMiscOps.fillUniform(image,rand,0,100);
interp.setImage(image);
RectangleRotate_F32 rect = new RectangleRotate_F32(20,25,10,15,0);
LocalWeightedHistogramRotRect alg = new LocalWeightedHistogramRotRect(10,3,12,3,255,interp);
alg.computeHistogramBorder(image,rect);
int[] insideHistIndex = alg.sampleHistIndex.clone();
float[] insideHist = alg.histogram.clone();
alg = new LocalWeightedHistogramRotRect(10,3,12,3,255,interp);
alg.computeHistogramInside(rect);
for( int i = 0; i < insideHist.length; i++ ) {
assertEquals(insideHist[i],alg.histogram[i],1e-4f);
}
for( int i = 0; i < insideHistIndex.length; i++ ) {
assertEquals(insideHistIndex[i],alg.sampleHistIndex[i],1e-4f);
}
}
/**
* Make sure it handles pixels outside the image correctly
*/
@Test
public void computeHistogramBorder_outside() {
int numSamples = 10;
InterleavedF32 image = new InterleavedF32(40,50,3);
DummyInterpolate interp = new DummyInterpolate();
RectangleRotate_F32 rect = new RectangleRotate_F32(4,5,10,20,0);
LocalWeightedHistogramRotRect alg = new LocalWeightedHistogramRotRect(numSamples,3,12,3,255,interp);
alg.c = 1; alg.s = 0;
alg.computeHistogramBorder(image,rect);
int numInside = 0;
int i = 0;
for( int y = 0; y < numSamples; y++ ) {
for( int x = 0; x < numSamples; x++ , i++ ) {
alg.squareToImageSample((float)x/(numSamples-1)-0.5f,(float)y/(numSamples-1)-0.5f,rect);
boolean inside = alg.imageX >= 0 && alg.imageX < 40 && alg.imageY >= 0 && alg.imageY < 50;
if( inside ) {
numInside++;
assertTrue(alg.sampleHistIndex[i] >= 0 );
assertTrue(alg.histogram[alg.sampleHistIndex[i]] > 0 );
} else {
assertTrue(alg.sampleHistIndex[i] == -1 );
}
}
}
assertTrue(numInside != numSamples*numSamples);
}
@Test
public void computeHistogramBin() {
LocalWeightedHistogramRotRect alg = new LocalWeightedHistogramRotRect(10,3,12,3,255,null);
float div = alg.maxPixelValue/12;
assertEquals(0,alg.computeHistogramBin(new float[]{0,0,0}));
assertEquals(1+2*12+3*144,alg.computeHistogramBin(new float[]{1*div,2*div,3*div}));
}
@Test
public void isInFastBounds() {
DummyInterpolate interp = new DummyInterpolate();
RectangleRotate_F32 rect = new RectangleRotate_F32(4,5,10,20,0);
LocalWeightedHistogramRotRect alg = new LocalWeightedHistogramRotRect(10,3,12,3,255,interp);
alg.c = 1; alg.s = 0;
assertTrue(alg.isInFastBounds(rect));
// see if it checked to see if the four corners are in bounds
assertEquals(4,interp.list.size());
Point2D_F32 p0 = interp.list.get(0);
Point2D_F32 p1 = interp.list.get(1);
Point2D_F32 p2 = interp.list.get(2);
Point2D_F32 p3 = interp.list.get(3);
// the order really doesn't matter, but easier to code the test this way
assertEquals(4f-4.5f,p0.x,1e-4f);
assertEquals(5f-9.5f,p0.y,1e-4f);
assertEquals(4f-4.5f,p1.x,1e-4f);
assertEquals(5f+9.5f,p1.y,1e-4f);
assertEquals(4f+4.5f,p2.x,1e-4f);
assertEquals(5f+9.5f,p2.y,1e-4f);
assertEquals(4f+4.5f,p3.x,1e-4f);
assertEquals(5f-9.5f,p3.y,1e-4f);
}
@Test
public void normalizeHistogram() {
LocalWeightedHistogramRotRect alg = new LocalWeightedHistogramRotRect(10,3,12,3,255,null);
float total = 0;
for( int i = 0; i < alg.histogram.length; i++ ) {
total += alg.histogram[i] = i+1;
}
float expected[] = alg.histogram.clone();
alg.normalizeHistogram();
for( int i = 0; i < alg.histogram.length; i++ ) {
assertEquals( expected[i]/total,alg.histogram[i],1e-4);
}
}
@Test
public void squareToImage() {
RectangleRotate_F32 rect = new RectangleRotate_F32(4,5,10,20,0);
LocalWeightedHistogramRotRect alg = new LocalWeightedHistogramRotRect(10,3,12,3,255,null);
alg.c = 1; alg.s = 0;
alg.squareToImageSample(0,0,rect);
assertEquals(4,alg.imageX,1e-4f);
assertEquals(5,alg.imageY,1e-4f);
alg.squareToImageSample(-0.5f,0.5f,rect);
assertEquals(4f-4.5f,alg.imageX,1e-4f);
assertEquals(5f+9.5f,alg.imageY,1e-4f);
alg.c = 0.5f; alg.s = -0.5f;
alg.squareToImageSample(-0.5f,0.5f,rect);
// -4.5 + 9.5
assertEquals(4f-2.25f+4.75f,alg.imageX,1e-4f);
assertEquals(5f+2.25f+4.75f,alg.imageY,1e-4f);
}
static class DummyInterpolate implements InterpolatePixelMB {
List<Point2D_F32> list = new ArrayList<>();
@Override
public void setBorder(ImageBorder border) {}
@Override
public ImageBorder getBorder() {return null;}
@Override
public void setImage(ImageBase image) {}
@Override
public ImageBase getImage() {
return null;
}
@Override
public boolean isInFastBounds(float x, float y) {
list.add( new Point2D_F32(x,y));
return true;
}
@Override
public int getFastBorderX() {
return 0;
}
@Override
public int getFastBorderY() {
return 0;
}
@Override
public ImageType getImageType() {
return null;
}
@Override
public void get(float x, float y, float[] values) {}
@Override
public void get_fast(float x, float y, float[] values) {}
}
}