boofcv.alg.disparity.sgm.SgmDisparitySelector Maven / Gradle / Ivy
/*
* Copyright (c) 2011-2020, 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.disparity.sgm;
import boofcv.struct.image.GrayU16;
import boofcv.struct.image.GrayU8;
import boofcv.struct.image.Planar;
/**
* Selects the best disparity for each pixel from aggregated SGM cost. If no valid match is or can be found then
* it is set to {@link #invalidDisparity}.
*
* @author Peter Abeles
*/
public class SgmDisparitySelector {
protected final SgmHelper helper = new SgmHelper();
// tolerance for right to left validation. if < 0 then it's disabled
protected int rightToLeftTolerance = 1;
// Maximum allowed error
int maxError = Integer.MAX_VALUE;
double textureThreshold = 0.0;
// The minimum possible disparity
int disparityMin = 0;
// specified which value was used to indivate that a disparity is invalid
int invalidDisparity = -1;
// Shape of the input tensor
// lengthD is the disparity range being considered
int lengthY, lengthX, lengthD;
/**
* Given the aggregated cost compute the best disparity to pixel level accuracy for all pixels
*
* @param aggregatedYXD (Input) Aggregated disparity cost for each pixel
* @param disparity (output) selected disparity
*/
public void select( Planar costYXD, Planar aggregatedYXD, GrayU8 disparity ) {
setup(aggregatedYXD);
// Ensure that the output matches the input
disparity.reshape(lengthX, lengthY);
for (int y = 0; y < lengthY; y++) {
GrayU16 aggregatedXD = aggregatedYXD.getBand(y);
// if 'x' is less than minDisparity then that's nothing that it can compare against
for (int x = 0; x < disparityMin; x++) {
disparity.unsafe_set(x, y, invalidDisparity);
}
for (int x = disparityMin; x < lengthX; x++) {
disparity.unsafe_set(x, y, findBestDisparity(x, aggregatedXD));
}
}
}
/**
* Sets up internal data structures based on the aggregated cost
*/
void setup( Planar aggregatedYXD ) {
this.lengthY = aggregatedYXD.getNumBands();
this.lengthX = aggregatedYXD.height;
this.lengthD = aggregatedYXD.width;
this.invalidDisparity = invalidGivenRange(lengthD);
helper.configure(lengthX, disparityMin, lengthD);
if (invalidDisparity > 255)
throw new IllegalArgumentException("Disparity range is too great. Must be < 256 not " + lengthD);
}
/**
* Selects the disparity for the specified pixel using a winner takes all strategy
*
* @param x x-coordinate in original image coordinates. DO NOT SUBTRACT disparityMin
*/
int findBestDisparity( int x, GrayU16 aggregatedXD ) {
// The maximum disparity range that can be considered at 'x'
int localMaxRange = helper.localDisparityRangeLeft(x);
int bestScore = Integer.MAX_VALUE;
int bestRange = invalidDisparity;
// Select the disparity with the lowest aggregated cost
final int idx = aggregatedXD.getIndex(0, x - disparityMin);
for (int d = 0; d < localMaxRange; d++) {
int cost = aggregatedXD.data[idx + d] & 0xFFFF;
if (cost < bestScore) {
bestScore = cost;
bestRange = d;
}
}
if (bestRange == invalidDisparity)
return invalidDisparity;
// See if the maximum error is exceeded
if (bestScore > maxError) {
return invalidDisparity;
}
// right to left consistency check
if (rightToLeftTolerance >= 0) {
// TODO why isn't this pruning the left side of the disparity image as much as block matching does?
// Not nearly as effective at pruning as it is with
int bestRange_R_to_L = selectRightToLeft(x - bestRange - disparityMin, aggregatedXD);
if (Math.abs(bestRange_R_to_L - bestRange) > rightToLeftTolerance)
return invalidDisparity;
}
// See if the best solution is ambiguous
if (localMaxRange > 3 && textureThreshold > 0) {
// find the second best disparity value and exclude its neighbors
int secondBest = Integer.MAX_VALUE;
for (int d = 0; d < bestRange - 1; d++) {
int v = aggregatedXD.data[idx + d] & 0xFFFF;
if (v < secondBest) {
secondBest = v;
}
}
for (int d = bestRange + 2; d < localMaxRange; d++) {
int v = aggregatedXD.data[idx + d] & 0xFFFF;
if (v < secondBest) {
secondBest = v;
}
}
// similar scores indicate lack of texture
// C = (C2-C1)/C1
if (secondBest - bestScore <= textureThreshold*bestScore)
bestRange = invalidDisparity;
// TODO try this same check for right to left disparity
}
return bestRange;
}
/**
* Finds the best fit region going from the column (x-coordinate) in right image to left. To find
* the pixel in left image that's compared against a pixel in right image, take it's x-coordinate then add
* the disparity. e.g. x=10 in right matches x=15 and d=5 in left
*
* @param x x-coordinate of point in right image
* @return best fit disparity from right to left
*/
private int selectRightToLeft( int x, GrayU16 aggregatedXD ) {
// The range of disparities it can search from right to left
int localDisparityRange = helper.localDisparityRangeRight(x);
if (localDisparityRange <= 0) // it can't perform the check because it's too far right, just give it a pass
return x;
// Note in the cost x=0 is really disparityMin, this is the idx at x+disparityMin
int idx = aggregatedXD.getIndex(0, x); // disparity of zero at col
// best column in left image that it matches up with col in right
int bestD = 0;
float scoreBest = aggregatedXD.data[idx] & 0xFFFF;
for (int i = 1; i < localDisparityRange; i++) {
idx += lengthD; // go to index next x-coordinate
int s = aggregatedXD.data[idx + i] & 0xFFFF;
if (s < scoreBest) {
scoreBest = s;
bestD = i;
}
}
return bestD;
}
public int getRightToLeftTolerance() {
return rightToLeftTolerance;
}
public void setRightToLeftTolerance( int rightToLeftTolerance ) {
this.rightToLeftTolerance = rightToLeftTolerance;
}
public int getMaxError() {
return maxError;
}
public void setMaxError( int maxError ) {
this.maxError = maxError;
}
public int getDisparityMin() {
return disparityMin;
}
public void setDisparityMin( int disparityMin ) {
this.disparityMin = disparityMin;
}
public int getInvalidDisparity() {
return invalidDisparity;
}
public double getTextureThreshold() {
return textureThreshold;
}
public void setTextureThreshold( double textureThreshold ) {
this.textureThreshold = textureThreshold;
}
/**
* Convenience function to make it clear what the value assigned to an invalid disparity is. Any
* value
*/
public static int invalidGivenRange( int disparityRange ) {
return disparityRange;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy