Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Photometric Stereo] MultiView fusion in Texturing #1590

Merged
merged 67 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
0f4d08d
[mvsUtils] Add error case
jmelou Jul 21, 2023
3b2df20
[PS] Add check for metadata
jmelou Jul 21, 2023
7100998
[RTI] Add import from Lp file (RTI)
jmelou Jul 21, 2023
6634a7a
[PS] Add some breaks
jmelou Jul 21, 2023
8fcbc90
[lightingCalibration] Use perspective hypothesis for brightest point …
jmelou Jul 21, 2023
013aebb
[PS] Typo and break for Lp files
jmelou Sep 7, 2023
5ce4f36
[texturing] Add default filetype (none)
jmelou Sep 12, 2023
4f8d343
[multi_view] Change MultiViewParams constructor
jmelou Sep 12, 2023
8cbd151
[multi-view] Change call to MultiViewParams constructor
jmelou Sep 12, 2023
cc17359
[texturing] Add margin as parameter
jmelou Sep 12, 2023
4da0075
[texturing] Allow texturing with normalMaps
jmelou Sep 12, 2023
13ee765
[texturing] Debug check for pixels that had been visited before
jmelou Sep 18, 2023
55b458b
[lightCalibration] Debug read lp and json lightfiles
jmelou Sep 25, 2023
4ea4acf
[lightingCalibration] Check for metadata
jmelou Oct 29, 2023
f06c170
[lighting calibration] Check for metadata (part. II)
jmelou Oct 29, 2023
ce5f74a
[lighting calibration] Add debug function
jmelou Oct 29, 2023
a6800eb
[ps] Typo
jmelou Oct 29, 2023
664cc68
[ps] Add log, indent
jmelou Oct 29, 2023
5bd31ca
[ps] Add png output
jmelou Oct 29, 2023
f641785
[ps] Resolve issue in mask values
jmelou Oct 30, 2023
af00331
[ps] resolve issue in mask values (part. 2)
jmelou Oct 30, 2023
1ecf282
[lighting calibration] Add spherical harmonics estimation
jmelou Oct 30, 2023
3fe41de
[ps] Debug tiling
jmelou Nov 3, 2023
a82f25c
[mesh] Texturing: Use integral type for OMP parallel for
cbentejac Nov 14, 2023
af331e4
[ps] Spherical Harmonics : Change "HS" in "SH
jmelou Nov 20, 2023
674e3a9
[ps] Second version for SH calibration
jmelou Nov 23, 2023
20e91f6
[software] Texturing: Fix formatting issue
cbentejac Nov 24, 2023
93e5162
PS: Apply formating with clang-format
cbentejac Nov 24, 2023
1929c86
[cameraInit] Allow more PS folder names
jmelou Mar 18, 2024
1373a34
[PS] Add png export for albedo
jmelou Mar 19, 2024
ad8ac37
[PS] Data IO : change light normalisation
jmelou Mar 19, 2024
2f9b3d2
[LightCalibration] Add functions for elliptical case
jmelou Mar 20, 2024
8514055
[PS] LightCalibration : allow use of mask instead of json
jmelou Mar 20, 2024
8a3c0d7
[PS] Lighting Calibration : add functions for calibration from mask
jmelou Mar 20, 2024
d323304
[PS] Lighting calibration : debug perspective estimation
jmelou Mar 20, 2024
ff4bb7b
[PS] Lighting calibration : add needed include
jmelou Mar 20, 2024
cdf234f
[PS] Lighting calibration : allows use of mask instead of json
jmelou Mar 20, 2024
8da0dc9
[PS] Lighting calibration : sphere detection file as input instead of…
jmelou Mar 21, 2024
b1847aa
[PS] Fix png output
jmelou Apr 29, 2024
8bbe127
[lightingCalibration] Debug ellipse geometry
jmelou Apr 29, 2024
1c65593
[sphereDetection] Change output
jmelou Apr 29, 2024
7a42452
[lightingCalibration] Use CVmask fir ellipse
jmelou Apr 29, 2024
61e98b0
[LightingCalibration] Ellipse geometry : use OpenCV mask
jmelou Apr 29, 2024
8de36df
[lightingCaliration] Forgotten in the previous commit
jmelou Apr 29, 2024
54f1f64
[lightingCalibration] Add ellipseGeometry in cmakelist
jmelou Apr 29, 2024
1cdf820
[NormalIntegration] Debug normal integration
jmelou May 2, 2024
173acbb
[PS - I/O] Add new function for mask reading
jmelou May 31, 2024
b74da78
[PS] Debug read of a built model
jmelou May 31, 2024
070420d
[PS] Debug sub-masks creation
jmelou May 31, 2024
4aff764
[PS] Correct spelling of "ambient"
jmelou Jun 2, 2024
e0177ef
build fixes
fabiencastan Jun 3, 2024
0a9485c
[bin] lidarMeshing: build fix related to MultiViewParams constructor …
fabiencastan Jun 3, 2024
1aec3b0
[cmake] include order
fabiencastan Jun 4, 2024
3263e1f
[cmake] minor build cleanup
fabiencastan Jun 5, 2024
9b99573
[photometricStereo] msvc build fix
fabiencastan Jun 5, 2024
7dc46c3
[lightingCalibration] Add option for elliptic estimation
jmelou Jun 5, 2024
8fab68c
[sphereDetection] Same output for all methods
jmelou Jun 5, 2024
aca03db
[PS] Spelling correction
jmelou Jun 5, 2024
dd064ef
[LightingCalibration] Back to orthographic projection for incident ray
jmelou Jun 7, 2024
1eb6269
[LightingCalibration] Comments and vrariable names
jmelou Jun 7, 2024
49a4b13
[LightingCalibration] Remove case of non-JSON file
jmelou Jun 7, 2024
8c2e9d4
[PS] Remove useless code
jmelou Jun 7, 2024
4b859ac
[lightingCalibration] Add debug
jmelou Jul 17, 2024
24580ec
[lightingCalibration] Be more restrictive on selected pixels
jmelou Jul 17, 2024
255a315
[lightingCalibration] Add debug (hpp)
jmelou Jul 17, 2024
8dd7a59
[lightingCalibration] Add debug (main)
jmelou Jul 17, 2024
977f4cc
[photometricStereo] Add outputs
jmelou Jul 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ option(ALICEVISION_BUILD_SFM "Build AliceVision SfM part" ON)
option(ALICEVISION_BUILD_MVS "Build AliceVision MVS part" ON)
option(ALICEVISION_BUILD_HDR "Build AliceVision HDR part" ON)
option(ALICEVISION_BUILD_SEGMENTATION "Build AliceVision Segmentation part" ON)
option(ALICEVISION_BUILD_STEREOPHOTOMETRY "Build AliceVision StereoPhotometry part" ON)
option(ALICEVISION_BUILD_PHOTOMETRICSTEREO "Build AliceVision Photometric Stereo part" ON)
option(ALICEVISION_BUILD_PANORAMA "Build AliceVision panorama part" ON)
option(ALICEVISION_BUILD_SOFTWARE "Build AliceVision command line tools." ON)
option(ALICEVISION_BUILD_COVERAGE "Enable code coverage generation (gcc only)" OFF)
Expand Down Expand Up @@ -94,7 +94,7 @@ if(NOT ALICEVISION_BUILD_SFM)
SET(ALICEVISION_BUILD_MVS OFF)
SET(ALICEVISION_BUILD_HDR OFF)
SET(ALICEVISION_BUILD_SEGMENTATION OFF)
SET(ALICEVISION_BUILD_STEREOPHOTOMETRY OFF)
SET(ALICEVISION_BUILD_PHOTOMETRICSTEREO OFF)
SET(ALICEVISION_BUILD_PANORAMA OFF)
SET(ALICEVISION_BUILD_LIDAR OFF)
endif()
Expand Down
4 changes: 2 additions & 2 deletions src/aliceVision/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ endif()
# MVS modules

if(ALICEVISION_BUILD_MVS)
add_subdirectory(lightingEstimation)
add_subdirectory(mesh)
add_subdirectory(mvsData)
add_subdirectory(mvsUtils)
Expand All @@ -72,12 +71,13 @@ if(ALICEVISION_BUILD_SFM AND ALICEVISION_BUILD_MVS)
add_subdirectory(sfmMvsUtils)
endif()

if (ALICEVISION_BUILD_STEREOPHOTOMETRY)
if (ALICEVISION_BUILD_PHOTOMETRICSTEREO)
if(ALICEVISION_HAVE_OPENCV)
add_subdirectory(photometricStereo)
if(ALICEVISION_HAVE_ONNX)
add_subdirectory(sphereDetection)
endif()
add_subdirectory(lightingEstimation)
endif()
endif()

Expand Down
2 changes: 1 addition & 1 deletion src/aliceVision/fuseCut/DelaunayGraphCut_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE(fuseCut_delaunayGraphCut)
const NViewDatasetConfigurator config(1000, 1000, 500, 500, 1, 0);
SfMData sfmData = generateSfm(config, 6);

mvsUtils::MultiViewParams mp(sfmData, "", "", "", false);
mvsUtils::MultiViewParams mp(sfmData, "", "", "");

mp.userParams.put("LargeScale.universePercentile", 0.999);
mp.userParams.put("delaunaycut.seed", 1);
Expand Down
4 changes: 4 additions & 0 deletions src/aliceVision/lightingEstimation/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,24 @@ set(lightingEstimation_files_headers
augmentedNormals.hpp
lightingEstimation.hpp
lightingCalibration.hpp
ellipseGeometry.hpp
)

# Sources
set(lightingEstimation_files_sources
augmentedNormals.cpp
lightingEstimation.cpp
lightingCalibration.cpp
ellipseGeometry.cpp
)

alicevision_add_library(aliceVision_lightingEstimation
SOURCES ${lightingEstimation_files_headers} ${lightingEstimation_files_sources}
PUBLIC_LINKS
${OpenCV_LIBS}
aliceVision_image
aliceVision_system
aliceVision_photometricStereo
)


Expand Down
4 changes: 2 additions & 2 deletions src/aliceVision/lightingEstimation/augmentedNormals.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ class AugmentedNormal : public Eigen::Matrix<float, 9, 1, 0, 9, 1>
inline const T& nz() const { return (*this)(2); }
inline T& nz() { return (*this)(2); }

inline const T& nambiant() const { return (*this)(3); }
inline T& nambiant() { return (*this)(3); }
inline const T& nambient() const { return (*this)(3); }
inline T& nambient() { return (*this)(3); }

inline const T& nx_ny() const { return (*this)(4); }
inline T& nx_ny() { return (*this)(4); }
Expand Down
232 changes: 232 additions & 0 deletions src/aliceVision/lightingEstimation/ellipseGeometry.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
// This file is part of the AliceVision project.
// Copyright (c) 2023 AliceVision contributors.
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

#include "ellipseGeometry.hpp"
#include <aliceVision/photometricStereo/photometricDataIO.hpp>
#include <aliceVision/image/io.hpp>

#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>

#include <algorithm>
#include <limits>

namespace aliceVision {
namespace lightingEstimation {

#define PI 3.14159265

void quadraticFromEllipseParameters(const std::array<float, 5>& ellipseParameters,
Eigen::Matrix3f& Q)
{
float phi = ellipseParameters[0]*PI/180.0;
float c_x = ellipseParameters[1];
float c_y = ellipseParameters[2];
float b = ellipseParameters[3];
float a = ellipseParameters[4];

float A = a*a*sin(phi)*sin(phi) + b*b*cos(phi)*cos(phi);
float B = 2*(b*b-a*a)*sin(phi)*cos(phi);
float C = a*a*cos(phi)*cos(phi) + b*b*sin(phi)*sin(phi);
float D = -2*A*c_x - B*c_y;
float E = -B*c_x - 2*C*c_y;
float F = A*c_x*c_x + C*c_y*c_y - a*a*b*b + B*c_x*c_y;

Q << A, B/2, D/2,
B/2, C, E/2,
D/2, E/2, F;

Q /= Q.norm();
}

int findUniqueIndex(const std::vector<int>& vec) {

std::unordered_map<int, int> countMap;

// Compter le nombre d'occurrences de chaque élément dans le vecteur
for (int num : vec) {
countMap[num]++;
}

int toReturn = -1;
// Trouver l'élément avec une seule occurrence
for (size_t i = 0; i < vec.size(); ++i) {
if (countMap[vec[i]] == 1) {
toReturn = i;
}
}

return toReturn;
}

void estimateSphereCenter(const std::array<float, 5>& ellipseParameters,
const float sphereRadius,
const Eigen::Matrix3f& K,
std::array<float, 3>& sphereCenter)
{
Eigen::Matrix3f Q;
quadraticFromEllipseParameters(ellipseParameters, Q);

Eigen::Matrix3f M = K.transpose()*Q*K;
Eigen::EigenSolver<Eigen::MatrixXf> es(M);

Eigen::Vector3f eigval = es.eigenvalues().real();

std::vector<int> eigvalSign;

for (int i = 0; i < 3; ++i) {
eigvalSign.push_back((eigval[i] > 0) ? 1 : -1);
}

int index = findUniqueIndex(eigvalSign);
float uniqueEigval = eigval[index];

float dist = sqrt(1 - ((eigval[0] + eigval[1] + eigval[2] - uniqueEigval)/2) / uniqueEigval);

Eigen::Vector3f eigvect = es.eigenvectors().col(index).real();
float sign = eigvect[2] > 0 ? 1 : -1;

float norm = eigvect.norm();
float C_factor = sphereRadius*dist*sign/norm;
Eigen::Vector3f C = C_factor*eigvect;
sphereCenter[0]=C[0];
sphereCenter[1]=C[1];
sphereCenter[2]=C[2];
}

void sphereRayIntersection(const Eigen::Vector3f& direction,
const std::array<float, 3>& sphereCenter,
const float sphereRadius,
float& delta,
Eigen::Vector3f& normal)
{
float a = direction.dot(direction);

Eigen::Vector3f spCenter;
spCenter << sphereCenter[0], sphereCenter[1], sphereCenter[2];

float b = -2*direction.dot(spCenter);
float c = spCenter.dot(spCenter) - sphereRadius*sphereRadius;

delta = b*b - 4*a*c;

float factor;

if (delta >= 0) {
factor = (-b - sqrt(delta))/(2*a);
normal = (direction*factor - spCenter)/sphereRadius;
}
else {
delta = -1;
}
}

void estimateSphereNormals(const std::array<float, 3>& sphereCenter,
const float sphereRadius,
const Eigen::Matrix3f& K,
image::Image<image::RGBfColor>& normals,
image::Image<float>& newMask)
{
Eigen::Matrix3f invK = K.inverse();

for (int i = 0; i < normals.rows(); ++i) {
for (int j = 0; j < normals.cols(); ++j) {
// Get homogeneous coordinates of the pixel :
Eigen::Vector3f coordinates = Eigen::Vector3f(j, i, 1.0f);

// Get the direction of the ray :
Eigen::Vector3f direction = invK*coordinates;

// Estimate the interception of the ray with the sphere :
float delta = 0.0;
Eigen::Vector3f normal = Eigen::Vector3f(0.0f, 0.0f, 0.0f);

sphereRayIntersection(direction, sphereCenter, sphereRadius, delta, normal);

// If the ray intercepts the sphere we add this pixel to the new mask :
if(delta > 0)
{
newMask(i, j) = 1.0;
normals(i, j) = image::RGBfColor(normal[0], normal[1], normal[2]);
}
else
{
newMask(i, j) = 0.0;
normals(i, j) = image::RGBfColor(0.0, 0.0, 0.0);
}
}
}
}

void getRealNormalOnSphere(const cv::Mat& maskCV,
const Eigen::Matrix3f& K,
const float sphereRadius,
image::Image<image::RGBfColor>& normals,
image::Image<float>& newMask)
{

// Apply a threshold to the image
int thresholdValue = 150;
cv::Mat thresh;
cv::threshold(maskCV, thresh, thresholdValue, 255, 0);

// Find contours
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(thresh, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);

// Fit the ellipse to the first contour
std::vector<cv::Point> cnt = contours[0];
cv::RotatedRect ellipse = cv::fitEllipse(cnt);
float angle = ellipse.angle;
cv::Size2f size = ellipse.size;
cv::Point2f center = ellipse.center;

// Ellipse is converted as five-parameter array
std::array<float, 5> ellipseParameters;

ellipseParameters[0] = angle;
ellipseParameters[1] = center.x;
ellipseParameters[2] = center.y;
ellipseParameters[3] = size.width/2;
ellipseParameters[4] = size.height/2;

std::array<float, 3> sphereCenter;
estimateSphereCenter(ellipseParameters, sphereRadius, K, sphereCenter);
estimateSphereNormals(sphereCenter, sphereRadius, K, normals, newMask);
}

void getEllipseMaskFromSphereParameters(const std::array<float, 3>& sphereParam, const Eigen::Matrix3f& K, std::array<float, 5>& ellipseParameters, cv::Mat maskCV)
{

// Distance between center of image and center of disk :
float imX = K(0, 2);
float imY = K(1, 2);
float f = K(0, 0);
float delta = sqrt((sphereParam[0])*(sphereParam[0]) + (sphereParam[1])*(sphereParam[1]));
// sphere params = x,y, radius
// ellipse params = angle, x, y, semi minor axe, semi major axe

// Ellipse from sphere parameters
// semi minor axe = radius;
// semi major axe = formula from paper
// main direction = disc center to picture center
// ellipse center = disc center

float radians = atan((sphereParam[0]) / (sphereParam[1]));
ellipseParameters[0] = radians * (180.0/3.141592653589793238463);

ellipseParameters[1] = sphereParam[0];
ellipseParameters[2] = sphereParam[1];
ellipseParameters[3] = sphereParam[2];


// a² = b² ((distance between image center and disc center)² + f² + b²)/(f² + b²)
ellipseParameters[4] = sqrt((ellipseParameters[3]*ellipseParameters[3]) * (delta * delta + f*f + ellipseParameters[3]*ellipseParameters[3])/(f*f + ellipseParameters[3]*ellipseParameters[3]));
}

} // namespace lightingEstimation
} // namespace aliceVision
75 changes: 75 additions & 0 deletions src/aliceVision/lightingEstimation/ellipseGeometry.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// This file is part of the AliceVision project.
// Copyright (c) 2024 AliceVision contributors.
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

#pragma once

#include <aliceVision/image/Image.hpp>

#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>

#include <string>
#include <vector>
#include <array>

namespace aliceVision {
namespace lightingEstimation {

/**
* @brief Estimate the center of a sphere from the ellipse parameters
* @param[in] ellipseParameters An array of 5 floating-point: the parameters of the ellipse
* @param[in] sphereRadius The radius of the sphere
* @param[in] K Intrinsic parameters of the camera
* @param[out] sphereCenter An array of 3 floating-point: the coordinates of the sphere center in the picture frame
*/
void estimateSphereCenter(const std::array<float, 5>& ellipseParameters,
const float sphereRadius,
const Eigen::Matrix3f& K,
std::array<float, 3>& sphereCenter);

void sphereRayIntersection(const Eigen::Vector3f& direction,
const std::array<float, 3>& sphereCenter,
const float sphereRadius,
float& delta,
Eigen::Vector3f& normal);

void quadraticFromEllipseParameters(const std::array<float, 5>& ellipseParameters,
Eigen::Matrix3f& Q);

int findUniqueIndex(const std::vector<int>& vec);

/**
* @brief Estimate the normals of a sphere from a mask
* @param[in] sphereCenter An array of 3 floating-point: the coordinates of the sphere center in the picture frame
* @param[in] ellipseMask The binary mask of the sphere in the picture
* @param[in] K Intrinsic parameters of the camera
* @param[out] normals Normals on the sphere
* @param[out] newMask The mask of the sphere after ray tracing
*/
void estimateSphereNormals(const std::array<float, 3>& sphereCenter,
const float sphereRadius,
const Eigen::Matrix3f& K,
image::Image<image::RGBfColor>& normals,
image::Image<float>& newMask);

/**
* @brief Estimate the normals of a sphere from a mask
* @param[in] maskCV The openCV image of the binary mask of the ellipse in the picture
* @param[in] K Intrinsic parameters of the camera
* @param[out] normals Normals on the sphere in camera frame
* @param[out] newMask The mask of the sphere after ray tracing
*/
void getRealNormalOnSphere(const cv::Mat& maskCV,
const Eigen::Matrix3f& K,
const float sphereRadius,
image::Image<image::RGBfColor>& normals,
image::Image<float>& newMask);


void getEllipseMaskFromSphereParameters(const std::array<float, 3>& sphereParam, const Eigen::Matrix3f& K, std::array<float, 5>& ellipseParameters, cv::Mat maskCV);

} // namespace lightingEstimation
} // namespace aliceVision
Loading
Loading