/*
* 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.fiducial;
import boofcv.abst.fiducial.calib.ConfigChessboard;
import boofcv.abst.fiducial.calib.ConfigCircleAsymmetricGrid;
import boofcv.abst.fiducial.calib.ConfigSquareGrid;
import boofcv.abst.fiducial.calib.ConfigSquareGridBinary;
import boofcv.abst.geo.calibration.DetectorFiducialCalibration;
import boofcv.alg.geo.calibration.CalibrationObservation;
import boofcv.core.image.GConvertImage;
import boofcv.factory.fiducial.FactoryFiducialCalibration;
import boofcv.struct.geo.Point2D3D;
import boofcv.struct.geo.PointIndex2D_F64;
import boofcv.struct.image.GrayF32;
import boofcv.struct.image.ImageGray;
import boofcv.struct.image.ImageType;
import georegression.struct.point.Point2D_F64;
import java.util.ArrayList;
import java.util.List;
/**
* Wrapper which allows a calibration target to be used like a fiducial for pose estimation. The
* origin of the coordinate system depends on the default for the calibration target.
*
* @author Peter Abeles
*/
public class CalibrationFiducialDetector<T extends ImageGray>
extends FiducialDetectorPnP<T>
{
// detects the calibration target
private DetectorFiducialCalibration detector;
// indicates if a target was detected and the found transform
private boolean targetDetected;
// storage for converted input image. Detector only can process GrayF32
private GrayF32 converted;
// Expected type of input image
private ImageType<T> type;
// Known 3D location of points on calibration grid and current observations
private List<Point2D3D> points2D3D;
// average of width and height
private double width;
/**
* Configure it to detect chessboard style targets
*/
public CalibrationFiducialDetector(ConfigChessboard config,
Class<T> imageType) {
DetectorFiducialCalibration detector = FactoryFiducialCalibration.chessboard(config);
double sideWidth = config.numCols*config.squareWidth;
double sideHeight = config.numRows*config.squareWidth;
width = (sideWidth+sideHeight)/2.0;
init(detector, width, imageType);
}
/**
* Configure it to detect square-grid style targets
*/
public CalibrationFiducialDetector(ConfigSquareGrid config,
Class<T> imageType) {
DetectorFiducialCalibration detector = FactoryFiducialCalibration.squareGrid(config);
int squareCols = config.numCols;
int squareRows = config.numRows;
double sideWidth = squareCols* config.squareWidth + (squareCols-1)*config.spaceWidth;
double sideHeight = squareRows*config.squareWidth + (squareRows-1)*config.spaceWidth;
double width = (sideWidth+sideHeight)/2.0;
init(detector, width, imageType);
}
/**
* Configure it to detect square-grid style targets
*/
public CalibrationFiducialDetector(ConfigSquareGridBinary config,
Class<T> imageType) {
DetectorFiducialCalibration detector = FactoryFiducialCalibration.binaryGrid(config);
int squareCols = config.numCols;
int squareRows = config.numRows;
double sideWidth = squareCols*config.squareWidth + (squareCols-1)*config.spaceWidth;
double sideHeight = squareRows*config.squareWidth + (squareRows-1)*config.spaceWidth;
double width = (sideWidth+sideHeight)/2.0;
init(detector, width, imageType);
}
public CalibrationFiducialDetector(ConfigCircleAsymmetricGrid config,
Class<T> imageType) {
DetectorFiducialCalibration detector = FactoryFiducialCalibration.circleAsymmGrid(config);
int squareCols = config.numCols;
int squareRows = config.numRows;
double sideWidth = squareCols*config.centerDistance/2.0;
double sideHeight = squareRows*config.centerDistance/2.0;
double width = (sideWidth+sideHeight)/2.0;
init(detector, width, imageType);
}
protected void init(DetectorFiducialCalibration detector, double width, Class<T> imageType) {
this.detector = detector;
this.type = ImageType.single(imageType);
this.converted = new GrayF32(1,1);
this.width = width;
List<Point2D_F64> layout = detector.getLayout();
points2D3D = new ArrayList<>();
for (int i = 0; i < layout.size(); i++) {
Point2D_F64 p2 = layout.get(i);
Point2D3D p = new Point2D3D();
p.location.set(p2.x,p2.y,0);
points2D3D.add(p);
}
}
@Override
public void detect(T input) {
if( input instanceof GrayF32) {
converted = (GrayF32)input;
} else {
converted.reshape(input.width,input.height);
GConvertImage.convert(input, converted);
}
if( !detector.process(converted) ) {
targetDetected = false;
return;
} else {
targetDetected = true;
}
}
/**
* Returns the detection point average location. This will NOT be the same as the geometric center.
*
* @param which Fiducial's index
* @param location (output) Storage for the transform. modified.
*/
@Override
public void getImageLocation(int which, Point2D_F64 location) {
CalibrationObservation view = detector.getDetectedPoints();
location.set(0,0);
for (int i = 0; i < view.size(); i++) {
PointIndex2D_F64 p = view.get(i);
location.x += p.x;
location.y += p.y;
}
location.x /= view.size();
location.y /= view.size();
}
@Override
public int totalFound() {
return targetDetected ? 1 : 0;
}
@Override
public long getId( int which ) {
return 0;
}
@Override
public String getMessage(int which) {
return null;
}
@Override
public ImageType<T> getInputType() {
return type;
}
public List<Point2D_F64> getCalibrationPoints() {
return detector.getLayout();
}
public DetectorFiducialCalibration getCalibDetector() {
return detector;
}
public List<Point2D3D> getPoints2D3D() {
return points2D3D;
}
@Override
public double getWidth(int which) {
return width;
}
@Override
public boolean hasUniqueID() {
return false;
}
@Override
public boolean hasMessage() {
return false;
}
@Override
protected List<PointIndex2D_F64> getDetectedControl(int which) {
CalibrationObservation view = getCalibDetector().getDetectedPoints();
return view.points;
}
@Override
protected List<Point2D3D> getControl3D(int which) {
return getPoints2D3D();
}
}