/* * 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.util.Arrays; import static org.bytedeco.javacpp.opencv_core.*; import static org.bytedeco.javacv.cvkernels.*; /** * * @author Samuel Audet */ public class ProjectiveColorTransformer extends ProjectiveTransformer { public ProjectiveColorTransformer(CvMat K1, CvMat K2, CvMat R, CvMat t, CvMat n, double[] referencePoints1, double[] referencePoints2, CvMat X, int numGains, int numBiases) { super(K1, K2, R, t, n, referencePoints1, referencePoints2); this.X = X == null ? null : X.clone(); this.numGains = numGains; this.numBiases = numBiases; } protected static ThreadLocal<CvMat> X24x4 = CvMat.createThreadLocal(4, 4), temp3x1 = CvMat.createThreadLocal(3, 1); protected CvMat X = null; protected int numGains = 0, numBiases = 0; protected CvMat[] X2 = null; public CvMat getX() { return X; } public int getNumGains() { return numGains; } public int getNumBiases() { return numBiases; } public void transformColor(IplImage srcImage, IplImage dstImage, CvRect roi, int pyramidLevel, ImageTransformer.Parameters parameters, boolean inverse) { Parameters p = ((Parameters)parameters); if ((Arrays.equals(p.getColorParameters(), p.getIdentityColorParameters()) && (X == null || p.fakeIdentity)) || (X == null && numGains == 0 && numBiases == 0)) { if (srcImage != dstImage) { cvCopy(srcImage, dstImage); } return; } CvMat X2 = X24x4.get(); prepareColorTransform(X2, pyramidLevel, p, inverse); X2.rows(3); // do color transformation if (roi == null) { cvResetImageROI(dstImage); } else { cvSetImageROI(dstImage, roi); } X2.put(0, 3, X2.get(0, 3)*dstImage.highValue()); X2.put(1, 3, X2.get(1, 3)*dstImage.highValue()); X2.put(2, 3, X2.get(2, 3)*dstImage.highValue()); cvTransform(srcImage, dstImage, X2, null); X2.rows(4); } protected void prepareColorTransform(CvMat X2, int pyramidLevel, Parameters p, boolean inverse) { CvMat A = p.getA(), b = p.getB(); cvSetIdentity(X2); X2.rows(3); X2.cols(3); if (p.fakeIdentity && !inverse) { X2.put(A); } else if (A != null && X != null) { cvMatMul(X, A, X2); } else if (X == null) { X2.put(A); } else if (A == null) { X2.put(X); } X2.rows(4); X2.cols(4); if (b != null) { X2.put(0, 3, b.get(0)); X2.put(1, 3, b.get(1)); X2.put(2, 3, b.get(2)); } if (inverse) { // CV_LU doesn't work on OpenCV 2.0 with rows > 3 ... cvInvert(X2, X2, CV_SVD); } } @Override public void transform(Data[] data, CvRect roi, ImageTransformer.Parameters[] parameters, boolean[] inverses) { assert data.length == parameters.length; if (kernelData == null || kernelData.capacity() < data.length) { kernelData = new KernelData(data.length); } if (H == null || H.length < data.length) { H = new CvMat[data.length]; for (int i = 0; i < H.length; i++) { H[i] = CvMat.create(3, 3); } } if (X2 == null || X2.length < data.length) { X2 = new CvMat[data.length]; for (int i = 0; i < X2.length; i++) { X2[i] = CvMat.create(4, 4); } } for (int i = 0; i < data.length; i++) { kernelData.position(i); kernelData.srcImg(data[i].srcImg); kernelData.srcImg2(null); kernelData.subImg(data[i].subImg); kernelData.srcDotImg(data[i].srcDotImg); kernelData.mask(data[i].mask); kernelData.zeroThreshold(data[i].zeroThreshold); kernelData.outlierThreshold(data[i].outlierThreshold); boolean inverse = inverses == null ? false : inverses[i]; prepareHomography (H[i], data[i].pyramidLevel, (Parameters)parameters[i], inverse); prepareColorTransform(X2[i], data[i].pyramidLevel, (Parameters)parameters[i], inverse); kernelData.H1(H[i]); kernelData.H2(null); kernelData.X(X2[i]); kernelData.transImg(data[i].transImg); kernelData.dstImg(data[i].dstImg); kernelData.dstDstDot(data[i].dstDstDot); } long fullCapacity = kernelData.capacity(); kernelData.capacity(data.length); multiWarpColorTransform(kernelData, roi, getFillColor()); kernelData.capacity(fullCapacity); for (int i = 0; i < data.length; i++) { kernelData.position(i); data[i].dstCount = kernelData.dstCount(); data[i].dstCountZero = kernelData.dstCountZero(); data[i].dstCountOutlier = kernelData.dstCountOutlier(); data[i].srcDstDot = kernelData.srcDstDot(); } } @Override public Parameters createParameters() { return new Parameters(); } public class Parameters extends ProjectiveTransformer.Parameters { protected Parameters() { identityColorParameters = new double[numGains + numBiases]; if (numGains > 0) { A = CvMat.create(3, 3); cvSetIdentity(A); } if (numBiases > 0) { b = CvMat.create(3, 1); cvSetZero(b); } switch (numGains) { case 0: assert (A == null); break; case 1: identityColorParameters[0] = (A.get(0) + A.get(4) + A.get(8))/3; break; case 3: identityColorParameters[0] = A.get(0); identityColorParameters[1] = A.get(4); identityColorParameters[2] = A.get(8); break; case 9: A.get(0, identityColorParameters, 0, 9); break; default: assert (false); } switch (numBiases) { case 0: assert (b == null); break; case 1: identityColorParameters[numGains] = (b.get(0) + b.get(1) + b.get(2))/3; break; case 3: b.get(0, identityColorParameters, numGains, 3); break; default: assert (false); } reset(false); } protected double[] colorParameters = null, identityColorParameters = null; private CvMat A = null, b = null; public double[] getColorParameters() { return colorParameters; } public double[] getIdentityColorParameters() { return identityColorParameters; } @Override public int size() { return super.size() + numGains + numBiases; } @Override public double get(int i) { int s = super.size(); if (i < s) { return super.get(i); } else { return colorParameters[i-s]; } } @Override public void set(int i, double p) { int s = super.size(); if (i < s) { super.set(i, p); } else { if (colorParameters[i-s] != p) { colorParameters[i-s] = p; setUpdateNeeded(true); } } } @Override public void reset(boolean asIdentity) { super.reset(asIdentity); resetColor(asIdentity); } public void resetColor(boolean asIdentity) { if (identityColorParameters != null) { if (!Arrays.equals(colorParameters, identityColorParameters) || fakeIdentity != asIdentity) { fakeIdentity = asIdentity; colorParameters = identityColorParameters.clone(); setUpdateNeeded(true); } } } // @Override public boolean addDelta(int i, double scale) { // int s = super.size(); // if (i < s) { // return super.addDelta(i, scale); // } else { // // gradient varies linearly with intensity, so // // the increment value is not very important, but // // referenceCameraImage is good only for the value 1, // // so let's use that // int channel = i-s; // colorParameters[channel] += scale;//1; // setUpdateNeeded(true); // return false; // } // } @Override public void compose(ImageTransformer.Parameters p1, boolean inverse1, ImageTransformer.Parameters p2, boolean inverse2) { super.compose(p1, inverse1, p2, inverse2); composeColor(p1, inverse1, p2, inverse2); } public void composeColor(ImageTransformer.Parameters p1, boolean inverse1, ImageTransformer.Parameters p2, boolean inverse2) { assert (!inverse1 && !inverse2); Parameters pp1 = (Parameters)p1, pp2 = (Parameters)p2; CvMat A1 = pp1.getA(), b1 = pp1.getB(); CvMat A2 = pp2.getA(), b2 = pp2.getB(); if (b != null) { if (pp1.fakeIdentity && X != null) { CvMat temp = temp3x1.get(); cvMatMul(X, b1, temp); b1 = temp; } if (A2 == null && b2 == null) { cvCopy(b1, b); } else if (b1 == null) { cvCopy(b2, b); } else if (b2 == null) { cvMatMul(A2, b1, b); } else { cvGEMM(A2, b1, 1.0, b2, 1.0, b, 0); } } if (A != null) { if (A1 == null) { cvCopy(A2, A); } else if (A2 == null) { cvCopy(A1, A); } else { cvMatMul(A2, A1, A); } } switch (numGains) { case 0: assert (A == null); break; case 1: colorParameters[0] = (A.get(0) + A.get(4) + A.get(8))/3; break; case 3: colorParameters[0] = A.get(0); colorParameters[1] = A.get(4); colorParameters[2] = A.get(8); break; case 9: A.get(0, colorParameters, 0, 9); break; default: assert (false); } switch (numBiases) { case 0: assert (b == null); break; case 1: colorParameters[numGains] = (b.get(0) + b.get(1) + b.get(2))/3; break; case 3: b.get(0, colorParameters, numGains, 3); break; default: assert (false); } } public CvMat getA() { update(); return A; } public CvMat getB() { update(); return b; } @Override protected void update() { if (!isUpdateNeeded()) { return; } switch (numGains) { case 0: assert (A == null); break; case 1: A.put(0, colorParameters[0]); A.put(4, colorParameters[0]); A.put(8, colorParameters[0]); break; case 3: A.put(0, colorParameters[0]); A.put(4, colorParameters[1]); A.put(8, colorParameters[2]); break; case 9: A.put(0, colorParameters, 0, 9); break; default: assert (false); } switch (numBiases) { case 0: assert (b == null); break; case 1: b.put(0, colorParameters[numGains]); b.put(1, colorParameters[numGains]); b.put(2, colorParameters[numGains]); break; case 3: b.put(0, colorParameters, numGains, 3); break; default: assert (false); } super.update(); setUpdateNeeded(false); } @Override public Parameters clone() { Parameters p = new Parameters(); p.set(this); return p; } } }