/*
* 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.flow;
import boofcv.alg.misc.GImageMiscOps;
import boofcv.core.image.GeneralizedImageOps;
import boofcv.struct.flow.ImageFlow;
import boofcv.struct.image.GrayF32;
import boofcv.struct.image.ImageGray;
import boofcv.testing.BoofTesting;
import org.junit.Test;
import java.util.Random;
import static org.junit.Assert.assertTrue;
/**
* @author Peter Abeles
*/
public abstract class ChecksHornSchunck<T extends ImageGray, D extends ImageGray> {
Class<T> imageType;
Class<D> derivType;
Random rand = new Random(234);
int width = 20;
int height = 30;
protected ChecksHornSchunck(Class<T> imageType, Class<D> derivType) {
this.imageType = imageType;
this.derivType = derivType;
}
public abstract HornSchunck<T,D> createAlg();
/**
* Manually construct the input so that it has a known and easily understood output
*/
@Test
public void process() {
HornSchunck<T,D> alg = createAlg();
T image1 = GeneralizedImageOps.createSingleBand(imageType, width, height);
T image2 = GeneralizedImageOps.createSingleBand(imageType,width,height);
ImageFlow output = new ImageFlow(width,height);
GImageMiscOps.fillRectangle(image1, 100, 10, 0, 20, 30);
GImageMiscOps.fillRectangle(image2, 100, 11, 0, 20, 30);
alg.process(image1, image2, output);
for( int y = 0; y < height-1; y++ ) {
assertTrue( output.get(9,y).x > 0.9);
assertTrue( Math.abs(output.get(10,y).y) < 0.1 );
assertTrue( output.get(10,y).x > 0.9);
assertTrue( Math.abs(output.get(11,y).y) < 0.1 );
}
}
@Test
public void computeDerivX() {
Point[] samples = new Point[8];
float signs[] = new float[]{1,-1,1,-1,1,-1,1,-1};
samples[0] = new Point(1,0,0);
samples[1] = new Point(0,0,0);
samples[2] = new Point(1,1,0);
samples[3] = new Point(0,1,0);
samples[4] = new Point(1,0,1);
samples[5] = new Point(0,0,1);
samples[6] = new Point(1,1,1);
samples[7] = new Point(0,1,1);
HornSchunck<T,D> alg = createAlg();
T image1 = GeneralizedImageOps.createSingleBand(imageType, width, height);
T image2 = GeneralizedImageOps.createSingleBand(imageType,width,height);
D found = GeneralizedImageOps.createSingleBand(derivType, width, height);
GImageMiscOps.fillUniform(image1,rand,0,200);
GImageMiscOps.fillUniform(image2,rand,0,200);
alg.computeDerivX(image1,image2,found);
GrayF32 expected = computeExpected(image1,image2,samples,signs);
BoofTesting.assertEquals(expected, found, 1);
}
@Test
public void computeDerivY() {
Point[] samples = new Point[8];
float signs[] = new float[]{1,-1,1,-1,1,-1,1,-1};
samples[0] = new Point(0,1,0);
samples[1] = new Point(0,0,0);
samples[2] = new Point(1,1,0);
samples[3] = new Point(1,0,0);
samples[4] = new Point(0,1,1);
samples[5] = new Point(0,0,1);
samples[6] = new Point(1,1,1);
samples[7] = new Point(1,0,1);
HornSchunck<T,D> alg = createAlg();
T image1 = GeneralizedImageOps.createSingleBand(imageType, width, height);
T image2 = GeneralizedImageOps.createSingleBand(imageType,width,height);
D found = GeneralizedImageOps.createSingleBand(derivType, width, height);
GImageMiscOps.fillUniform(image1,rand,0,200);
GImageMiscOps.fillUniform(image2,rand,0,200);
alg.computeDerivY(image1,image2,found);
GrayF32 expected = computeExpected(image1,image2,samples,signs);
BoofTesting.assertEquals(expected,found,1);
}
@Test
public void computeDerivT() {
Point[] samples = new Point[8];
float signs[] = new float[]{1,-1,1,-1,1,-1,1,-1};
samples[0] = new Point(0,0,1);
samples[1] = new Point(0,0,0);
samples[2] = new Point(1,0,1);
samples[3] = new Point(1,0,0);
samples[4] = new Point(0,1,1);
samples[5] = new Point(0,1,0);
samples[6] = new Point(1,1,1);
samples[7] = new Point(1,1,0);
HornSchunck<T,D> alg = createAlg();
T image1 = GeneralizedImageOps.createSingleBand(imageType, width, height);
T image2 = GeneralizedImageOps.createSingleBand(imageType,width,height);
D found = GeneralizedImageOps.createSingleBand(derivType, width, height);
GImageMiscOps.fillUniform(image1,rand,0,200);
GImageMiscOps.fillUniform(image2,rand,0,200);
alg.computeDerivT(image1,image2,found);
GrayF32 expected = computeExpected(image1,image2,samples,signs);
BoofTesting.assertEquals(expected,found,1);
}
private GrayF32 computeExpected(ImageGray image1, ImageGray image2 ,
Point[] samples , float signs[] ) {
GrayF32 ret = new GrayF32(width,height);
for (int y = 0; y < image1.height; y++) {
for (int x = 0; x < image1.width; x++) {
float total = 0;
for( int i = 0; i < signs.length; i++ ) {
Point p = samples[i];
float s = signs[i];
if( p.k == 0 ) {
total += s*safeGet(image1,x+p.x,y+p.y);
} else {
total += s*safeGet(image2,x+p.x,y+p.y);
}
}
total *= 0.25;
ret.set(x,y,total);
}
}
return ret;
}
private float safeGet(ImageGray image , int x , int y ) {
if( x < 0 ) x = 0;
if( x >= image.width) x = image.width-1;
if( y < 0 ) y = 0;
if( y >= image.height) y = image.height-1;
return (float) GeneralizedImageOps.get(image, x, y);
}
private static class Point {
int x,y,k;
private Point(int x, int y, int k) {
this.x = x;
this.y = y;
this.k = k;
}
}
}