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

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

There is a newer version: 1.5.10
Show newest version
/*
 * 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 java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.logging.Logger;

import static org.bytedeco.javacpp.opencv_core.*;
import static org.bytedeco.javacpp.opencv_imgproc.*;

/**
 *
 * @author Samuel Audet
 */
public class ReflectanceInitializer {

    public ReflectanceInitializer(CameraDevice cameraDevice, ProjectorDevice projectorDevice,
            int channels, GNImageAligner.Settings alignerSettings) {
        this(cameraDevice, projectorDevice, channels, alignerSettings, 51, 0.01);
    }
    public ReflectanceInitializer(CameraDevice cameraDevice, ProjectorDevice projectorDevice,
            int channels, GNImageAligner.Settings alignerSettings, int smoothingSize, double reflectanceMin) {
        this.alignerSettings = alignerSettings;
        this.smoothingSize   = smoothingSize;
        this.reflectanceMin  = reflectanceMin;
        this.cameraDevice    = cameraDevice;
        this.projectorDevice = projectorDevice;
        this.projectorImages = new IplImage[3];

        for (int i = 0; i < projectorImages.length; i++) {
            projectorImages[i] = IplImage.create(projectorDevice.imageWidth,
                    projectorDevice.imageHeight, IPL_DEPTH_32F, channels);
        }

        // capture "black" image (illuminated by ambient light only)
        cvSetZero(projectorImages[0]);
        // capture "white" image (projector illumination + ambient light)
        cvSet(projectorImages[1], CvScalar.ONE);
        // capture image with some texture easy to register... ugh...
        CvMat H = mat3x3.get();
        projectorDevice.getRectifyingHomography(cameraDevice, H);
        JavaCV.fractalTriangleWave(projectorImages[2], H);
    }

    private static ThreadLocal
            mat3x1 = CvMat.createThreadLocal(3, 1),
            mat3x3 = CvMat.createThreadLocal(3, 3),
            mat4x4 = CvMat.createThreadLocal(4, 4);

    private GNImageAligner.Settings alignerSettings;
    private int smoothingSize;
    private double reflectanceMin;
    private CameraDevice cameraDevice;
    private ProjectorDevice projectorDevice;
    private IplImage[] projectorImages;

    public IplImage[] getProjectorImages() {
        return projectorImages;
    }

