/*
* 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.fiducial.square;
import boofcv.abst.filter.binary.InputToBinary;
import boofcv.alg.misc.ImageMiscOps;
import boofcv.alg.shapes.polygon.BinaryPolygonDetector;
import boofcv.factory.filter.binary.FactoryThresholdBinary;
import boofcv.factory.shape.ConfigPolygonDetector;
import boofcv.factory.shape.FactoryShapeDetector;
import boofcv.struct.image.GrayF32;
import boofcv.struct.image.GrayU8;
import org.junit.Test;
import java.util.Random;
import static org.junit.Assert.*;
/**
* @author Peter Abeles
*/
public class TestDetectFiducialSquareBinary {
static int gridWidth = 4;
static double borderWidth = 0.25;
static double blackBorderFraction = 0.65;
private Random rand = new Random(234);
private BinaryPolygonDetector<GrayU8> squareDetector = FactoryShapeDetector.polygon(
new ConfigPolygonDetector(false, 4,4),GrayU8.class);
private InputToBinary<GrayU8> inputToBinary = FactoryThresholdBinary.globalFixed(50, true, GrayU8.class);
/**
* Give it easy positive examples
*/
@Test
public void processSquare() {
for (int i = 0; i < 4; i++) {
GrayF32 input = create(DetectFiducialSquareBinary.w, 314);
for (int j = 0; j < i - 1; j++) {
ImageMiscOps.rotateCCW(input.clone(), input);
}
DetectFiducialSquareBinary alg =
new DetectFiducialSquareBinary<>(gridWidth,borderWidth,blackBorderFraction,
inputToBinary,squareDetector,GrayU8.class);
BaseDetectFiducialSquare.Result result = new BaseDetectFiducialSquare.Result();
assertTrue(alg.processSquare(input, result,0,0));
assertEquals(314, result.which);
assertEquals(Math.max(0,i-1), result.rotation);
}
}
/**
* Give it random noise. It should fail
*/
@Test
public void processSquare_negative() {
GrayF32 input = create(DetectFiducialSquareBinary.w, 314);
ImageMiscOps.fillUniform(input,rand,0,255);
DetectFiducialSquareBinary alg =
new DetectFiducialSquareBinary<>(gridWidth,borderWidth,blackBorderFraction,
inputToBinary,squareDetector,GrayU8.class);
BaseDetectFiducialSquare.Result result = new BaseDetectFiducialSquare.Result();
assertFalse(alg.processSquare(input, result,0,0));
}
@Test
public void getNumberOfDistinctFiducials() {
DetectFiducialSquareBinary alg =
new DetectFiducialSquareBinary<>(3,borderWidth,blackBorderFraction,
inputToBinary,squareDetector,GrayU8.class);
assertEquals(32,alg.getNumberOfDistinctFiducials());
alg = new DetectFiducialSquareBinary<>(4,borderWidth,blackBorderFraction,
inputToBinary,squareDetector,GrayU8.class);
assertEquals(4096,alg.getNumberOfDistinctFiducials());
alg = new DetectFiducialSquareBinary<>(5,borderWidth,blackBorderFraction,
inputToBinary,squareDetector,GrayU8.class);
assertEquals(2097152,alg.getNumberOfDistinctFiducials());
alg = new DetectFiducialSquareBinary<>(6,borderWidth,blackBorderFraction,
inputToBinary,squareDetector,GrayU8.class);
assertEquals(4294967296L, alg.getNumberOfDistinctFiducials());
}
/**
* See if it can detect a 3x3 grid
*/
@Test
public void checkGrid3x3() {
int number = 9;
GrayF32 input = create(DetectFiducialSquareBinary.w, number,3, borderWidth);
DetectFiducialSquareBinary alg =
new DetectFiducialSquareBinary<>(3,borderWidth,blackBorderFraction,
inputToBinary,squareDetector,GrayU8.class);
BaseDetectFiducialSquare.Result result = new BaseDetectFiducialSquare.Result();
assertTrue(alg.processSquare(input, result,0,0));
assertEquals(number, result.which);
}
/**
* See if it can detect a 3x3 grid
*/
@Test
public void checkGrid5x5() {
int number = 299382;
GrayF32 input = create(DetectFiducialSquareBinary.w, number,5, borderWidth);
DetectFiducialSquareBinary alg =
new DetectFiducialSquareBinary<>(5,borderWidth,blackBorderFraction,
inputToBinary,squareDetector,GrayU8.class);
BaseDetectFiducialSquare.Result result = new BaseDetectFiducialSquare.Result();
assertTrue(alg.processSquare(input, result,0,0));
assertEquals(number, result.which);
}
/**
* See if it can process a border that isn't 0.25
*/
@Test
public void differentBorderSizes() {
int number = 128;
double borders[] = new double[]{0.1,0.15,0.3};
for( double border : borders ) {
GrayF32 input = create(DetectFiducialSquareBinary.w, number,4, border);
DetectFiducialSquareBinary alg =
new DetectFiducialSquareBinary<>(gridWidth,border,blackBorderFraction,
inputToBinary,squareDetector,GrayU8.class);
BaseDetectFiducialSquare.Result result = new BaseDetectFiducialSquare.Result();
assertTrue(alg.processSquare(input, result,0,0));
assertEquals(number, result.which);
}
}
public static GrayF32 create(int square, int value ) {
return create(square,value,gridWidth,borderWidth);
}
public static GrayF32 create(int square, int value, int gridWidth , double borderFraction) {
int width = (int)Math.round((square*gridWidth)/(1-2.0*borderFraction));
GrayF32 ret = new GrayF32(width,width);
int s2 = (int)Math.round(ret.width*borderFraction);
int s5 = s2+square*(gridWidth-1);
int N = gridWidth*gridWidth-4;
for (int i = 0; i < N; i++) {
if( (value& (1<<i)) != 0 )
continue;
int where = index(i, gridWidth);
int x = where%gridWidth;
int y = gridWidth-1-(where/gridWidth);
x = s2 + square*x;
y = s2 + square*y;
ImageMiscOps.fillRectangle(ret,0xFF,x,y,square,square);
}
ImageMiscOps.fillRectangle(ret,0xFF,s2,s2,square,square);
ImageMiscOps.fillRectangle(ret,0xFF,s5,s5,square,square);
ImageMiscOps.fillRectangle(ret,0xFF,s5,s2,square,square);
return ret;
}
private static int index( int bit , int gridWidth ) {
int transitionBit0 = gridWidth-3;
int transitionBit1 = transitionBit0 + gridWidth*(gridWidth-2);
int transitionBit2 = transitionBit1 + gridWidth-2;
if( bit <= transitionBit0 )
bit++;
else if( bit <= transitionBit1 )
bit += 2;
else if( bit <= transitionBit2 )
bit += 3;
else
throw new RuntimeException("Bit out of range");
return bit;
}
}