/*
* Copyright (C) 2009-2012 Samuel Audet
*
* Licensed either under the Apache License, Version 2.0, or (at your option)
* under the terms of the GNU General Public License as published by
* the Free Software Foundation (subject to the "Classpath" exception),
* either version 2, or any later version (collectively, 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
* http://www.gnu.org/licenses/
* http://www.gnu.org/software/classpath/license.html
*
* or as provided in the LICENSE.txt file that accompanied this code.
* 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 org.bytedeco.javacv;
import static org.bytedeco.javacpp.opencv_calib3d.*;
import static org.bytedeco.javacpp.opencv_core.*;
import static org.bytedeco.javacpp.opencv_imgproc.*;
/**
*
* @author Samuel Audet
*/
public class MarkedPlane {
public MarkedPlane(int width, int height, Marker[] planeMarkers, double superScale) {
this(width, height, planeMarkers, false, CvScalar.BLACK, CvScalar.WHITE, superScale);
}
public MarkedPlane(int width, int height, Marker[] markers,
boolean initPrewarp, CvScalar foregroundColor, CvScalar backgroundColor, double superScale) {
this.markers = markers;
this.foregroundColor = foregroundColor;
this.backgroundColor = backgroundColor;
// this.srcPts = CvMat.create(planeMarkers.length*4, 2);
// this.dstPts = CvMat.create(planeMarkers.length*4, 2);
this.prewarp = null;
// this.totalWarp = CvMat.create(3, 3);
// this.tempWarp = CvMat.create(3, 3);
if (initPrewarp) {
prewarp = CvMat.create(3, 3);
double minx = Double.MAX_VALUE, miny = Double.MAX_VALUE,
maxx = Double.MIN_VALUE, maxy = Double.MIN_VALUE;
for (Marker m : markers) {
double[] c = m.corners;
minx = Math.min(Math.min(Math.min(Math.min(minx, c[0]), c[2]), c[4]), c[6]);
miny = Math.min(Math.min(Math.min(Math.min(miny, c[1]), c[3]), c[5]), c[7]);
maxx = Math.max(Math.max(Math.max(Math.max(maxx, c[0]), c[2]), c[4]), c[6]);
maxy = Math.max(Math.max(Math.max(Math.max(maxy, c[1]), c[3]), c[5]), c[7]);
}
double aspect = (maxx-minx)/(maxy-miny);
if (aspect > (double)width/height) {
double h = (double)width/aspect;
// srcPtsBuf.position(0); srcPtsBuf.put(new double[] { minx, miny, maxx, miny, maxx, maxy, minx, maxy });
// dstPtsBuf.position(0); dstPtsBuf.put(new double[] { 0, height-h, width, height-h, width, height, 0, height });
// srcPts.height = dstPts.height = 4;
// cvFindHomography(srcPts, dstPts, preWarp);
JavaCV.getPerspectiveTransform(
new double[] { minx, miny, maxx, miny, maxx, maxy, minx, maxy },
new double[] { 0, height-h, width, height-h, width, height, 0, height }, prewarp);
} else {
double w = height*aspect;
// srcPtsBuf.position(0); srcPtsBuf.put(new double[] { minx, miny, maxx, miny, maxx, maxy, minx, maxy });
// dstPtsBuf.position(0); dstPtsBuf.put(new double[] { 0, 0, w, 0, w, height, 0, height });
// srcPts.height = dstPts.height = 4;
// cvFindHomography(srcPts, dstPts, preWarp);
JavaCV.getPerspectiveTransform(
new double[] { minx, miny, maxx, miny, maxx, maxy, minx, maxy },
new double[] { 0, 0, w, 0, w, height, 0, height }, prewarp);
}
}
if (width > 0 && height > 0) {
planeImage = IplImage.create(width, height, IPL_DEPTH_8U, 1);
if (superScale == 1.0) {
superPlaneImage = null;
} else {
superPlaneImage = IplImage.create((int)Math.ceil(width*superScale),
(int)Math.ceil(height*superScale), IPL_DEPTH_8U, 1);
}
setPrewarp(prewarp);
}
localSrcPts = CvMat.createThreadLocal(markers.length*4, 2);
localDstPts = CvMat.createThreadLocal(markers.length*4, 2);
}
private Marker[] markers = null;
// private CvPoint tempPts = new CvPoint(4);
// private CvMat srcPts, dstPts;
private CvMat prewarp;//, totalWarp, tempWarp;
private IplImage planeImage = null, superPlaneImage = null;
private CvScalar foregroundColor, backgroundColor;
private ThreadLocal<CvMat> localSrcPts, localDstPts;
public CvScalar getForegroundColor() {
return foregroundColor;
}
public void setForegroundColor(CvScalar foregroundColor) {
this.foregroundColor = foregroundColor;
setPrewarp(prewarp);
}
public CvScalar getBackgroundColor() {
return backgroundColor;
}
public void setBackgroundColor(CvScalar backgroundColor) {
this.backgroundColor = backgroundColor;
setPrewarp(prewarp);
}
public Marker[] getMarkers() {
return markers;
}
public void setColors(CvScalar foregroundColor, CvScalar backgroundColor) {
this.foregroundColor = foregroundColor;
this.backgroundColor = backgroundColor;
setPrewarp(prewarp);
}
public CvMat getPrewarp() {
return prewarp;
}
public void setPrewarp(CvMat prewarp) {
this.prewarp = prewarp;
if (superPlaneImage == null) {
cvSet(planeImage, backgroundColor);
} else {
cvSet(superPlaneImage, backgroundColor);
}
for (int i = 0; i < markers.length; i++) {
if (superPlaneImage == null) {
markers[i].draw(planeImage, foregroundColor, 1.0, prewarp);
} else {
markers[i].draw(superPlaneImage, foregroundColor, (double)
superPlaneImage.width()/planeImage.width(), prewarp);
}
}
if (superPlaneImage != null) {
cvResize(superPlaneImage, planeImage, CV_INTER_AREA);
}
//cvSaveImage("planeImage.png", planeImage);
}
public IplImage getImage() {
return planeImage;
}
public int getWidth() {
return planeImage.width();
}
public int getHeight() {
return planeImage.height();
}
public double getTotalWarp(Marker[] imagedMarkers, CvMat totalWarp) {
return getTotalWarp(imagedMarkers, totalWarp, false);
}
private static ThreadLocal<CvMat>
tempWarp3x3 = CvMat.createThreadLocal(3, 3);
public double getTotalWarp(Marker[] imagedMarkers, CvMat totalWarp, boolean useCenters) {
double rmse = Double.POSITIVE_INFINITY;
int pointsPerMarker = useCenters ? 1 : 4;
CvMat srcPts = localSrcPts.get(); srcPts.rows(markers.length*pointsPerMarker);
CvMat dstPts = localDstPts.get(); dstPts.rows(markers.length*pointsPerMarker);
int numPoints = 0;
for (Marker m1 : markers) {
for (Marker m2 : imagedMarkers) {
if (m1.id == m2.id) {
if (useCenters) {
srcPts.put(numPoints*2, m1.getCenter());
dstPts.put(numPoints*2, m2.getCenter());
} else {
srcPts.put(numPoints*2, m1.corners);
dstPts.put(numPoints*2, m2.corners);
}
numPoints += pointsPerMarker;
break;
}
}
}
if (numPoints > 4 || (srcPts.rows() == 4 && numPoints == 4)) {
// compute homography ... should we use a robust method?
srcPts.rows(numPoints); dstPts.rows(numPoints);
if (numPoints == 4) {
JavaCV.getPerspectiveTransform(srcPts.get(), dstPts.get(), totalWarp);
} else {
cvFindHomography(srcPts, dstPts, totalWarp);
}
// compute transformed source<->dest RMSE
srcPts.cols(1); srcPts.type(CV_64F, 2);
dstPts.cols(1); dstPts.type(CV_64F, 2);
cvPerspectiveTransform(srcPts, srcPts, totalWarp);
srcPts.cols(2); srcPts.type(CV_64F, 1);
dstPts.cols(2); dstPts.type(CV_64F, 1);
rmse = 0;
for (int i = 0; i < numPoints; i++) {
double dx = dstPts.get(i*2 )-srcPts.get(i*2 );
double dy = dstPts.get(i*2+1)-srcPts.get(i*2+1);
rmse += dx*dx+dy*dy;
}
rmse = Math.sqrt(rmse/numPoints);
// System.out.println(rmse);
if (prewarp != null) {
// remove pre-warp from total warp
CvMat tempWarp = tempWarp3x3.get();
cvInvert(prewarp, tempWarp);
cvMatMul(totalWarp, tempWarp, totalWarp);
}
// System.out.println("totalWarp:\n" + totalWarp);
}
return rmse;
}
}