Skip to content

Commit

Permalink
Make into pypi package with macos and windows support (#51)
Browse files Browse the repository at this point in the history
* test for macos and windows support

* Apple Clang has issues with std::log as constexpr

* static cast implicit type conversions, include numeric

* disable msvc warnings as error, add python cibuildwheel for macos

* drop support for ubuntu 20.04, support python version >=3.8

* cache cmake build type for opencv windows

* statically linked CRT OFF

* add pypi workflow

* all set for pip install

* fix artifact same-name error due to shift to upload-artifact@v4

* try this

* remove matrix.version, matrix.os for artifact name is enough

* modify readme for pip install

* bump version

* cache v2 will literally break CI from 1st Feb 2025
  • Loading branch information
saurabh1002 authored Jan 16, 2025
1 parent 95bc9fe commit e4a1543
Show file tree
Hide file tree
Showing 17 changed files with 162 additions and 41 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/cpp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-22.04, ubuntu-24.04]
os: [ubuntu-24.04, ubuntu-22.04, windows-2022, macos-14, macos-15]
steps:
- uses: actions/checkout@v3
- name: Setup cmake
Expand All @@ -35,12 +35,12 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-22.04, ubuntu-24.04]
os: [ubuntu-22.04, ubuntu-24.04]

steps:
- uses: actions/checkout@v3
- name: Cache dependencies
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: ~/.apt/cache
key: ${{ runner.os }}-apt-${{ hashFiles('**/ubuntu_dependencies.yml') }}
Expand Down
67 changes: 67 additions & 0 deletions .github/workflows/pypi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Publish to PyPI.org
on:
release:
types: [published]
push:
branches: ["main"]
pull_request:
branches: ["main"]

