Skip to content

Commit eace67c

Browse files
committed
adding robust ptm for 9 planes
1 parent c2f7e49 commit eace67c

File tree

5 files changed

+116
-40
lines changed

5 files changed

+116
-40
lines changed

relight-cli/rtibuilder.cpp

+76-36
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,16 @@ bool RtiBuilder::init(std::function<bool(QString stage, int percent)> *_callback
175175
error = "PTM and HSH do not support MRGB";
176176
return false;
177177
}
178+
if(type == PTM) {
179+
if(colorspace == LRGB && (nplanes != 6 && nplanes != 9)) {
180+
error = "PTM with LRGB colorspace supports only 6 or 9 planes";
181+
return false;
182+
}
183+
if(colorspace == RGB && (nplanes != 9 && nplanes != 18)) {
184+
error = "PTM with RGB colorspace supports only 9 or 18 planes";
185+
return false;
186+
}
187+
}
178188

179189
if((type == RBF || type == BILINEAR) && (colorspace != MRGB && colorspace != MYCC)) {
180190
error = "RBF and BILINEAR support only MRGB and MYCC";
@@ -391,41 +401,64 @@ MaterialBuilder RtiBuilder::pickBasePTM(std::vector<Vector3f> &lights) {
391401
uint32_t dim = ndimensions*3;
392402
MaterialBuilder mat;
393403
mat.mean.resize(dim, 0.0);
394-
395-
Eigen::MatrixXd A(lights.size(), 6);
404+
405+
Eigen::MatrixXf A(lights.size(), 6);
396406
for(uint32_t l = 0; l < lights.size(); l++) {
397407
Vector3f &light = lights[l];
398408
A(l, 0) = 1.0;
399-
A(l, 1) = double(light[0]);
400-
A(l, 2) = double(light[1]);
401-
A(l, 3) = double(light[0]*light[0]);
402-
A(l, 4) = double(light[0]*light[1]);
403-
A(l, 5) = double(light[1]*light[1]);
404-
}
405-
406-
Eigen::MatrixXd iA = (A.transpose()*A).inverse()*A.transpose();
407-
408-
if(colorspace == LRGB) {
409-
assert(nplanes == 9);
410-
//we could generalize for different polynomials
411-
412-
std::vector<float> &proj = mat.proj;
413-
proj.resize((nplanes-3)*ndimensions, 0.0);
414-
for(uint32_t p = 0; p < nplanes-3; p++) {
415-
for(uint32_t k = 0; k < lights.size(); k++) {
416-
uint32_t off = k + p*ndimensions;
417-
proj[off] = float(iA(p, k));
409+
A(l, 1) = light[0];
410+
A(l, 2) = light[1];
411+
A(l, 3) = light[0]*light[0];
412+
A(l, 4) = light[0]*light[1];
413+
A(l, 5) = light[1]*light[1];
414+
}
415+
416+
if((colorspace == LRGB && nplanes == 6) || (colorspace == RGB && nplanes == 9)) {
417+
A.conservativeResize(lights.size(), 3);
418+
}
419+
420+
Eigen::MatrixXf iA = (A.transpose()*A).inverse()*A.transpose();
421+
if(iA.hasNaN()) {
422+
Eigen::MatrixXf AN(lights.size(), nplanes);
423+
for(uint32_t l = 0; l < lights.size(); l++) {
424+
Vector3f &light = lights[l];
425+
if(colorspace == LRGB) {
426+
A(l, 0) = 1.0;
427+
A(l, 1) = light[0];
428+
A(l, 2) = light[1];
429+
check for number of planes
430+
A(l, 3) = light[0]*light[0];
431+
A(l, 4) = light[0]*light[1];
432+
A(l, 5) = light[1]*light[1];
433+
} else {
434+
A(l, 0) = 1.0;
435+
A(l, 1) = light[0];
436+
A(l, 2) = light[1];
437+
A(l, 3) = light[0]*light[0];
438+
A(l, 4) = light[0]*light[1];
439+
A(l, 5) = light[1]*light[1];
418440
}
419-
}
441+
}
442+
mat.svd.compute(A, Eigen::ComputeThinU | Eigen::ComputeThinV);
420443
} else {
421-
assert(colorspace == RGB && nplanes == 18);
422-
423-
//nplanes should be 18 here!
424-
std::vector<float> &proj = mat.proj;
425-
proj.resize(nplanes*dim, 0.0);
426-
for(uint32_t p = 0; p < nplanes; p += 3) {
427-
for(uint32_t k = 0; k < lights.size(); k ++) {
428-
proj[k*3+0 + (p+0)*dim] = proj[k*3+1 + (p+1)*dim] = proj[k*3+2 + (p+2)*dim] = float(iA(p/3, k));
444+
if(colorspace == LRGB) {
445+
//we could generalize for different polynomials
446+
447+
std::vector<float> &proj = mat.proj;
448+
proj.resize((nplanes-3)*ndimensions, 0.0);
449+
for(uint32_t p = 0; p < nplanes-3; p++) {
450+
for(uint32_t k = 0; k < lights.size(); k++) {
451+
uint32_t off = k + p*ndimensions;
452+
proj[off] = float(iA(p, k));
453+
}
454+
}
455+
} else {
456+
std::vector<float> &proj = mat.proj;
457+
proj.resize(nplanes*dim, 0.0);
458+
for(uint32_t p = 0; p < nplanes; p += 3) {
459+
for(uint32_t k = 0; k < lights.size(); k ++) {
460+
proj[k*3+0 + (p+0)*dim] = proj[k*3+1 + (p+1)*dim] = proj[k*3+2 + (p+2)*dim] = float(iA(p/3, k));
461+
}
429462
}
430463
}
431464
}
@@ -1924,15 +1957,22 @@ std::vector<float> RtiBuilder::toPrincipal(Pixel &pixel, MaterialBuilder &materi
19241957

19251958

19261959
} else { //RGB, YCC
1927-
vector<float> col(dim);
1960+
if(!materialbuilder.svd.computeU()) { //not rank deficient.
1961+
vector<float> col(dim);
19281962

1929-
for(size_t k = 0; k < dim; k++)
1930-
col[k] = v[k] - materialbuilder.mean[k];
1963+
for(size_t k = 0; k < dim; k++)
1964+
col[k] = v[k] - materialbuilder.mean[k];
19311965

1932-
for(size_t p = 0; p < nplanes; p++) {
1933-
for(size_t k = 0; k < dim; k++) {
1934-
res[p] += col[k] * materialbuilder.proj[k + p*dim];
1966+
for(size_t p = 0; p < nplanes; p++) {
1967+
for(size_t k = 0; k < dim; k++) {
1968+
res[p] += col[k] * materialbuilder.proj[k + p*dim];
1969+
}
19351970
}
1971+
} else {
1972+
Eigen::Map<Eigen::VectorXf> ev(v, ndimensions*3);
1973+
Eigen::Map<Eigen::VectorXf> eres(&*res.begin(), nplanes);
1974+
1975+
eres = materialbuilder.svd.solve(ev);
19361976
}
19371977
if(colorspace == YCC) {
19381978
int count = 0;

relightlab/rtiframe.cpp

+22-3
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ void RtiFrame::init() {
105105
export_row->suggestPath();
106106
zoom_view->init();
107107
zoom_view->setRect(qRelightApp->project().crop);
108+
updateNPlanes();
108109
}
109110

110111
void RtiFrame::exportRti() {
@@ -117,6 +118,11 @@ void RtiFrame::exportRti() {
117118
return;
118119
}
119120

121+
if(project.dome.directions.size()*3 < parameters.nplanes) {
122+
QMessageBox::warning(this, "Too few light directions.", "The number of coefficient planes is too high for the number of light directions available.");
123+
return;
124+
}
125+
120126
if(parameters.path.isEmpty()) {
121127
QMessageBox::warning(this, "Destination path is missing.", "Fill in the output folder or the filename for the RTI.");
122128
return;
@@ -161,12 +167,25 @@ void RtiFrame::updateNPlanes() {
161167

162168
auto &nplanes = parameters.nplanes;
163169
auto &nchroma = parameters.nchroma;
170+
Project &project = qRelightApp->project();
164171

165172
switch(parameters.basis) {
166-
case Rti::PTM:
167-
nplanes = parameters.colorspace == Rti::RGB? 18: 9;
173+
case Rti::PTM: {
174+
QList<int> n_planes;
175+
if(parameters.colorspace == Rti::RGB) {
176+
n_planes.push_back(9);
177+
if(project.dome.directions.size() > 6)
178+
n_planes.push_back(18);
179+
}
180+
if(parameters.colorspace == Rti::LRGB) {
181+
n_planes.push_back(6);
182+
if(project.dome.directions.size() > 6)
183+
n_planes.push_back(9);
184+
}
185+
nplanes = n_planes.back();
168186
nchroma = 0;
169-
planes_row->forceNPlanes(nplanes);
187+
planes_row->forceNPlanes(n_planes);
188+
}
170189
break;
171190
case Rti::HSH:
172191
nplanes = parameters.colorspace == Rti::RGB? 27 : 12;

relightlab/rtiplan.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,20 @@ RtiPlanesRow::RtiPlanesRow(RtiParameters &parameters, QFrame *parent): RtiPlanRo
145145
setNChroma(parameters.nchroma);
146146
}
147147

148+
void RtiPlanesRow::forceNPlanes(QList<int> n_planes) {
149+
QStandardItemModel *model = qobject_cast<QStandardItemModel *>(nplanesbox->model());
150+
Q_ASSERT(model != nullptr);
151+
for(int i = 0; i < 7; i++) {
152+
QStandardItem *item = model->item(i);
153+
bool disabled = !n_planes.contains(nplanes[i]);
154+
item->setFlags(disabled ? item->flags() & ~Qt::ItemIsEnabled
155+
: item->flags() | Qt::ItemIsEnabled);
156+
if(!disabled)
157+
nplanesbox->setCurrentIndex(i);
158+
}
159+
}
160+
161+
148162
void RtiPlanesRow::forceNPlanes(int n_planes) {
149163
QStandardItemModel *model = qobject_cast<QStandardItemModel *>(nplanesbox->model());
150164
Q_ASSERT(model != nullptr);

relightlab/rtiplan.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,11 @@ class RtiPlanesRow: public RtiPlanRow {
5656
RtiPlanesRow(RtiParameters &parameters, QFrame *parent = nullptr);
5757
void setNPlanes(int nplanes, bool emitting = false);
5858
void setNChroma(int nchroma, bool emitting = false);
59+
void forceNPlanes(QList<int> nplanes);
5960
void forceNPlanes(int nplanes);
6061
private:
6162
QComboBox *nplanesbox, *nchromabox;
62-
int nplanes[7] = { 9, 12, 15, 18, 21, 24, 27 };
63+
int nplanes[8] = { 6, 9, 12, 15, 18, 21, 24, 27 };
6364
int nchromas[3] = { 1, 2, 3 };
6465
signals:
6566
void nplanesChanged();

src/material.h

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <cstdint>
55
#include <vector>
66
#include <algorithm>
7+
#include <Eigen/Dense>
78

89
class Material {
910
public:
@@ -47,6 +48,7 @@ class MaterialBuilder {
4748
public:
4849
std::vector<float> proj;
4950
std::vector<float> mean;
51+
Eigen::JacobiSVD<Eigen::MatrixXf> svd;
5052
};
5153

5254

0 commit comments

Comments
 (0)