Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
63 changes: 31 additions & 32 deletions meshroom/aliceVision/SphereDetection.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "1.0"
__version__ = "2.0"

from meshroom.core import desc
from meshroom.core.utils import VERBOSE_LEVEL
Expand Down Expand Up @@ -31,45 +31,43 @@ class SphereDetection(desc.CommandLineNode):
description="Automatic detection of calibration spheres.",
value=False,
),
desc.Circle(
name="sphereShape",
label="Sphere Shape",
description="The shape of the calibration sphere for every image.",
enabled=lambda node: not node.autoDetect.value,
group=lambda node: None if node.sphereFile.value else "allParams",
keyable=True,
keyType="viewId",
),
desc.File(
name="sphereFile",
label="Sphere Shape File",
description="An input JSON file containing the shapes for every image. If provided, "
"the shapes provided with \"Sphere Shape\" will be ignored.",
semantic="shapeFile",
value="",
enabled=lambda node: not node.autoDetect.value,
group=lambda node: None if not node.sphereFile.value else "allParams",
),
desc.BoolParam(
name="fillMissingSpheres",
label="Fill Missing Spheres",
description="Checked if a sphere position is to be written as detected although it "
"was not provided. In that case, the position of the last known sphere "
"will be used.",
value=False,
enabled=lambda node: not node.autoDetect.value,
),
desc.FloatParam(
name="minScore",
label="Minimum Score",
description="Minimum score for the detection.",
value=0.0,
range=(0.0, 50.0, 0.01),
enabled=lambda node: node.autoDetect.value,
advanced=True,
),
desc.GroupAttribute(
name="sphereCenter",
label="Sphere Center",
description="Center of the circle (XY offset to the center of the image in pixels).",
groupDesc=[
desc.FloatParam(
name="x",
label="x",
description="X offset in pixels.",
value=0.0,
range=(-1000.0, 10000.0, 1.0),
),
desc.FloatParam(
name="y",
label="y",
description="Y offset in pixels.",
value=0.0,
range=(-1000.0, 10000.0, 1.0),
),
],
enabled=lambda node: not node.autoDetect.value,
group=None, # skip group from command line
),
desc.FloatParam(
name="sphereRadius",
label="Radius",
description="Sphere radius in pixels.",
value=500.0,
range=(0.0, 10000.0, 0.1),
enabled=lambda node: not node.autoDetect.value,
),
desc.ChoiceParam(
name="verboseLevel",
label="Verbose Level",
Expand All @@ -84,6 +82,7 @@ class SphereDetection(desc.CommandLineNode):
name="output",
label="Output Path",
description="Sphere detection information will be written here.",
semantic="shapeFile",
value="{nodeCacheFolder}/detection.json",
)
]
4 changes: 2 additions & 2 deletions meshroom/multi-viewPhotometricStereo.mg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"header": {
"releaseVersion": "2025.1.0",
"releaseVersion": "2026.1.0+develop",
"fileVersion": "2.0",
"nodesVersions": {
"CameraInit": "12.0",
Expand All @@ -17,7 +17,7 @@
"PrepareDenseScene": "3.1",
"SfMFilter": "1.0",
"SfMTransfer": "2.1",
"SphereDetection": "1.0",
"SphereDetection": "2.0",
"StructureFromMotion": "3.3",
"Texturing": "6.0"
},
Expand Down
4 changes: 2 additions & 2 deletions meshroom/photometricStereo.mg
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"header": {
"releaseVersion": "2025.1.0",
"releaseVersion": "2026.1.0+develop",
"fileVersion": "2.0",
"nodesVersions": {
"CameraInit": "12.0",
"CopyFiles": "1.3",
"LightingCalibration": "1.0",
"PhotometricStereo": "1.0",
"SphereDetection": "1.0"
"SphereDetection": "2.0"
},
"template": true
},
Expand Down
27 changes: 22 additions & 5 deletions src/aliceVision/lightingEstimation/lightingCalibration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,138 +35,155 @@
namespace aliceVision {
namespace lightingEstimation {

void lightCalibration(const sfmData::SfMData& sfmData,
const std::string& inputFile,
const std::string& outputPath,
const std::string& method,
const bool doDebug,
const bool saveAsModel,
const bool ellipticEstimation)
{
std::vector<std::string> imageList;
std::vector<float> focals;

std::map<std::string, sfmData::View> viewMap;
for (auto& viewIt : sfmData.getViews())
{
std::map<std::string, std::string> currentMetadata = sfmData.getView(viewIt.first).getImage().getMetadata();

if (currentMetadata.find("Exif:DateTimeDigitized") == currentMetadata.end())
{
std::cout << "No metadata case" << std::endl;
viewMap[sfmData.getView(viewIt.first).getImage().getImagePath()] = sfmData.getView(viewIt.first);
}
else
{
viewMap[currentMetadata.at("Exif:DateTimeDigitized")] = sfmData.getView(viewIt.first);
}
}

std::vector<std::array<float, 3>> allSpheresParams;
std::vector<Eigen::Matrix3f> KMatrices;

int nbCols = 0;
int nbRows = 0;

// Main tree
bpt::ptree fileTree;

// Read the json file and initialize the tree
bpt::read_json(inputFile, fileTree);

// Spheres tree
bpt::ptree spheresTree;

// Initialize spheres tree
const auto shapesTreeOpt = fileTree.get_child_optional("shapes");
if (shapesTreeOpt && !shapesTreeOpt->empty())
{
const auto& firstShapeTree = shapesTreeOpt->begin()->second;
spheresTree = firstShapeTree.get_child("observations");
}
else
{
ALICEVISION_THROW_ERROR("Cannot find sphere detection data in '" << inputFile << "'.");
}

for (const auto& [currentId, currentView] : viewMap)
{
ALICEVISION_LOG_INFO("View Id: " << currentView.getViewId());
const fs::path imagePath = fs::path(currentView.getImage().getImagePath());

if (!boost::algorithm::icontains(imagePath.stem().string(), "ambient"))
{
std::string sphereName = std::to_string(currentView.getViewId());
auto sphereExists = (fileTree.get_child_optional(sphereName)).is_initialized();
auto sphereExists = (spheresTree.get_child_optional(sphereName)).is_initialized();
if (sphereExists)
{
ALICEVISION_LOG_INFO(" - " << imagePath.string());
imageList.push_back(imagePath.string());

std::array<float, 3> currentSphereParams;
for (auto& currentSphere : fileTree.get_child(sphereName))
{
currentSphereParams[0] = currentSphere.second.get_child("").get("x", 0.0);
currentSphereParams[1] = currentSphere.second.get_child("").get("y", 0.0);
currentSphereParams[2] = currentSphere.second.get_child("").get("r", 0.0);
const auto& currentSphere = spheresTree.get_child(sphereName);
currentSphereParams[0] = currentSphere.get("center.x", 0.0) - (currentView.getImage().getWidth() / 2);
currentSphereParams[1] = currentSphere.get("center.y", 0.0) - (currentView.getImage().getHeight() / 2);
currentSphereParams[2] = currentSphere.get("radius", 0.0);
}

allSpheresParams.push_back(currentSphereParams);

IndexT intrinsicId = currentView.getIntrinsicId();
focals.push_back(sfmData.getIntrinsics().at(intrinsicId)->getParameters().at(0));

float focalPx = sfmData.getIntrinsics().at(intrinsicId)->getParameters().at(0);
nbCols = sfmData.getIntrinsics().at(intrinsicId)->w();
nbRows = sfmData.getIntrinsics().at(intrinsicId)->h();
float x_p = (nbCols) / 2 + sfmData.getIntrinsics().at(intrinsicId)->getParameters().at(2);
float y_p = (nbRows) / 2 + sfmData.getIntrinsics().at(intrinsicId)->getParameters().at(3);

Eigen::MatrixXf currentK = Eigen::MatrixXf::Zero(3, 3);
// Create K matrix
currentK << focalPx, 0.0, x_p, 0.0, focalPx, y_p, 0.0, 0.0, 1.0;

KMatrices.push_back(currentK);
}
else
{
ALICEVISION_LOG_WARNING("No detected sphere found for '" << imagePath << "'.");
}
}
}

int lightSize = 3;
if (!method.compare("SH"))
lightSize = 9;

Eigen::MatrixXf lightMat(imageList.size(), lightSize);
std::vector<float> intList;

for (size_t i = 0; i < imageList.size(); ++i)
{
std::string picturePath = imageList.at(i);
Eigen::VectorXf lightingDirection = Eigen::VectorXf::Zero(lightSize);
float intensity;

float focal = focals.at(i);
std::array<float, 3> sphereParam = allSpheresParams.at(i);
if (ellipticEstimation)
{
Eigen::Matrix3f K = KMatrices.at(i);
float sphereRadius = 1.0;
std::array<float, 5> ellipseParam;

cv::Mat maskCV = cv::Mat::zeros(nbRows, nbCols, CV_8UC1);

image::Image<float> imageFloat;
image::readImage(picturePath, imageFloat, image::EImageColorSpace::NO_CONVERSION);
getEllipseMaskFromSphereParameters(sphereParam, K, ellipseParam);
calibrateLightFromRealSphere(imageFloat, maskCV, K, sphereRadius, method, lightingDirection, intensity);
}
else
{
lightCalibrationOneImage(picturePath, sphereParam, focal, method, lightingDirection, intensity);
}

lightMat.row(i) = lightingDirection;
intList.push_back(intensity);

if (doDebug)
{
int outputSize = 1024;
std::string outputFileName =
fs::path(outputPath).parent_path().string() + "/" + fs::path(picturePath).stem().string() + "_" + method + ".png";
sphereFromLighting(lightingDirection, lightingDirection.norm(), outputFileName, outputSize);
}
}

// Write in JSON file
writeJSON(outputPath, sfmData, imageList, lightMat, intList, saveAsModel, method);
}

Check notice on line 186 in src/aliceVision/lightingEstimation/lightingCalibration.cpp

View check run for this annotation

codefactor.io / CodeFactor

src/aliceVision/lightingEstimation/lightingCalibration.cpp#L38-L186

Complex Method
void lightCalibrationOneImage(const std::string& picturePath,
const std::array<float, 3>& sphereParam,
const float focal,
Expand Down
Loading
Loading