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-2011 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 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] };
}
}