
boofcv.alg.flow.HornSchunck Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of boofcv-feature Show documentation
Show all versions of boofcv-feature Show documentation
BoofCV is an open source Java library for real-time computer vision and robotics applications.
The newest version!
/*
* Copyright (c) 2021, 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.InputSanityCheck;
import boofcv.struct.flow.ImageFlow;
import boofcv.struct.image.ImageBase;
import boofcv.struct.image.ImageType;
/**
*
* This is Horn-Schunck's well known work [1] for dense optical flow estimation. It is based off the following
* equation Ex*u + Ey*v + Et = 0, where (u,v) is the estimated flow for a single pixel, and (Ex,Ey) is the pixel's
* gradient and Et is the grave in intensity value. It is assumed that each pixel maintains a constant intensity
* and that changes in flow are smooth. This implementation is faithful to the original
* work and does not make any effort to improve its performance using more modern techniques.
*
*
*
* [1] Horn, Berthold K., and Brian G. Schunck. "Determining optical flow."
* 1981 Technical Symposium East. International Society for Optics and Photonics, 1981.
*
*
* @author Peter Abeles
*/
public abstract class HornSchunck, D extends ImageBase> {
// used to weight the error of image brightness and smoothness of velocity flow
protected float alpha2;
// Number of iterations
protected int numIterations;
// storage for the average flow
protected ImageFlow averageFlow = new ImageFlow(1, 1);
// If the output should be cleared each time a new image is processed or used as an initial estimate
protected boolean resetOutput = true;
// storage for derivatives
protected D derivX;
protected D derivY;
protected D derivT;
/**
* Constructor
*
* @param alpha Larger values place more importance on flow smoothness consistency over brightness consistency. Try 20
* @param numIterations Number of iterations. Try 1000
*/
protected HornSchunck( float alpha, int numIterations, ImageType derivType ) {
this.alpha2 = alpha*alpha;
this.numIterations = numIterations;
derivX = derivType.createImage(1, 1);
derivY = derivType.createImage(1, 1);
derivT = derivType.createImage(1, 1);
}
/**
* changes the maximum number of iterations
*
* @param numIterations maximum number of iterations
*/
public void setNumIterations( int numIterations ) {
this.numIterations = numIterations;
}
/**
* Computes dense optical flow from the first image's gradient and the difference between
* the second and the first image.
*
* @param image1 First image
* @param image2 Second image
* @param output Found dense optical flow
*/
public void process( T image1, T image2, ImageFlow output ) {
InputSanityCheck.checkSameShape(image1, image2);
derivX.reshape(image1.width, image1.height);
derivY.reshape(image1.width, image1.height);
derivT.reshape(image1.width, image1.height);
averageFlow.reshape(output.width, output.height);
if (resetOutput)
output.fillZero();
computeDerivX(image1, image2, derivX);
computeDerivY(image1, image2, derivY);
computeDerivT(image1, image2, derivT);
findFlow(derivX, derivY, derivT, output);
}
protected abstract void computeDerivX( T image1, T image2, D derivX );
protected abstract void computeDerivY( T image1, T image2, D derivY );
protected abstract void computeDerivT( T image1, T image2, D derivT );
/**
* Inner function for computing optical flow
*/
protected abstract void findFlow( D derivX, D derivY, D derivT, ImageFlow output );
/**
* Computes average flow using an 8-connect neighborhood for the inner image
*/
protected static void innerAverageFlow( ImageFlow flow, ImageFlow averageFlow ) {
int endX = flow.width - 1;
int endY = flow.height - 1;
for (int y = 1; y < endY; y++) {
int index = flow.width*y + 1;
for (int x = 1; x < endX; x++, index++) {
ImageFlow.D average = averageFlow.data[index];
ImageFlow.D f0 = flow.data[index - 1];
ImageFlow.D f1 = flow.data[index + 1];
ImageFlow.D f2 = flow.data[index - flow.width];
ImageFlow.D f3 = flow.data[index + flow.width];
ImageFlow.D f4 = flow.data[index - 1 - flow.width];
ImageFlow.D f5 = flow.data[index + 1 - flow.width];
ImageFlow.D f6 = flow.data[index - 1 + flow.width];
ImageFlow.D f7 = flow.data[index + 1 + flow.width];
average.x = 0.1666667f*(f0.x + f1.x + f2.x + f3.x) + 0.08333333f*(f4.x + f5.x + f6.x + f7.x);
average.y = 0.1666667f*(f0.y + f1.y + f2.y + f3.y) + 0.08333333f*(f4.y + f5.y + f6.y + f7.y);
}
}
}
/**
* Computes average flow using an 8-connect neighborhood for the image border
*/
protected static void borderAverageFlow( ImageFlow flow, ImageFlow averageFlow ) {
for (int y = 0; y < flow.height; y++) {
computeBorder(flow, averageFlow, 0, y);
computeBorder(flow, averageFlow, flow.width - 1, y);
}
for (int x = 1; x < flow.width - 1; x++) {
computeBorder(flow, averageFlow, x, 0);
computeBorder(flow, averageFlow, x, flow.height - 1);
}
}
protected static void computeBorder( ImageFlow flow, ImageFlow averageFlow, int x, int y ) {
ImageFlow.D average = averageFlow.get(x, y);
ImageFlow.D f0 = getExtend(flow, x - 1, y);
ImageFlow.D f1 = getExtend(flow, x + 1, y);
ImageFlow.D f2 = getExtend(flow, x, y - 1);
ImageFlow.D f3 = getExtend(flow, x, y + 1);
ImageFlow.D f4 = getExtend(flow, x - 1, y - 1);
ImageFlow.D f5 = getExtend(flow, x + 1, y - 1);
ImageFlow.D f6 = getExtend(flow, x - 1, y + 1);
ImageFlow.D f7 = getExtend(flow, x + 1, y + 1);
average.x = 0.1666667f*(f0.x + f1.x + f2.x + f3.x) + 0.08333333f*(f4.x + f5.x + f6.x + f7.x);
average.y = 0.1666667f*(f0.y + f1.y + f2.y + f3.y) + 0.08333333f*(f4.y + f5.y + f6.y + f7.y);
}
protected static ImageFlow.D getExtend( ImageFlow flow, int x, int y ) {
if (x < 0) x = 0;
else if (x >= flow.width) x = flow.width - 1;
if (y < 0) y = 0;
else if (y >= flow.height) y = flow.height - 1;
return flow.unsafe_get(x, y);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy