/* * 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.abst.filter.derivative.ImageGradient; import boofcv.alg.misc.ImageMiscOps; import boofcv.alg.tracker.klt.PkltConfig; import boofcv.alg.tracker.klt.PyramidKltTracker; import boofcv.alg.transform.pyramid.PyramidOps; import boofcv.factory.filter.derivative.FactoryDerivative; import boofcv.factory.tracker.FactoryTrackerAlg; import boofcv.factory.transform.pyramid.FactoryPyramid; import boofcv.struct.flow.ImageFlow; import boofcv.struct.image.GrayF32; import boofcv.struct.pyramid.ImagePyramid; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** * @author Peter Abeles */ public class TestDenseOpticalFlowKlt { GrayF32 image0 = new GrayF32(30,40); GrayF32 image1 = new GrayF32(30,40); ImagePyramid<GrayF32> prev; GrayF32[] prevDerivX; GrayF32[] prevDerivY; ImagePyramid<GrayF32> curr; ImageGradient<GrayF32, GrayF32> gradient = FactoryDerivative.sobel(GrayF32.class, GrayF32.class); PkltConfig config = new PkltConfig(); @Before public void setup() { config.pyramidScaling = new int[]{1,2}; config.config.maxPerPixelError = 15; prev = FactoryPyramid.discreteGaussian(config.pyramidScaling, -1, 2, true, GrayF32.class); curr = FactoryPyramid.discreteGaussian(config.pyramidScaling, -1, 2, true, GrayF32.class); prev.process(image0); curr.process(image0); prevDerivX = PyramidOps.declareOutput(prev,GrayF32.class); prevDerivY = PyramidOps.declareOutput(prev,GrayF32.class); } private void processInputImage() { prev.process(image0); curr.process(image1); PyramidOps.gradient(prev, gradient, prevDerivX,prevDerivY); } protected DenseOpticalFlowKlt<GrayF32,GrayF32> createAlg() { PyramidKltTracker<GrayF32, GrayF32> tracker = FactoryTrackerAlg.kltPyramid(config.config, GrayF32.class, GrayF32.class); return new DenseOpticalFlowKlt<>(tracker, config.pyramidScaling.length, 3); } /** * Very simple positive case */ @Test public void positive() { ImageMiscOps.fillRectangle(image0,50,10,12,2,2); ImageMiscOps.fillRectangle(image1,50,11,13,2,2); processInputImage(); DenseOpticalFlowKlt<GrayF32,GrayF32> alg = createAlg(); ImageFlow flow = new ImageFlow(image0.width,image0.height); flow.invalidateAll(); alg.process(prev,prevDerivX,prevDerivY,curr,flow); // no texture in the image so KLT can't do anything check(flow.get(0,0),false,0,0); check(flow.get(29,39),false,0,0); // there is texture at the target check(flow.get(10,12),true,1,1); check(flow.get(11,12),true,1,1); check(flow.get(10,13),true,1,1); check(flow.get(11,13),true,1,1); } private void check( ImageFlow.D flow , boolean valid , float x , float y ) { assertEquals(valid,flow.isValid()); if( valid ) { assertEquals(x,flow.x,0.05f); assertEquals(y,flow.y,0.05f); } } /** * Very simple negative case. The second image is blank so it should fail at tracking */ @Test public void negative() { ImageMiscOps.fillRectangle(image0, 200, 7, 9, 5, 5); processInputImage(); DenseOpticalFlowKlt<GrayF32,GrayF32> alg = createAlg(); ImageFlow flow = new ImageFlow(image0.width,image0.height); alg.process(prev,prevDerivX,prevDerivY,curr,flow); int totalFail = 0; for (int i = 0; i < flow.data.length; i++) { if( !flow.data[i].isValid() ) { totalFail++; } } assertTrue( totalFail/(double)flow.data.length >= 0.90 ); } }