org.bytedeco.javacv.ProCamGeometricCalibrator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javacv Show documentation
Show all versions of javacv Show documentation
Java interface to OpenCV, FFmpeg, and more
/*
* Copyright (C) 2009,2010,2011 Samuel Audet
*
* This file is part of JavaCV.
*
* JavaCV is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version (subject to the "Classpath" exception
* as provided in the LICENSE.txt file that accompanied this code).
*
* JavaCV is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with JavaCV. If not, see .
*/
package org.bytedeco.javacv;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import static org.bytedeco.javacpp.opencv_core.*;
import static org.bytedeco.javacpp.opencv_imgproc.*;
/**
*
* @author Samuel Audet
*/
public class ProCamGeometricCalibrator {
public ProCamGeometricCalibrator(Settings settings, MarkerDetector.Settings detectorSettings,
MarkedPlane boardPlane, MarkedPlane projectorPlane,
ProjectiveDevice camera, ProjectiveDevice projector) {
this(settings, detectorSettings, boardPlane, projectorPlane,
new GeometricCalibrator[] {
new GeometricCalibrator(settings, detectorSettings, boardPlane, camera)},
new GeometricCalibrator(settings, detectorSettings, projectorPlane, projector));
}
@SuppressWarnings("unchecked")
public ProCamGeometricCalibrator(Settings settings, MarkerDetector.Settings detectorSettings,
MarkedPlane boardPlane, MarkedPlane projectorPlane,
GeometricCalibrator[] cameraCalibrators, GeometricCalibrator projectorCalibrator) {
this.settings = settings;
this.detectorSettings = detectorSettings;
this.boardPlane = boardPlane;
this.projectorPlane = projectorPlane;
this.cameraCalibrators = cameraCalibrators;
int n = cameraCalibrators.length;
markerDetectors = new MarkerDetector[n];
// "unchecked" warning here
allImagedBoardMarkers = new LinkedList[n];
grayscaleImage = new IplImage[n];
tempImage1 = new IplImage[n];
tempImage2 = new IplImage[n];
lastDetectedMarkers1 = new Marker[n][];
lastDetectedMarkers2 = new Marker[n][];
rmseBoardWarp = new double[n];
rmseProjWarp = new double[n];
boardWarp = new CvMat[n];
projWarp = new CvMat[n];
prevBoardWarp = new CvMat[n];
lastBoardWarp = new CvMat[n];
tempPts1 = new CvMat[n];
tempPts2 = new CvMat[n];
for (int i = 0; i < n; i++) {
markerDetectors[i] = new MarkerDetector(detectorSettings);
allImagedBoardMarkers[i] = new LinkedList();
grayscaleImage[i] = null;
tempImage1[i] = null;
tempImage2[i] = null;
lastDetectedMarkers1[i] = null;
lastDetectedMarkers2[i] = null;
rmseBoardWarp[i] = Double.POSITIVE_INFINITY;
rmseProjWarp[i] = Double.POSITIVE_INFINITY;
boardWarp[i] = CvMat.create(3, 3);
projWarp[i] = CvMat.create(3, 3);
prevBoardWarp[i] = CvMat.create(3, 3);
lastBoardWarp[i] = CvMat.create(3, 3);
cvSetIdentity(prevBoardWarp[i]);
cvSetIdentity(lastBoardWarp[i]);
tempPts1[i] = CvMat.create(1, 4, CV_64F, 2);
tempPts2[i] = CvMat.create(1, 4, CV_64F, 2);
}
this.projectorCalibrator = projectorCalibrator;
this.boardWarpSrcPts = CvMat.create(1, 4, CV_64F, 2);
if (boardPlane != null) {
int w = boardPlane.getImage().width();
int h = boardPlane.getImage().height();
boardWarpSrcPts.put(0.0, 0.0, w, 0.0, w, h, 0.0, h);
}
if (projectorPlane != null) {
int w = projectorPlane.getImage().width();
int h = projectorPlane.getImage().height();
projectorCalibrator.getProjectiveDevice().imageWidth = w;
projectorCalibrator.getProjectiveDevice().imageHeight = h;
}
}
private final int
MSB_IMAGE_SHIFT = 8,
LSB_IMAGE_SHIFT = 7;
public static class Settings extends GeometricCalibrator.Settings {
double detectedProjectorMin = 0.5;
boolean useOnlyIntersection = true;
double prewarpUpdateErrorMax = 0.01;
public double getDetectedProjectorMin() {
return detectedProjectorMin;
}
public void setDetectedProjectorMin(double detectedProjectorMin) {
this.detectedProjectorMin = detectedProjectorMin;
}
public boolean isUseOnlyIntersection() {
return useOnlyIntersection;
}
public void setUseOnlyIntersection(boolean useOnlyIntersection) {
this.useOnlyIntersection = useOnlyIntersection;
}
public double getPrewarpUpdateErrorMax() {
return prewarpUpdateErrorMax;
}
public void setPrewarpUpdateErrorMax(double prewarpUpdateErrorMax) {
this.prewarpUpdateErrorMax = prewarpUpdateErrorMax;
}
}
private Settings settings;
private MarkerDetector.Settings detectorSettings;
// (possibly) multiple camera stuff in arrays
private GeometricCalibrator[] cameraCalibrators;
private MarkerDetector[] markerDetectors;
// keep our own list of markers for the camera, since cameraCalibrators
// might be used outside ProCamGeometricCalibrator as well...
LinkedList[] allImagedBoardMarkers;
private IplImage[] grayscaleImage, tempImage1, tempImage2;
private Marker[][] lastDetectedMarkers1, lastDetectedMarkers2;
private double[] rmseBoardWarp, rmseProjWarp;
private CvMat[] boardWarp, projWarp;
private CvMat[] prevBoardWarp, lastBoardWarp;
private CvMat[] tempPts1, tempPts2;
// single board and projector stuff
private boolean updatePrewarp = false;
private final MarkedPlane boardPlane, projectorPlane;
private final GeometricCalibrator projectorCalibrator;
private final CvMat boardWarpSrcPts;
public MarkedPlane getBoardPlane() {
return boardPlane;
}
public MarkedPlane getProjectorPlane() {
return projectorPlane;
}
public GeometricCalibrator[] getCameraCalibrators() {
return cameraCalibrators;
}
public GeometricCalibrator getProjectorCalibrator() {
return projectorCalibrator;
}
public int getImageCount() {
int n = projectorCalibrator.getImageCount()/cameraCalibrators.length;
for (GeometricCalibrator c : cameraCalibrators) {
assert(c.getImageCount() == n);
}
return n;
}
public Marker[][] processCameraImage(IplImage cameraImage) {
return processCameraImage(cameraImage, 0);
}
public Marker[][] processCameraImage(IplImage cameraImage, final int cameraNumber) {
cameraCalibrators[cameraNumber].getProjectiveDevice().imageWidth = cameraImage.width();
cameraCalibrators[cameraNumber].getProjectiveDevice().imageHeight = cameraImage.height();
if (cameraImage.nChannels() > 1) {
if (grayscaleImage[cameraNumber] == null ||
grayscaleImage[cameraNumber].width() != cameraImage.width() ||
grayscaleImage[cameraNumber].height() != cameraImage.height() ||
grayscaleImage[cameraNumber].depth() != cameraImage.depth()) {
grayscaleImage[cameraNumber] = IplImage.create(cameraImage.width(),
cameraImage.height(), cameraImage.depth(), 1, cameraImage.origin());
}
cvCvtColor(cameraImage, grayscaleImage[cameraNumber], CV_BGR2GRAY);
} else {
grayscaleImage[cameraNumber] = cameraImage;
}
final boolean boardWhiteMarkers = boardPlane.getForegroundColor().magnitude() >
boardPlane.getBackgroundColor().magnitude();
final boolean projWhiteMarkers = projectorPlane.getForegroundColor().magnitude() >
projectorPlane.getBackgroundColor().magnitude();
if (grayscaleImage[cameraNumber].depth() > 8) {
if (tempImage1[cameraNumber] == null ||
tempImage1[cameraNumber].width() != grayscaleImage[cameraNumber].width() ||
tempImage1[cameraNumber].height() != grayscaleImage[cameraNumber].height()) {
tempImage1[cameraNumber] = IplImage.create(grayscaleImage[cameraNumber].width(),
grayscaleImage[cameraNumber].height(), IPL_DEPTH_8U, 1,
grayscaleImage[cameraNumber].origin());
tempImage2[cameraNumber] = IplImage.create(grayscaleImage[cameraNumber].width(),
grayscaleImage[cameraNumber].height(), IPL_DEPTH_8U, 1,
grayscaleImage[cameraNumber].origin());
}
Parallel.run(new Runnable() { public void run() {
cvConvertScale(grayscaleImage[cameraNumber],
tempImage1[cameraNumber], 1.0/(1< allDistortedProjectorMarkers = projectorCalibrator.getAllObjectMarkers(),
distortedProjectorMarkersAtOrigin = new LinkedList(),
allUndistortedProjectorMarkers = new LinkedList(),
undistortedProjectorMarkersAtOrigin = new LinkedList();
Iterator ip = allDistortedProjectorMarkers.iterator();
// "unchecked" warning here
Iterator[] ib = new Iterator[cameraCalibrators.length];
for (int cameraNumber = 0; cameraNumber < cameraCalibrators.length; cameraNumber++) {
ib[cameraNumber] = allImagedBoardMarkers[cameraNumber].iterator();
}
// iterate over all the saved markers in the right order...
// eew, this is getting ugly...
while (ip.hasNext()) {
for (int cameraNumber = 0; cameraNumber < cameraCalibrators.length; cameraNumber++) {
double maxError = settings.prewarpUpdateErrorMax *
(cameraCalibrators[cameraNumber].getProjectiveDevice().imageWidth+
cameraCalibrators[cameraNumber].getProjectiveDevice().imageHeight)/2;
Marker[] distortedBoardMarkers = ib[cameraNumber].next(),
distortedProjectorMarkers = ip.next(),
undistortedBoardMarkers = new Marker[distortedBoardMarkers.length],
undistortedProjectorMarkers = new Marker[distortedProjectorMarkers.length];
// remove radial distortion from all points imaged by the camera
for (int i = 0; i < distortedBoardMarkers.length; i++) {
Marker m = undistortedBoardMarkers[i] = distortedBoardMarkers[i].clone();
m.corners = cameraCalibrators[cameraNumber].getProjectiveDevice().undistort(m.corners);
}
for (int i = 0; i < distortedProjectorMarkers.length; i++) {
Marker m = undistortedProjectorMarkers[i] = distortedProjectorMarkers[i].clone();
m.corners = cameraCalibrators[cameraNumber].getProjectiveDevice().undistort(m.corners);
}
// remove linear distortion/warping of camera imaged markers from
// the projector, to get their physical location on the board
if (boardPlane.getTotalWarp(undistortedBoardMarkers, boardWarp[cameraNumber]) > maxError) {
assert(false);
}
cvInvert(boardWarp[cameraNumber], boardWarp[cameraNumber]);
Marker.applyWarp(undistortedProjectorMarkers, boardWarp[cameraNumber]);
// tadam, we not have undistorted "object" corners for the projector..
allUndistortedProjectorMarkers.add(undistortedProjectorMarkers);
if (cameraCalibrators[cameraNumber] == calibratorAtOrigin) {
undistortedProjectorMarkersAtOrigin.add(undistortedProjectorMarkers);
distortedProjectorMarkersAtOrigin.add(distortedProjectorMarkers);
} else {
undistortedProjectorMarkersAtOrigin.add(new Marker[0]);
distortedProjectorMarkersAtOrigin.add(new Marker[0]);
}
}
}
// calibrate projector
projectorCalibrator.setAllObjectMarkers(allUndistortedProjectorMarkers);
double[] reprojErr = projectorCalibrator.calibrate(useCenters);
// projectorCalibrator.getProjectiveDevice().nominalDistance =
// projectorCalibrator.getProjectiveDevice().getNominalDistance(boardPlane);
// calibrate as a stereo pair (find rotation and translation)
// let's use the projector markers only...
LinkedList om = calibratorAtOrigin.getAllObjectMarkers(),
im = calibratorAtOrigin.getAllImageMarkers();
calibratorAtOrigin.setAllObjectMarkers(undistortedProjectorMarkersAtOrigin);
calibratorAtOrigin.setAllImageMarkers(distortedProjectorMarkersAtOrigin);
double[] epipolarErr = calibratorAtOrigin.calibrateStereo(useCenters, projectorCalibrator);
// reset everything as it was before we started, so we get the same
// result if called a second time..
projectorCalibrator.setAllObjectMarkers(allDistortedProjectorMarkers);
calibratorAtOrigin.setAllObjectMarkers(om);
calibratorAtOrigin.setAllImageMarkers(im);
return new double[] { reprojErr[0], reprojErr[1], epipolarErr[0], epipolarErr[1] };
}
}