diff --git a/relight/mainwindow.cpp b/relight/mainwindow.cpp index 40ed37a..e2d835c 100644 --- a/relight/mainwindow.cpp +++ b/relight/mainwindow.cpp @@ -741,6 +741,11 @@ void MainWindow::detectHighlights() { progress->show(); progress->setMaximum(project.size()); + //clean up reflection summary for each sphere. + for(Sphere *sphere: project.spheres) { + sphere->sphereImg = QImage(); + } + QThreadPool::globalInstance()->setMaxThreadCount(1); progress_jobs.clear(); for(size_t i = 0; i < project.size(); i++) diff --git a/relight/normalstask.cpp b/relight/normalstask.cpp index 2c2723b..946e201 100644 --- a/relight/normalstask.cpp +++ b/relight/normalstask.cpp @@ -18,6 +18,8 @@ #include #include +using namespace std; + //////////////////////////////////////////////////////// NORMALS TASK ////////////////////////////////////////////////////////// /// \brief NormalsTask: Takes care of creating the normals from the images given in a folder (inputFolder) and saves the file /// in the outputFolder. After applying the crop described in the QRect passed as an argument to the constructor, @@ -25,46 +27,43 @@ /// That NormalsWorker fills a vector with the colors of the normals in that line. /// -void NormalsTask::run() -{ +void NormalsTask::run() { status = RUNNING; - // ImageSet initialization - ImageSet imageSet(input_folder.toStdString().c_str()); - QList qlights = (*this)["lights"].value.toList(); std::vector lights(qlights.size()/3); for(int i = 0; i < qlights.size(); i+= 3) for(int k = 0; k < 3; k++) lights[i/3][k] = qlights[i+k].toDouble(); + + + ImageSet imageSet; + imageSet.images = (*this)["images"].value.toStringList(); imageSet.lights = lights; + imageSet.light3d = project->dome.lightConfiguration != Dome::DIRECTIONAL; + imageSet.image_width_mm = project->dome.imageWidth; + imageSet.dome_radius = project->dome.domeDiameter/2.0; + imageSet.vertical_offset = project->dome.verticalOffset; + imageSet.initLights(); + imageSet.initImages(input_folder.toStdString().c_str()); + + if(hasParameter("crop")) { + QRect rect = (*this)["crop"].value.toRect(); + imageSet.crop(rect.left(), rect.top(), rect.width(), rect.height()); + } // Normals vector - int start = clock(); - // Init imageSet.setCallback(nullptr); - // Set the crop - if(!m_Crop.isValid()) { - m_Crop.setLeft(0); - m_Crop.setWidth(imageSet.width); - m_Crop.setTop(0); - m_Crop.setHeight(imageSet.height); - } - imageSet.crop(m_Crop.left(), m_Crop.top(), m_Crop.width(), m_Crop.height()); - std::vector normals(imageSet.width * imageSet.height * 3); - // Thread pool used to handle the processors RelightThreadPool pool; - // Line in the imageset to be processed PixelArray line; pool.start(QThread::idealThreadCount()); - for (int i=0; i run = [this, &line, &imageSet, data](void)->void { - NormalsWorker task(solver, line, data, imageSet.lights); - return task.run(); + NormalsWorker *task = new NormalsWorker(solver, i, line, data, imageSet); + + std::function run = [this, task](void)->void { + task->run(); + delete task; }; - // Launch the task pool.queue(run); pool.waitForSpace(); @@ -118,6 +118,12 @@ void NormalsTask::run() // Save the final result QImage img(normalmap.data(), imageSet.width, imageSet.height, imageSet.width*3, QImage::Format_RGB888); + // Set spatial resolution if known. Need to convert as pixelSize stored in mm/pixel whereas QImage requires pixels/m + if( pixelSize > 0 ) { + int dotsPerMeter = round(1000.0/pixelSize); + img.setDotsPerMeterX(dotsPerMeter); + img.setDotsPerMeterY(dotsPerMeter); + } img.save(output); std::function callback = [this](QString s, int n)->bool { return this->progressed(s, n); }; @@ -148,21 +154,6 @@ void NormalsTask::run() qDebug() << "Time: " << ((double)(end - start) / CLOCKS_PER_SEC); progressed("Finished", 100); } -/* -bool NormalsTask::progressed(QString str, int percent) -{ - if(status == PAUSED) { - mutex.lock(); //mutex should be already locked. this talls the - mutex.unlock(); - } - if(status == STOPPED) - return false; - - emit progress(str, percent); - if(status == STOPPED) - return false; - return true; -}*/ /** * \brief NormalsWorker: generates the normals for a given line in the image set, depending on the method specified when @@ -195,6 +186,8 @@ void NormalsWorker::run() void NormalsWorker::solveL2() { + std::vector &m_Lights = m_Imageset.lights; + std::vector &m_Lights3d = m_Imageset.lights3d; // Pixel data Eigen::MatrixXd mLights(m_Lights.size(), 3); Eigen::MatrixXd mPixel(m_Lights.size(), 1); @@ -202,6 +195,7 @@ void NormalsWorker::solveL2() unsigned int normalIdx = 0; + // Fill the lights matrix for (size_t i = 0; i < m_Lights.size(); i++) for (int j = 0; j < 3; j++) @@ -214,12 +208,19 @@ void NormalsWorker::solveL2() for (size_t m = 0; m < m_Lights.size(); m++) mPixel(m, 0) = m_Row[p][m].mean(); - // Solve - mNormals = (mLights.transpose() * mLights).ldlt().solve(mLights.transpose() * mPixel); - mNormals.col(0).normalize(); + if(m_Imageset.light3d) { + for(size_t i = 0; i < m_Lights3d.size(); i++) { + Vector3f light = m_Imageset.relativeLight(m_Lights3d[i], p, m_Imageset.height - row); + light.normalize(); + for (int j = 0; j < 3; j++) + mLights(i, j) = light[j]; + } + } + - // Save + mNormals = (mLights.transpose() * mLights).ldlt().solve(mLights.transpose() * mPixel); + mNormals.col(0).normalize(); m_Normals[normalIdx+0] = mNormals(0, 0); m_Normals[normalIdx+1] = mNormals(1, 0); m_Normals[normalIdx+2] = mNormals(2, 0); diff --git a/relight/normalstask.h b/relight/normalstask.h index 39fb557..6a5dbe3 100644 --- a/relight/normalstask.h +++ b/relight/normalstask.h @@ -9,6 +9,8 @@ #include #include #include "../src/relight_vector.h" +#include "../src/imageset.h" +#include "../src/project.h" #include "task.h" #include @@ -27,21 +29,23 @@ class NormalsTask : public Task QRect m_Crop; float pixelSize = 0.0f; - NormalsTask(QString& inputPath, QString& outputPath, QRect crop, NormalSolver _solver, FlatMethod _flatMethod) : - solver(_solver), flatMethod(_flatMethod), m_Crop(crop) { - input_folder = inputPath; - output = outputPath; + NormalsTask(Project *_project, NormalSolver _solver, FlatMethod _flatMethod) : + project(_project), solver(_solver), flatMethod(_flatMethod) { } virtual ~NormalsTask(){}; virtual void run() override; +private: + //TODO remove dependency on project! + Project *project; + }; class NormalsWorker { public: - NormalsWorker(NormalSolver _solver, PixelArray& toProcess, float* normals, std::vector lights) : - solver(_solver), m_Row(toProcess), m_Normals(normals), m_Lights(lights){} + NormalsWorker(NormalSolver _solver, int _row, PixelArray& toProcess, float* normals, ImageSet &imageset) : + solver(_solver), row(_row), m_Row(toProcess), m_Normals(normals), m_Imageset(imageset){} void run(); private: @@ -50,9 +54,9 @@ class NormalsWorker void solveRPCA(); private: NormalSolver solver; + int row; PixelArray m_Row; - float* m_Normals; - std::vector m_Lights; + ImageSet &m_Imageset; QMutex m_Mutex; }; diff --git a/relight/qspheremarker.cpp b/relight/qspheremarker.cpp index 2af01c2..53ba692 100644 --- a/relight/qspheremarker.cpp +++ b/relight/qspheremarker.cpp @@ -100,13 +100,14 @@ void SphereMarker::init() { axis[1]->setLine(c.x(), c.y(), c.x() - dir.y()*sphere->eHeight, c.y() + dir.x()*sphere->eHeight); double r1 = r * sphere->eHeight/sphere->eWidth; + smallcircle->setRect(c.x() - r, c.y() - r1, 2*r, 2*r1); smallcircle->setTransformOriginPoint(c); smallcircle->setRotation(sphere->eAngle); } else { circle->setRect(c.x()-R, c.y()-R, 2*R, 2*R); + smallcircle->setRect(c.x()-r, c.y()-r, 2*r, 2*r); } circle->setVisible(true); - smallcircle->setRect(c.x()-r, c.y()-r, 2*r, 2*r); smallcircle->setVisible(true); } diff --git a/relight/relight.pro b/relight/relight.pro index 62f6fb8..639dbb9 100644 --- a/relight/relight.pro +++ b/relight/relight.pro @@ -67,7 +67,6 @@ SOURCES += main.cpp \ task.cpp \ rtitask.cpp \ settingsdialog.cpp \ - ../relight-cli/convert_rti.cpp \ domecalibration.cpp \ ../src/lp.cpp \ qmarkerlist.cpp \ diff --git a/relight/rtiexport.cpp b/relight/rtiexport.cpp index 1bf25f2..31ae665 100644 --- a/relight/rtiexport.cpp +++ b/relight/rtiexport.cpp @@ -176,14 +176,18 @@ void RtiExport::createNormals() { double flat_radius = ui->flat_fourier_radius->value(); - ProcessQueue &queue = ProcessQueue::instance(); - NormalsTask *task = new NormalsTask(path, output, crop, solver, flat_method); + + NormalsTask *task = new NormalsTask(project, solver, flat_method); + task->input_folder = path; + task->output = output; task->exportSurface = ui->export_surface->isChecked(); -// task->exportDepthmap = ui->export_depthmap->isChecked(); + task->exportDepthmap = ui->export_depthmap->isChecked(); task->exportK = ui->discontinuity->value(); task->flat_radius = ui->flat_fourier_radius->value(); + task->addParameter("images", Parameter::STRINGLIST, images); + QList slights; for(auto light: lights) for(int k = 0; k < 3; k++) diff --git a/relight/rtiexport.ui b/relight/rtiexport.ui index 26f9fa6..5d13be5 100644 --- a/relight/rtiexport.ui +++ b/relight/rtiexport.ui @@ -286,8 +286,8 @@ Normals - - + + Solver @@ -326,7 +326,7 @@ - + 3D Surface @@ -339,14 +339,21 @@ - + + + + Export depth map + + + + Discontinuity - + 2.000000000000000 @@ -356,7 +363,7 @@ - + Flatten normals @@ -393,7 +400,7 @@ - + Crop... @@ -404,7 +411,7 @@ - + Qt::Vertical @@ -417,7 +424,7 @@ - + Build diff --git a/relight/rtitask.h b/relight/rtitask.h index cf57a49..b6c0a61 100644 --- a/relight/rtitask.h +++ b/relight/rtitask.h @@ -52,7 +52,6 @@ class RtiTask: public Task { virtual void run() override; public slots: - void relight(bool commonMinMax = false, bool saveLegacy = false); //use true for .rti and .ptm void toRTI(); void fromRTI(); diff --git a/src/imageset.cpp b/src/imageset.cpp index 81aaf15..8f12361 100644 --- a/src/imageset.cpp +++ b/src/imageset.cpp @@ -321,6 +321,7 @@ void ImageSet::readLine(PixelArray &pixels) { if(light3d) { assert(lights3d.size() == size_t(images.size())); for(Pixel &pixel: pixels) { + assert(pixel.size() == lights3d.size()); for(size_t i = 0; i < lights3d.size(); i++) { Vector3f l = relativeLight(lights3d[i], pixel.x, pixel.y); float r = l.squaredNorm(); diff --git a/src/project.cpp b/src/project.cpp index a583a5e..79dd4a2 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -327,6 +327,7 @@ void Project::load(QString filename) { for(auto sphere: obj["spheres"].toArray()) { Sphere *_sphere = new Sphere; _sphere->fromJson(sphere.toObject()); + _sphere->image_size = imgsize; spheres.push_back(_sphere); } } @@ -619,7 +620,7 @@ void Project::computeDirections() { v = v.rotate(axis, angle); if(dome.domeDiameter) { - //find intersection between direAlignctions and sphere. + //find intersection between directions and sphere. for(size_t i = 0; i < sphere->directions.size(); i++) { Vector3f &direction = sphere->directions[i]; direction.normalize(); diff --git a/src/sphere.cpp b/src/sphere.cpp index 3a9104d..be7a45e 100644 --- a/src/sphere.cpp +++ b/src/sphere.cpp @@ -158,7 +158,6 @@ bool Sphere::fit() { center = QPointF(a, b); radius = r; - } float max_angle = (50.0/180.0)*M_PI; //slightly over 45. hoping not to spot reflexes smallradius = radius*sin(max_angle); diff --git a/src/sphere.h b/src/sphere.h index 87471c5..023cef8 100644 --- a/src/sphere.h +++ b/src/sphere.h @@ -20,7 +20,8 @@ struct Line { class Sphere { public: - //TODO: initalize these values somewhere. + + QSize image_size; //size of the picture, needed for properly fitting reflections. QPointF center; //center of circle or ellipse, in pixel coordinates of the image /* Circle parameters */