jobs:
build_sdist:
name: Build source distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Build sdist
run: pipx run build --sdist ${{github.workspace}}/python/
- name: Move sdist to dist
run: mkdir -p dist && mv ${{github.workspace}}/python/dist/*.tar.gz dist/

- uses: actions/upload-artifact@v4
with:
path: dist/*.tar.gz

cibuildwheel:
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-24.04, windows-2022, macos-15]

steps:
- uses: actions/checkout@v3

- name: Build test wheels (only PRs)
if: github.event_name != 'release'
uses: pypa/cibuildwheel@v2.22.0
env: # build 1 build per platform just to make sure we can do it later when releasing
CIBW_BUILD: "cp310-*"
with:
package-dir: ${{github.workspace}}/python/

- name: Build all wheels
if: github.event_name == 'release'
uses: pypa/cibuildwheel@v2.22.0
with:
package-dir: ${{github.workspace}}/python/

- uses: actions/upload-artifact@v4
with:
name: artifact-${{ matrix.os }}
path: ./wheelhouse/*.whl

pypi:
if: github.event_name == 'release'
needs: [cibuildwheel, build_sdist]
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: artifact
path: dist

- uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
2 changes: 1 addition & 1 deletion .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-24.04, ubuntu-22.04, ubuntu-20.04]
os: [ubuntu-24.04, ubuntu-22.04, windows-2022, macos-14, macos-15]

steps:
- uses: actions/checkout@v3
Expand Down
16 changes: 4 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<a href="https://github.com/PRBonn/MapClosures/releases"><img src="https://img.shields.io/github/v/release/PRBonn/MapClosures?label=version" /></a>
<a href="https://github.com/PRBonn/MapClosures/blob/main/LICENSE"><img src=https://img.shields.io/badge/license-MIT-green" /></a>
<a href="https://github.com/PRBonn/MapClosures/blob/main/"><img src="https://img.shields.io/badge/Linux-FCC624?logo=linux&logoColor=black" /></a>
<a href="https://github.com/PRBonn/MapClosures/blob/main/"><img src="https://img.shields.io/badge/Windows-0078D6?st&logo=windows&logoColor=white" /></a>
<a href="https://github.com/PRBonn/MapClosures/blob/main/"><img src="https://img.shields.io/badge/mac%20os-000000?&logo=apple&logoColor=white" /></a>
<br />
<br />
<span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
Expand Down Expand Up @@ -53,18 +55,8 @@ target_link_libraries(my_target PUBLIC map_closures)
```

## Install the Python API and CLI
1. First, install the necessary system dependencies
```sh
sudo apt-get install --no-install-recommends -y build-essential cmake pybind11-dev libeigen3-dev libopencv-dev libtbb-dev
```
2. To get an odometry estimate in our Python CLI we rely on [KISS-ICP](https://github.com/PRBonn/kiss-icp), you can install it using
```sh
pip install kiss-icp
```
3. Then run:
```sh
make
```
`pip install map-closures`

### Usage
<details>
<summary>
Expand Down
2 changes: 2 additions & 0 deletions cpp/3rdparty/opencv/opencv.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ set(BUILD_opencv_features2d ON CACHE BOOL "Build OpenCV features2d module")
set(BUILD_opencv_imgproc ON CACHE BOOL "Build OpenCV imgproc module")

set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries")
set(BUILD_WITH_STATIC_CRT OFF CACHE BOOL "Build with statically linked CRT")
set(BUILD_JAVA OFF CACHE BOOL "Build Java bindings")
set(BUILD_PERF_TESTS OFF CACHE BOOL "Build performance tests")
set(BUILD_PROTOBUF OFF CACHE BOOL "Build protobuf")
Expand All @@ -49,6 +50,7 @@ set(BUILD_opencv_python_tests OFF CACHE BOOL "Build OpenCV Python tests")
set(BUILD_opencv_stitching OFF CACHE BOOL "Build OpenCV stitching module")
set(BUILD_opencv_video OFF CACHE BOOL "Build OpenCV video module")
set(BUILD_opencv_videoio OFF CACHE BOOL "Build OpenCV video IO module")
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "CMake build type")

message(STATUS "Fetching OpenCV from Github")
include(FetchContent)
Expand Down
3 changes: 2 additions & 1 deletion cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
# SOFTWARE.

cmake_minimum_required(VERSION 3.16...3.26)
project(map_closures_cpp VERSION 1.0.0 LANGUAGES CXX)
project(map_closures_cpp VERSION 1.0.1 LANGUAGES CXX)

option(USE_SYSTEM_EIGEN3 "Use system pre-installed Eigen" ON)
option(USE_SYSTEM_TBB "Use system pre-installed TBB" ON)
Expand All @@ -33,6 +33,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

include(3rdparty/find_dependencies.cmake)
include(cmake/CompilerOptions.cmake)

add_subdirectory(map_closures)
add_subdirectory(gt_closures)
51 changes: 51 additions & 0 deletions cpp/cmake/CompilerOptions.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# MIT License
#
# Copyright (c) 2022 Ignacio Vizzo, Tiziano Guadagnino, Benedikt Mersch, Cyrill
# Stachniss.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
function(set_global_target_properties target)
target_compile_features(${target} PUBLIC cxx_std_17)
target_compile_definitions(${target} PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,MSVC>:_USE_MATH_DEFINES>)
target_compile_options(
${target}
PRIVATE # MSVC
$<$<COMPILE_LANG_AND_ID:CXX,MSVC>:/W4>
# Clang/AppleClang
$<$<COMPILE_LANG_AND_ID:CXX,Clang,AppleClang>:-fcolor-diagnostics>
$<$<COMPILE_LANG_AND_ID:CXX,Clang,AppleClang>:-Wall>
$<$<COMPILE_LANG_AND_ID:CXX,Clang,AppleClang>:-Wextra>
$<$<COMPILE_LANG_AND_ID:CXX,Clang,AppleClang>:-Wconversion>
$<$<COMPILE_LANG_AND_ID:CXX,Clang,AppleClang>:-Wno-sign-conversion>
# GNU
$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-fdiagnostics-color=always>
$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Wall>
$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Wextra>
$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-pedantic>
$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Wcast-align>
$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Wcast-qual>
$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Wconversion>
$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Wdisabled-optimization>
$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Woverloaded-virtual>)
set(INCLUDE_DIRS ${PROJECT_SOURCE_DIR})
get_filename_component(INCLUDE_DIRS ${INCLUDE_DIRS} PATH)
target_include_directories(
${target} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
PUBLIC $<BUILD_INTERFACE:${INCLUDE_DIRS}> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
endfunction()
2 changes: 1 addition & 1 deletion cpp/gt_closures/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ add_library(gt_closures STATIC)
target_sources(gt_closures PRIVATE VoxelHashSet.cpp GTClosures.cpp)
target_include_directories(gt_closures PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..)
target_link_libraries(gt_closures PUBLIC Eigen3::Eigen TBB::tbb tsl::robin_map)
target_compile_features(gt_closures PUBLIC cxx_std_17)
set_global_target_properties(gt_closures)
2 changes: 1 addition & 1 deletion cpp/gt_closures/GTClosures.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ int GTClosures::GetSegments() {
last_pose = poses_.at(idx);
scan_occupancies_.erase(idx);
});
return segments_.size() - n_skip_segments_;
return static_cast<int>(segments_.size()) - n_skip_segments_;
}

std::vector<Eigen::Vector2i> GTClosures::ComputeClosuresForQuerySegment(
Expand Down
2 changes: 1 addition & 1 deletion cpp/gt_closures/VoxelHashSet.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ struct VoxelHashSet {
const Eigen::Vector3d &t);
void AddVoxels(const VoxelHashSet &other_set);
double ComputeOverlap(const VoxelHashSet &other_set);
int size() const { return set_.size(); }
std::size_t size() const { return set_.size(); }
void clear() { set_.clear(); }

double voxel_size_;
Expand Down
28 changes: 15 additions & 13 deletions cpp/map_closures/AlignRansac2D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,24 @@
#include <Eigen/LU>
#include <Eigen/SVD>
#include <algorithm>
#include <numeric>
#include <random>
#include <utility>
#include <vector>

namespace {
Eigen::Isometry2d KabschUmeyamaAlignment2D(
const std::vector<map_closures::PointPair> &keypoint_pairs) {
auto mean = std::reduce(keypoint_pairs.cbegin(), keypoint_pairs.cend(),
map_closures::PointPair(), [](auto lhs, const auto &rhs) {
lhs.ref += rhs.ref;
lhs.query += rhs.query;
return lhs;
});
mean.query /= keypoint_pairs.size();
mean.ref /= keypoint_pairs.size();
auto covariance_matrix = std::transform_reduce(
map_closures::PointPair mean =
std::reduce(keypoint_pairs.cbegin(), keypoint_pairs.cend(), map_closures::PointPair(),
[](auto lhs, const auto &rhs) {
lhs.ref += rhs.ref;
lhs.query += rhs.query;
return lhs;
});
mean.query /= static_cast<double>(keypoint_pairs.size());
mean.ref /= static_cast<double>(keypoint_pairs.size());
Eigen::Matrix2d covariance_matrix = std::transform_reduce(
keypoint_pairs.cbegin(), keypoint_pairs.cend(), Eigen::Matrix2d().setZero(),
std::plus<Eigen::Matrix2d>(), [&](const auto &keypoint_pair) {
return (keypoint_pair.ref - mean.ref) *
Expand All @@ -66,8 +68,8 @@ static constexpr double inliers_distance_threshold = 3.0;
static constexpr double inliers_ratio = 0.3;
static constexpr double probability_success = 0.999;
static constexpr int min_points = 2;
static constexpr int __RANSAC_TRIALS__ = std::ceil(
std::log(1.0 - probability_success) / std::log(1.0 - std::pow(inliers_ratio, min_points)));
static int __RANSAC_TRIALS__ = std::ceil(std::log(1.0 - probability_success) /
std::log(1.0 - std::pow(inliers_ratio, min_points)));
} // namespace

namespace map_closures {
Expand Down Expand Up @@ -106,12 +108,12 @@ std::pair<Eigen::Isometry2d, int> RansacAlignment2D(const std::vector<PointPair>
}
}

const int num_inliers = optimal_inlier_indices.size();
const std::size_t num_inliers = optimal_inlier_indices.size();
std::vector<PointPair> inlier_keypoint_pairs(num_inliers);
std::transform(optimal_inlier_indices.cbegin(), optimal_inlier_indices.cend(),
inlier_keypoint_pairs.begin(),
[&](const auto index) { return keypoint_pairs[index]; });
auto T = KabschUmeyamaAlignment2D(inlier_keypoint_pairs);
Eigen::Isometry2d T = KabschUmeyamaAlignment2D(inlier_keypoint_pairs);
return {T, num_inliers};
}
} // namespace map_closures
2 changes: 1 addition & 1 deletion cpp/map_closures/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ add_library(map_closures STATIC)
target_sources(map_closures PRIVATE DensityMap.cpp AlignRansac2D.cpp MapClosures.cpp)
target_include_directories(map_closures PUBLIC ${hbst_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/..)
target_link_libraries(map_closures PUBLIC Eigen3::Eigen TBB::tbb ${OpenCV_LIBS})
target_compile_features(map_closures PUBLIC cxx_std_17)
set_global_target_properties(map_closures)
4 changes: 3 additions & 1 deletion cpp/map_closures/MapClosures.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <Eigen/Core>
#include <algorithm>
#include <cmath>
#include <numeric>
#include <opencv2/core.hpp>
#include <utility>
#include <vector>
Expand Down Expand Up @@ -108,7 +109,8 @@ ClosureCandidate MapClosures::ValidateClosure(const int reference_id, const int
const auto &ref_map_lower_bound = density_maps_.at(reference_id).lower_bound;
const auto &qry_map_lower_bound = density_maps_.at(query_id).lower_bound;
auto to_world_point = [](const auto &p, const auto &offset) {
return Eigen::Vector2d(p.y + offset.x(), p.x + offset.y());
return Eigen::Vector2d(p.y + static_cast<float>(offset.x()),
p.x + static_cast<float>(offset.y()));
};
std::vector<PointPair> keypoint_pairs(num_matches);
std::transform(
Expand Down
4 changes: 2 additions & 2 deletions cpp/map_closures/MapClosures.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ using Tree = srrg_hbst::BinaryTree<Node>;

namespace map_closures {
struct Config {
float density_map_resolution = 0.5;
float density_threshold = 0.05;
float density_map_resolution = 0.5f;
float density_threshold = 0.05f;
int hamming_distance_threshold = 50;
};

Expand Down
2 changes: 1 addition & 1 deletion python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
cmake_minimum_required(VERSION 3.16...3.26)
project(map_closure_pybind VERSION 1.0.0 LANGUAGES CXX)
project(map_closure_pybind VERSION 1.0.1 LANGUAGES CXX)

# Set build type
set(CMAKE_BUILD_TYPE Release)
Expand Down
2 changes: 1 addition & 1 deletion python/map_closures/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

__version__ = "1.0.0"
__version__ = "1.0.1"
8 changes: 6 additions & 2 deletions python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ build-backend = "scikit_build_core.build"

[project]
name = "map_closures"
version = "1.0.0"
version = "1.0.1"
description = "Effectively Detecting Loop Closures using Point Cloud Density Maps"
readme = "README.md"
authors = [{ name = "Saurabh Gupta" }, { name = "Tiziano Guadagnino" }]
requires-python = ">=3.7"
requires-python = ">=3.8"
keywords = ["Loop Closures", "Localization", "SLAM", "LiDAR"]
dependencies = [
"kiss-icp>=1.0.0",
Expand Down Expand Up @@ -63,3 +63,7 @@ max-line-length = "100"
[tool.cibuildwheel]
archs = ["auto64"]
skip = ["*-musllinux*", "pp*", "cp36-*"]

[tool.cibuildwheel.macos]
environment = "MACOSX_DEPLOYMENT_TARGET=11.00"
archs = ["auto64", "arm64"]

0 comments on commit e4a1543

Please sign in to comment.