    public IplImage initializeReflectance(IplImage[] cameraImages, IplImage reflectance,
            double[] roiPts, double[] gainAmbientLight) {
        int w        = cameraImages[0].width();
        int h        = cameraImages[0].height();
        int channels = cameraImages[0].nChannels();

        IplImage mask = IplImage.create(w, h, IPL_DEPTH_8U, 1);
        cvSetZero(mask);
        cvFillConvexPoly(mask, new CvPoint(roiPts.length/2).put((byte)(16-cameraDevice.getMapsPyramidLevel()), roiPts),
                4, CvScalar.WHITE, 8, 16);

        // make the images very very smooth to compensate for small movements
        IplImage float1 = cameraImages[0];
        IplImage float2 = cameraImages[1];
        cvCopy(float2, reflectance);
        cvSmooth(float1, float1, CV_GAUSSIAN, smoothingSize, 0, 0, 0);
        cvSmooth(float2, float2, CV_GAUSSIAN, smoothingSize, 0, 0, 0);

        // remove ambient light of image1 from image2 -> image2
        cvSub(float2, float1, float2, null);

        // remove distortion caused by the mixing matrix of the projector light
        // and recover (very very smooth) reflectance map
        CvMat p = mat3x1.get();
        p.put(1.0, 1.0, 1.0); // white
        cvMatMul(projectorDevice.colorMixingMatrix, p, p);
        CvMat invp;
        if (float2.nChannels() == 4) {
            invp = mat4x4.get();
            invp.put(1/p.get(0), 0, 0, 0,
                     0, 1/p.get(1), 0, 0,
                     0, 0, 1/p.get(2), 0,
                     0, 0, 0, 1);
        } else {
            invp = mat3x3.get();
            invp.put(1/p.get(0), 0, 0,
                     0, 1/p.get(1), 0,
                     0, 0, 1/p.get(2));
        }
        cvTransform(float2, float2, invp, null);

        // recover (very very smooth) ambient light by removing distortions
        // caused by the reflectance map
        // cvDiv(image1, image3, image1, 1);
        // cvDiv() doesn't support division by zero...
        FloatBuffer fb1 = float1.getFloatBuffer();
        FloatBuffer fb2 = float2.getFloatBuffer();
        ByteBuffer  mb  = mask.getByteBuffer();
        assert fb1.capacity() == fb2.capacity()/3;
        assert fb1.capacity() == mb.capacity()/3;
        int[] nPixels = new int[channels];
        for (int i = 0, j = 0; j < fb1.capacity(); i++, j+=channels) {
            for (int z = 0; z < channels; z++) {
                float ra = fb1.get(j+z);
                float r  = fb2.get(j+z);
                float a  = r == 0 ? 0 : ra/r;
                fb1.put(j+z, a);
                if (mb.get(i) != 0) {
                    if (r > reflectanceMin) {
                        nPixels[z]++;
                        gainAmbientLight[z+1] += a;
                    }
                }
            }
        }
        gainAmbientLight[0] = 1.0; // assume projector gain = 1.0
        for (int z = 0; z < gainAmbientLight.length-1; z++) {
            gainAmbientLight[z+1] = nPixels[z] == 0 ? 0 : gainAmbientLight[z+1]/nPixels[z];
        }
//        System.out.println(ambientLight[0] + " " + ambientLight[1] + " " + ambientLight[2]);

        // recover sharp reflectance map by using the original image2 and smooth ambient light
        cvAddS(float1, cvScalar(p.get(0), p.get(1), p.get(2), 0.0), float1, null);
        cvDiv(reflectance, float1, reflectance, 1.0);

        cvNot(mask, mask);
        // increase region a bit so that the resulting image can be
        // interpolated or averaged properly within the region of interest...
        cvErode(mask, mask, null, 15);
        cvSet(reflectance, CvScalar.ZERO, mask);

        return reflectance;
    }

    public CvMat initializePlaneParameters(IplImage reflectance, IplImage cameraImage,
            double[] referencePoints, double[] roiPts, double[] gainAmbientLight) {
        ProCamTransformer transformer = new ProCamTransformer(referencePoints, cameraDevice, projectorDevice, null);
        transformer.setProjectorImage(projectorImages[2], 0, alignerSettings.pyramidLevelMax);

        ProCamTransformer.Parameters parameters = transformer.createParameters();
//        parameters.set(8,  0);
//        parameters.set(9,  0);
//        parameters.set(10, -1/cameraDevice.getSettings().nominalDistance);
        final int gainAmbientLightStart = parameters.size() - gainAmbientLight.length;
        final int gainAmbientLightEnd   = parameters.size();
        for (int i = gainAmbientLightStart; i < gainAmbientLightEnd; i++) {
            parameters.set(i, gainAmbientLight[i-gainAmbientLightStart]);
        }
        ImageAligner aligner = new GNImageAligner(transformer, parameters,
                reflectance, roiPts, cameraImage, alignerSettings);

        double[] delta = new double[parameters.size()+1];
        boolean converged = false;
        long iterationsStartTime = System.currentTimeMillis();
        int iterations = 0;
        while (!converged && iterations < 100) {
            converged = aligner.iterate(delta);
            iterations++;
        }
        parameters = (ProCamTransformer.Parameters)aligner.getParameters();
//        for (int i = 0; i < gainAmbientLight.length; i++) {
//            gainAmbientLight[i] = parameters.get(11+i);
//        }
        Logger.getLogger(ReflectanceInitializer.class.getName()).info(
            "iteratingTime = " + (System.currentTimeMillis()-iterationsStartTime) +
                "  iterations = " + iterations + "  objectiveRMSE = " + (float)aligner.getRMSE());
        return parameters.getN0();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy