/*
* 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.calib.chess;
import boofcv.alg.shapes.polygon.BinaryPolygonDetector;
import boofcv.alg.shapes.polygon.PolygonHelper;
import boofcv.alg.shapes.polygon.RefineBinaryPolygon;
import boofcv.struct.image.ImageGray;
import georegression.struct.point.Point2D_F64;
import georegression.struct.point.Point2D_I32;
import georegression.struct.shapes.Polygon2D_F64;
import org.ddogleg.struct.FastQueue;
import org.ddogleg.struct.GrowQueue_I32;
import java.util.List;
/**
* Helper which expands polygons prior to optimization. This is done to counter act the erosion step which shrunk
* the polygon
*
* @author Peter Abeles
*/
public class ChessboardPolygonHelper<T extends ImageGray> implements PolygonHelper {
BinaryPolygonDetector<T> detectorSquare;
RefineBinaryPolygon<T> refineLine;
RefineBinaryPolygon<T> refineCorner;
double threshold = 400;
int width,height;
FastQueue<Point2D_F64> tmp = new FastQueue<>(Point2D_F64.class, true);
public ChessboardPolygonHelper(BinaryPolygonDetector<T> detectorSquare,
RefineBinaryPolygon<T> refineLine ,
RefineBinaryPolygon<T> refineCorner ) {
this.detectorSquare = detectorSquare;
this.refineLine = refineLine;
this.refineCorner = refineCorner;
}
@Override
public void setImageShape(int width, int height) {
this.width = width;
this.height = height;
}
@Override
public void adjustBeforeOptimize(Polygon2D_F64 polygon) {
int N = polygon.size();
tmp.resize(N);
for (int i = 0; i < N; i++) {
tmp.get(i).set(0,0);
}
for (int i = N-1,j=0; j < N; i=j,j++) {
Point2D_F64 a = polygon.get(i);
Point2D_F64 b = polygon.get(j);
double dx = b.x-a.x;
double dy = b.y-a.y;
double l = Math.sqrt(dx*dx + dy*dy);
dx *= 1.3/l;
dy *= 1.3/l;
Point2D_F64 _a = tmp.get(i);
Point2D_F64 _b = tmp.get(j);
_a.x -= dx;
_a.y -= dy;
_b.x += dx;
_b.y += dy;
}
for (int i = 0; i < N; i++) {
Point2D_F64 a = polygon.get(i);
Point2D_F64 t = tmp.get(i);
a.x += t.x;
a.y += t.y;
if( a.x < 0) a.x = 0;
else if( a.x > width-1 ) a.x = width-1;
if( a.y < 0) a.y = 0;
else if( a.y > height-1 ) a.y = height-1;
}
if( refineCorner != null ) {
double area = polygon.areaSimple();
if (area < threshold) {
detectorSquare.setRefinePolygon(refineLine);
} else {
detectorSquare.setRefinePolygon(refineCorner);
}
} else {
detectorSquare.setRefinePolygon(refineLine);
}
}
@Override
public boolean filterContour(List<Point2D_I32> contour, boolean touchesBorder, boolean distorted) {
return true;
}
/**
* If not touching the border then the number of corners must be 4. If touching the border there must be
* at least 3 corners not touching the border. 7 corners at most. If there were 8 then all sides of a square
* would be touching the border. No more than 3 corners since that's the most number of non-border corners
* a square can have.
*/
@Override
public boolean filterPixelPolygon(List<Point2D_I32> externalUndist, List<Point2D_I32> externalDist,
GrowQueue_I32 splits, boolean touchesBorder) {
if( touchesBorder ) {
if( splits.size() > 7 || splits.size() < 3)
return false;
int totalRegular = 0;
for (int i = 0; i < splits.size(); i++) {
Point2D_I32 p = externalDist.get(splits.get(i));
if( !(p.x == 0 || p.y == 0 || p.x == width-1 || p.y == height-1))
totalRegular++;
}
return totalRegular > 0 && totalRegular <= 4; // should be 3, but noise/imprecision in corner can make it 4
} else {
return splits.size() == 4;
}
}
}