/*
* 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.awt.Color;
import static org.bytedeco.javacpp.opencv_core.*;
/**
*
* @author Samuel Audet
*/
public class ColorCalibrator {
public ColorCalibrator(ProjectiveDevice device) {
this.device = device;
}
private ProjectiveDevice device;
public double calibrate(Color[] referenceColors, Color[] deviceColors) {
assert(referenceColors.length == deviceColors.length);
int[] order = device.getRGBColorOrder();
// solve for X and a in c = X p + a
CvMat A = CvMat.create(referenceColors.length*3, 12);
CvMat b = CvMat.create(referenceColors.length*3, 1);
CvMat x = CvMat.create(12, 1);
double gamma = device.getSettings().getResponseGamma();
for (int i = 0; i < referenceColors.length; i++) {
float[] dc = deviceColors [i].getRGBColorComponents(null);
float[] rc = referenceColors[i].getRGBColorComponents(null);
double dc1 = Math.pow(dc[order[0]], gamma);
double dc2 = Math.pow(dc[order[1]], gamma);
double dc3 = Math.pow(dc[order[2]], gamma);
for (int j = 0; j < 3; j++) {
int k = i*36 + j*16;
A.put(k , dc1);
A.put(k+1, dc2);
A.put(k+2, dc3);
A.put(k+3, 1.0);
if (j < 2) {
for (int m = 0; m < 12; m++) {
A.put(k+4+m, 0.0);
}
}
}
b.put(i*3 , rc[order[0]]);
b.put(i*3+1, rc[order[1]]);
b.put(i*3+2, rc[order[2]]);
}
//System.out.println("A =\n" + A);
//System.out.println("b =\n" + b);
// A.height = b.height = 18;
if (cvSolve(A, b, x, CV_SVD) != 1.0) {
System.out.println("Error solving.");
}
// compute RMSE and R^2 coefficient ...
CvMat b2 = CvMat.create(b.rows(), 1);
cvMatMul(A, x, b2);
double MSE = cvNorm(b, b2)*cvNorm(b, b2)/b.rows();
double RMSE = Math.sqrt(MSE);
CvScalar mean = new CvScalar(), stddev = new CvScalar();
cvAvgSdv(b, mean, stddev, null);
double R2 = 1 - MSE/(stddev.val(0)*stddev.val(0));
//System.out.println("RMSE: " + RMSE + " R2: " + R2);
//System.out.println("b2 =\n" + b2);
device.colorMixingMatrix = CvMat.create(3, 3);
device.additiveLight = CvMat.create(3, 1);
for (int i = 0; i < 3; i++) {
double x0 = x.get(i*4 );
double x1 = x.get(i*4+1);
double x2 = x.get(i*4+2);
double x3 = x.get(i*4+3);
device.colorMixingMatrix.put(i*3 , x0);
device.colorMixingMatrix.put(i*3+1, x1);
device.colorMixingMatrix.put(i*3+2, x2);
device.additiveLight .put(i, x3);
}
//System.out.println(device.colorMixingMatrix);
//System.out.println(device.additiveLight);
device.colorR2 = R2;
return device.avgColorErr = RMSE;
}
}