/* * 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.flow; import boofcv.alg.misc.GImageMiscOps; import boofcv.core.image.GeneralizedImageOps; import boofcv.struct.flow.ImageFlow; import boofcv.struct.image.ImageGray; import boofcv.testing.BoofTesting; import org.junit.Test; import java.util.Random; import static org.junit.Assert.*; /** * @author Peter Abeles */ public abstract class GeneralDenseOpticalFlowChecks<T extends ImageGray> { Random rand = new Random(234); Class<T> imageType; T orig; T shifted; ImageFlow found; boolean justCorrectSign = false; protected GeneralDenseOpticalFlowChecks(Class<T> imageType) { this.imageType = imageType; orig = GeneralizedImageOps.createSingleBand(imageType,20,25); shifted = GeneralizedImageOps.createSingleBand(imageType,20,25); found = new ImageFlow(20,25); GImageMiscOps.fillUniform(orig,rand,0,256); } public void setJustCorrectSign(boolean justCorrectSign) { this.justCorrectSign = justCorrectSign; } public void allTests( boolean justCorrectSign ) { this.justCorrectSign = justCorrectSign; allTests(); } public void allTests() { processEdges(); checkPlanarMotion(); checkChangeInputSize(); checkSubImage(); } public abstract DenseOpticalFlow<T> createAlg( Class<T> imageType ); /** * Makes sure it attempts to compute flow through out the whole image. Specially checks the image border * to see if those are skipped */ @Test public void processEdges() { shift(orig,1,0,shifted); DenseOpticalFlow<T> alg = createAlg( imageType ); found.invalidateAll(); alg.process(orig,shifted,found); int count0 = 0, count1 = 0; for( int x = 0; x < shifted.width; x++ ) { if( found.get(x,0).isValid() ) count0++; if( found.get(x,found.height-2).isValid() ) count1++; } assertTrue(count0>=found.width/3); assertTrue(count1>=found.width/3); // process it again so that there should be an obvious solution along the left and right sides found.invalidateAll(); shift(orig, 0, 1, shifted); alg.process(orig,shifted,found); int count2 = 0, count3 = 0; for( int y = 0; y < shifted.height; y++ ) { if( found.get(0,y).isValid() ) count2++; if( found.get(found.width-2,y).isValid() ) count3++; } assertTrue(count2>=found.height/3); assertTrue(count3>=found.height/3); } /** * Very simple test where every pixel moves at the same speed along x and or y direction */ @Test public void checkPlanarMotion() { for( int dy = -1; dy <= 1; dy++ ){ for( int dx = -1; dx <= 1; dx++ ){ DenseOpticalFlow<T> alg = createAlg( imageType ); shift(orig,dx,dy,shifted); found.invalidateAll(); alg.process(orig,shifted,found); ImageFlow.D flow = found.get(10,10); assertTrue(flow.isValid()); if( justCorrectSign ) { // if the two flows are in agreement then sum will be positive float sum = 0; for( int y = 0; y < found.height; y++ ) { for (int x = 0; x < found.width; x++) { flow = found.get(x,y); sum += flow.x*dx; sum += flow.y*dy; } } assertTrue( sum >= 0 ); } else { assertEquals(dx, flow.x, 0.2); assertEquals(dy, flow.y, 0.2); } } } } /** * Does it handle the input image size being changed after the first image? */ @Test public void checkChangeInputSize() { DenseOpticalFlow<T> alg = createAlg( imageType ); alg.process(orig,shifted,found); T larger0 = GeneralizedImageOps.createSingleBand(imageType,40,35); T larger1 = GeneralizedImageOps.createSingleBand(imageType,40,35); // if it doesn't blow up it worked alg.process(larger0,larger1,new ImageFlow(40,35)); } @Test public void checkSubImage() { DenseOpticalFlow<T> alg = createAlg( imageType ); shift(orig,1,-1,shifted); alg.process(orig,shifted,found); // should produce identical solution T subOrig = BoofTesting.createSubImageOf(orig); T subShifted = BoofTesting.createSubImageOf(shifted); ImageFlow found2 = new ImageFlow(found.width,found.height); alg.process(subOrig,subShifted,found2); for( int y = 0; y < found.height; y++ ) { for( int x = 0; x < found.width; x++ ){ ImageFlow.D a = found.get(x,y); ImageFlow.D b = found2.get(x,y); if( a.isValid() ) { assertTrue( a.x == b.x ); assertTrue( a.y == b.y ); } else { assertFalse(b.isValid()); } } } } private void shift( T input , int dx , int dy , T output ) { int w = input.width; int h = input.height; if( dx >= 0 ){ output.subimage(dx,0,w,h).setTo(input.subimage(0,0,w-dx,h)); output.subimage(0,0,dx,h).setTo(input.subimage(w-dx,0,w,h)); } else { output.subimage(0,0,w+dx,h).setTo(input.subimage(-dx,0,w,h)); output.subimage(w+dx,0,w,h).setTo(input.subimage(0,0,-dx,h)); } T tmp = (T)output.clone(); if( dy >= 0 ){ output.subimage(0,dy,w,h).setTo(tmp.subimage(0,0,w,h-dy)); output.subimage(0,0,w,dy).setTo(tmp.subimage(0,h-dy,w,h)); } else { output.subimage(0,0,w,h+dy).setTo(tmp.subimage(0,-dy,w,h)); output.subimage(0,h+dy,w,h).setTo(tmp.subimage(0,0,w,-dy)); } } }