All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bytedeco.javacv.ProCamGeometricCalibrator Maven / Gradle / Ivy

There is a newer version: 1.5.10
Show newest version
/*
 * 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] };
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy