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
1 change: 0 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ jobs:
arch-name: linuxx86-64
- os: ubuntu-24.04-arm
arch-name: linuxarm64
extra-flags: -DARM64_BUILD=ON
- os: windows-latest
arch-name: windowsx86-64
extra-flags: -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER=clang-cl
Expand Down
6 changes: 1 addition & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w")
if(NOT APPLE)
set(BLA_VENDOR OpenBLAS)

if(ARM64_BUILD)
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
# ----------------------------------------------------------------
# Variables for subdirectory builds must be set in the parent cache
# BEFORE FetchContent_MakeAvailable.
Expand Down Expand Up @@ -117,10 +117,6 @@ if(NOT APPLE)
set(DYNAMIC_ARCH OFF CACHE BOOL "" FORCE)
set(DYNAMIC_OLDER OFF CACHE BOOL "" FORCE)

# Lock the compiler to ARMv8-A baseline. This prevents the compiler
# from emitting SVE intrinsics even if OpenBLAS assembly kernels
# happen to include SVE .S files guarded by preprocessor.
set(CMAKE_C_FLAGS_OPENBLAS "-march=armv8-a" CACHE STRING "" FORCE)
set(COMMON_OPT "-march=armv8-a" CACHE STRING "" FORCE)

# Explicitly set ARCH so OpenBLAS does not re-detect it.
Expand Down
65 changes: 25 additions & 40 deletions src/mrcal_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@
#include <stdlib.h>

#include <algorithm>
#include <chrono>
#include <cstdio>
#include <iostream>
#include <memory>
#include <opencv2/calib3d.hpp>
#include <random>
#include <span>
#include <stdexcept>
Expand Down Expand Up @@ -167,9 +166,6 @@ static std::unique_ptr<mrcal_result> mrcal_calibrate(

// in
int *c_imagersizes = imagersize;
auto point_min_range = -1.0, point_max_range = -1.0;
mrcal_problem_constants_t problem_constants = {
.point_min_range = point_min_range, .point_max_range = point_max_range};

@Gold856 Gold856 Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you follow the logic for this, you'll see this struct is only accessed by function calls in an #ifdef 0 block, so this is never accessed. Future versions of mrcal empty the struct out entirely.

int verbose = 0;

auto stats = mrcal_optimize(
Expand All @@ -179,9 +175,8 @@ static std::unique_ptr<mrcal_result> mrcal_calibrate(
c_observations_board, c_observations_point, Nobservations_board,
Nobservations_point, NULL, -1, // We don't use these, so pass nulls
c_observations_board_pool, c_observations_point_pool, &mrcal_lensmodel,
c_imagersizes, problem_selections, &problem_constants,
calibration_object_spacing, calibration_object_width_n,
calibration_object_height_n, verbose, false);
c_imagersizes, problem_selections, NULL, calibration_object_spacing,
calibration_object_width_n, calibration_object_height_n, verbose, false);

std::vector<double> residuals = {c_x_final, c_x_final + Nmeasurements};
return std::make_unique<mrcal_result>(
Expand Down Expand Up @@ -283,34 +278,31 @@ mrcal_pose_t getSeedPose(const mrcal_point3_t *c_observations_board_pool,
double cy = (imagerSize.height / 2.0) - 0.5;

vector<Point3f> objectPoints;
vector<Point2d> imagePoints;
vector<mrcal_point2_t> mrcal_imagepts;

// Fill in object/image points
for (int i = 0; i < boardSize.height; i++) {
for (int j = 0; j < boardSize.width; j++) {
auto &corner = c_observations_board_pool[i * boardSize.width + j];
// weight<0 means ignored -- filter these out
if (corner.z >= 0) {
imagePoints.emplace_back(corner.x, corner.y);
objectPoints.push_back(Point3f(j * squareSize, i * squareSize, 0));
mrcal_imagepts.emplace_back(
mrcal_point2_t{.x = corner.x, .y = corner.y});
objectPoints.emplace_back(j * squareSize, i * squareSize, 0);
}
}
}

vector<Point2d> imagePoints(mrcal_imagepts.size());
{
// convert from stereographic to pinhole to match python
std::vector<mrcal_point2_t> mrcal_imagepts(imagePoints.size());
std::transform(
imagePoints.begin(), imagePoints.end(), mrcal_imagepts.begin(),
[](const auto &pt) { return mrcal_point2_t{.x = pt.x, .y = pt.y}; });

mrcal_lensmodel_t model{.type = MRCAL_LENSMODEL_STEREOGRAPHIC};
std::vector<mrcal_point3_t> out(imagePoints.size());
std::vector<mrcal_point3_t> out(mrcal_imagepts.size());
const double intrinsics[] = {fx, fy, cx, cy};
bool ret = mrcal_unproject(out.data(), mrcal_imagepts.data(),
mrcal_imagepts.size(), &model, intrinsics);
if (!ret) {
std::cerr << "couldn't unproject!" << std::endl;
BARF("couldn't unproject!");
}
model = {.type = MRCAL_LENSMODEL_PINHOLE};
mrcal_project(mrcal_imagepts.data(), NULL, NULL, out.data(), out.size(),
Expand All @@ -322,23 +314,18 @@ mrcal_pose_t getSeedPose(const mrcal_point3_t *c_observations_board_pool,
}

// Initial guess at intrinsics
Mat cameraMatrix = (Mat_<double>(3, 3) << fx, 0, cx, 0, fy, cy, 0, 0, 1);
Mat cameraMatrix{{3, 3}, {fx, 0.0, cx, 0.0, fy, cy, 0.0, 0.0, 1.0}};
Mat distCoeffs = Mat(4, 1, CV_64FC1, Scalar(0));

Mat_<double> rvec, tvec;
vector<Point3f> objectPoints3;
for (auto a : objectPoints)
objectPoints3.push_back(Point3f(a.x, a.y, 0));

solvePnP(objectPoints3, imagePoints, cameraMatrix, distCoeffs, rvec, tvec,
solvePnP(objectPoints, imagePoints, cameraMatrix, distCoeffs, rvec, tvec,
false, SOLVEPNP_ITERATIVE);

return mrcal_pose_t{.r = {.x = rvec(0), .y = rvec(1), .z = rvec(2)},
.t = {.x = tvec(0), .y = tvec(1), .z = tvec(2)}};
}

mrcal_result::~mrcal_result() { return; }

// Code taken from mrcal, license:
// Copyright (c) 2017-2023 California Institute of Technology ("Caltech"). U.S.
// Government sponsorship acknowledged. All rights reserved.
Expand Down Expand Up @@ -367,7 +354,7 @@ std::unique_ptr<mrcal_result> mrcal_main(
std::vector<double> intrinsics = {focal_length_guess, focal_length_guess,
cx, cy};

std::cout << "Initial solve (geometry only)" << std::endl;
std::printf("Initial solve (geometry only)\n");

mrcal_problem_selections_t options = construct_problem_selections(
{.do_optimize_intrinsics_core = false,
Expand All @@ -389,9 +376,8 @@ std::unique_ptr<mrcal_result> mrcal_main(
}

{
std::cout
<< "Initial solve (geometry and LENSMODEL_STEREOGRAPHIC core only)"
<< std::endl;
std::printf(
"Initial solve (geometry and LENSMODEL_STEREOGRAPHIC core only)\n");
mrcal_problem_selections_t options = construct_problem_selections(
{.do_optimize_intrinsics_core = true,
.do_optimize_intrinsics_distortions = true,
Expand Down Expand Up @@ -443,9 +429,8 @@ std::unique_ptr<mrcal_result> mrcal_main(
std::copy(seedDistortions.begin(), seedDistortions.end(),
intrinsics.begin() + result->intrinsics.size());

std::cout
<< "Optimizing everything except board warp from seeded intrinsics"
<< std::endl;
std::printf(
"Optimizing everything except board warp from seeded intrinsics\n");
mrcal_problem_selections_t options = construct_problem_selections(
{.do_optimize_intrinsics_core = true,
.do_optimize_intrinsics_distortions = true,
Expand All @@ -462,7 +447,7 @@ std::unique_ptr<mrcal_result> mrcal_main(
}

{
std::cout << "Final, full solve" << std::endl;
std::printf("Final, full solve\n");
mrcal_problem_selections_t options = construct_problem_selections(
{.do_optimize_intrinsics_core = true,
.do_optimize_intrinsics_distortions = true,
Expand Down Expand Up @@ -515,20 +500,20 @@ bool undistort_mrcal(cv::Mat *dst, const cv::Mat *cameraMat,
mrcal_lensmodel.LENSMODEL_SPLINED_STEREOGRAPHIC__config.Ny = Ny;
break;
default:
std::cerr << "Unknown lensmodel\n";
BARF("Unknown lensmodel\n");
return false;
}

if (!(dst->cols == 2)) {
std::cerr << "Bad input array size\n";
if (dst->cols != 2) {
BARF("Bad input array size\n");
return false;
}
if (!(dst->type() == CV_64FC2)) {
std::cerr << "Bad input type -- need CV_64F\n";
if (dst->type() != CV_64FC2) {
BARF("Bad input type -- need CV_64F\n");
return false;
}
if (!(dst->isContinuous())) {
std::cerr << "Bad input array -- need continuous\n";
if (!dst->isContinuous()) {
BARF("Bad input array -- need continuous\n");
return false;
}

Expand Down
18 changes: 12 additions & 6 deletions src/mrcal_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,11 @@

#pragma once

extern "C" {
// Seems to be missing C++ guards
#include <mrcal.h>

} // extern "C"

#include <memory>
#include <opencv2/opencv.hpp>
#include <opencv2/core/mat.hpp>
#include <opencv2/core/types.hpp>
#include <span>
#include <utility>
#include <vector>
Expand All @@ -44,9 +41,18 @@ struct mrcal_result {
rms_error{rms_error_}, residuals{std::move(residuals_)},
calobject_warp{calobject_warp_}, Noutliers_board{Noutliers_board_} {}
mrcal_result(mrcal_result &&) = delete;
~mrcal_result();
~mrcal_result() = default;
};

/**
* Gets the seed pose for a board.
*
* @param c_observations_board_pool The corners in image space.
* @param boardSize The size of the corner grid.
* @param imagerSize The size of the image in pixels.
* @param squareSize The size of the squares in a physical distance unit.
* @param focal_len_guess A focal length guess in pixels.
*/
mrcal_pose_t getSeedPose(const mrcal_point3_t *c_observations_board_pool,
cv::Size boardSize, cv::Size imagerSize,
double squareSize, double focal_len_guess);
Expand Down
Loading